为什么.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()执行很多调用:

  1. 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() 被调用 。

  1. 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(); 
  2. 打开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); 
  3. 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#代码中调用本地包装器 。