问题描述
我有一段代码需要使用UNC路径从NAS服务器打开和读取很多小文本文件.此代码是最初用C ++编写的模块的一部分,但现在已转换为C#.C#版本明显较慢.我确定打开文件的调用几乎解决了所有性能差异.使用WireShark,我发现这是因为与类似的C ++代码相比,System.IO.File.Open调用发出的SMB网络请求要多得多.
I have a block of code that needs to open and read a lot of small text files from a NAS server using UNC paths. This code is part of a module that was originally written in C++ but is now being converted to C#. The C# version is significantly slower. I determined that the call to open the file accounts for nearly all of the performance difference. Using WireShark I found that this is because the System.IO.File.Open call makes far more SMB network requests than similar C++ code.
C ++代码进行此调用:
The C++ code makes this call:
FILE *f = _wfsopen(fileName, L"r", _SH_DENYWR);
这将导致以下SMB请求序列:
This results in the following sequence of SMB requests:
NT Create AndX Request, FID: 0x0004, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0004
Trans2 Request, QUERY_FILE_INFO, FID: 0x0004, Query File Basic Info
Trans2 Response, FID: 0x0004, QUERY_FILE_INFO
Read AndX Request, FID: 0x0004, 1327 bytes at offset 0
Read AndX Response, FID: 0x0004, 1327 bytes
Close Request, FID: 0x0004
Close Response, FID: 0x0004
NT Create AndX Request, FID: 0x0005, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0005
C#代码进行此调用:
The C# code makes this call:
FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
这将导致以下SMB请求序列:
This results in the following sequence of SMB requests:
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path:
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path:
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i
Trans2 Response, FIND_FIRST2, Files: i
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a\\q
Trans2 Response, FIND_FIRST2, Files: q
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a\\q\\~141106162638847.nmd
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path:
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path:
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i
Trans2 Response, FIND_FIRST2, Files: i
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a
Trans2 Response, FIND_FIRST2, Files: a
Trans2 Request, QUERY_PATH_INFO, Query File Basic Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, QUERY_PATH_INFO, Query File Standard Info, Path: \\a\\i\\a
Trans2 Response, QUERY_PATH_INFO
Trans2 Request, FIND_FIRST2, Pattern: \\a\\i\\a\\q
Trans2 Response, FIND_FIRST2, Files: q
Close Request, FID: 0x000f
Close Response
NT Create AndX Request, FID: 0x0018, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0018
Trans2 Request, QUERY_FILE_INFO, FID: 0x0018, Query File Basic Info
Trans2 Response, FID: 0x0018, QUERY_FILE_INFO
Read AndX Request, FID: 0x0018, 1327 bytes at offset 0
Read AndX Response, FID: 0x0018, 1327 bytes
Close Request, FID: 0x0018
Close Response, FID: 0x0018
NT Create AndX Request, FID: 0x0019, Path: \\a\\i\\a\\q\\~141106162638847.nmd
NT Create AndX Response, FID: 0x0019
为什么System.IO.File.Open发出所有这些额外的SMB请求?有什么方法可以更改此代码,以避免所有这些额外的请求?
Why does System.IO.File.Open make all these extra SMB requests? Is there any way to change this code to avoid all these extra requests?
推荐答案
简而言之,File.Open调用 new FileStream()
和 new FileStream()
进行很多呼叫:
In short, File.Open calls new FileStream()
and new FileStream()
does a lot of calls:
String filePath = Path.NormalizePath(path, true, maxPath); // fullCheck: true
导致此代码:
1.a:获取完整路径:
1.a: Get full path:
if (fullCheck) { ...
result = newBuffer.GetFullPathName();
GetFullPathName()调用 Win32Native.GetFullPathName
一到两次(取决于最终路径的长度).
GetFullPathName() calls Win32Native.GetFullPathName
one or two times (depending on the lentgh of resulting path).
1.b.试图扩大短路.您的路径包含〜
字符,因此它看起来像的候选者路径扩展:
1.b. Trying to expand short path. Your path contains ~
char, so it looks like a candidate for a path expanding:
if (mightBeShortFileName) {
bool r = newBuffer.TryExpandShortFileName();
因此,调用了Win32Native.GetLongPathName()..
as a result, Win32Native.GetLongPathName() is called.
-
FileIoPermission.Demand()(仅适用于非信任):
// All demands in full trust domains are no-ops, so skip
if (!CodeAccessSecurityEngine.QuickCheckForAllDemands()) {
...
new FileIOPermission(secAccess, control, new String[] { filePath }, false, false).Demand();
打开fileStream (软盘反冲;)):
// Don't pop up a dialog for reading from an emtpy floppy drive
int oldMode = Win32Native.SetErrorMode(Win32Native.SEM_FAILCRITICALERRORS);
try {
...
_handle = Win32Native.SafeCreateFile(tempPath, fAccess, share, secAttrs, mode, flagsAndAttributes, IntPtr.Zero);
不是所有的人都会导致smb请求,但是有些人会.我尝试通过使用源代码进行逐步调试来重现聊天请求(此处为手册用于启用.net源调试)并在每个步骤之后检查日志.结果与您的第一个清单更相似.如果您真的有兴趣找到真正的问题,则必须自己解决.
Not all of them would lead to smb request, but some will do. I've tried to reproduce chatty requests by debugging with source step-by-step (here's manual for enabling the .net source debugging) and checking the log after each step. Resuts are more similar to your's first listing.If you're really interested in finding the real issue, you'll have to do it yourself.
UPD 注意,我已经检查了当前(.net 4.5.2)行为.自2.0以来,它已多次更改(例如,最初也为完全受信任的代码调用了 FileIOPermission.Demand()
),所以这取决于:)
UPD Note that I've checked current (.net 4.5.2) behavior. It was changed multiple times since 2.0 (e.g. FileIOPermission.Demand()
originally was called for full-trusted code too), so it depends:)
这篇关于为什么带有UNC路径的.NET的File.Open进行过多的SMB调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!