为什么.NET的File.Open使UNCpath过度的SMB调用?
我有一个代码块,需要打开并使用UNCpath从NAS服务器读取很多小文本文件。 此代码是最初用C ++编写的模块的一部分,但现在正在转换为C#。 C#版本明显较慢。 我确定打开文件的电话几乎占了所有的性能差异。 使用WireShark我发现这是因为System.IO.File.Open调用比类似的C ++代码更多的SMBnetworking请求。
C ++代码进行这个调用:
FILE *f = _wfsopen(fileName, L"r", _SH_DENYWR);
这会导致以下一系列SMB请求:
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#代码进行这个调用:
FileStream f = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
这会导致以下一系列SMB请求:
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请求? 有没有办法改变这个代码,以避免所有这些额外的要求?
简而言之,File.Open调用new FileStream()
, new FileStream()
会执行很多调用:
-
NormalisePath 。
String filePath = Path.NormalizePath(path, true, maxPath); // fullCheck: true
导致这个代码:
1.a:获取完整path:
if (fullCheck) { ... result = newBuffer.GetFullPathName();
GetFullPathName()调用Win32Native.GetFullPathName
一次或两次(取决于结果path的lentgh)。
1.B. 试图扩大短path。 你的path包含〜char,所以它看起来像一个path扩展的候选人:
if (mightBeShortFileName) { bool r = newBuffer.TryExpandShortFileName();
结果,Win32Native.GetLongPathName() 被调用 。
-
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);
-
Win32Native.GetFileType()
不是所有的人都会导致请求,但有些人会这样做。 我尝试通过逐步debugging源代码( 这里是启用.net源代码debugging的手册)并在每个步骤之后检查日志来重现繁琐的请求。 Resute更类似于你的第一个上市。 如果你真的有兴趣find真正的问题,你必须自己做。
UPD请注意,我已经检查了当前(.net 4.5.2)的行为。 它自2.0以来多次更改(例如, FileIOPermission.Demand()
最初也被称为完全可信的代码),所以它取决于:)
我没有真正的答案,为什么.NET实现是如此唠叨,但这种行为将是由于System.IO.FileStream
所有实现File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
正在做的是将parameter passing给FileStream的构造函数 。
public static FileStream Open(string path, FileMode mode, FileAccess access, FileShare share) { return new FileStream(path, mode, access, share); }
改变FileStream的行为将意味着你将基本上必须重新实现FileStream类,这将需要很大的努力。
另外一个更简单的select是创build一个调用你所提供的C ++代码的本地包装器。 然后从你的C#代码中调用本地包装器 。