2012-10-15 7 views
7

Einige Code, mit dem ich arbeite, muss gelegentlich auf lange UNC-Pfade verweisen (zB \\? \ UNC \ MachineName \ Path), aber wir haben festgestellt, dass dies egal ist Wo sich das Verzeichnis befindet, selbst auf demselben Rechner, ist es beim Zugriff über den UNC-Pfad viel langsamer als der lokale Pfad.UNC-Pfad zeigt auf lokales Verzeichnis viel langsamer als lokaler Zugriff

Zum Beispiel haben wir Benchmarking-Code geschrieben, der eine Zeichenfolge von Kauderwelsch in eine Datei schreibt und sie dann später mehrmals zurückliest. Ich teste es mit 6 verschiedene Möglichkeiten, um die gleiche freigegebene Verzeichnis auf meinem dev Maschine zugreifen zu können, mit der Code auf der gleichen Maschine laufen:

  • C: \ Temp
  • \\ Computername \ Temp
  • ? \\ \ C:? \ Temp
  • \\ \ UNC \ Machine \ Temp
  • \\ 127.0.0.1 \ Temp
  • \\ \ UNC \ 127.0.0.1 \ Temp

Und hier sind die Ergebnisse:

Testing: C:\Temp 
Wrote 1000 files to C:\Temp in 861.0647 ms 
Read 1000 files from C:\Temp in 60.0744 ms 
Testing: \\MachineName\Temp 
Wrote 1000 files to \\MachineName\Temp in 2270.2051 ms 
Read 1000 files from \\MachineName\Temp in 1655.0815 ms 
Testing: \\?\C:\Temp 
Wrote 1000 files to \\?\C:\Temp in 916.0596 ms 
Read 1000 files from \\?\C:\Temp in 60.0517 ms 
Testing: \\?\UNC\MachineName\Temp 
Wrote 1000 files to \\?\UNC\MachineName\Temp in 2499.3235 ms 
Read 1000 files from \\?\UNC\MachineName\Temp in 1684.2291 ms 
Testing: \\127.0.0.1\Temp 
Wrote 1000 files to \\127.0.0.1\Temp in 2516.2847 ms 
Read 1000 files from \\127.0.0.1\Temp in 1721.1925 ms 
Testing: \\?\UNC\127.0.0.1\Temp 
Wrote 1000 files to \\?\UNC\127.0.0.1\Temp in 2499.3211 ms 
Read 1000 files from \\?\UNC\127.0.0.1\Temp in 1678.18 ms 

Ich versuchte die IP-Adresse, ein DNS-Problem auszuschließen. Könnte es sein, Zugangsdaten oder Berechtigungen für jeden Dateizugriff zu prüfen? Wenn ja, gibt es eine Möglichkeit, es zwischenzuspeichern? Nimmt es einfach an, da es ein UNC-Pfad ist, dass es alles über TCP/IP tun sollte, anstatt direkt auf den Datenträger zuzugreifen? Ist es etwas falsch mit dem Code, den wir für das Lesen/Schreiben verwenden? Ich habe die relevanten Teile für das Benchmarking herausgerissen, siehe unten:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 
using Microsoft.Win32.SafeHandles; 
using Util.FileSystem; 

namespace UNCWriteTest { 
    internal class Program { 
     [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 
     public static extern bool DeleteFile(string path); // File.Delete doesn't handle \\?\UNC\ paths 

     private const int N = 1000; 

     private const string TextToSerialize = 
      "asd;lgviajsmfopajwf0923p84jtmpq93worjgfq0394jktp9orgjawefuogahejngfmliqwegfnailsjdhfmasodfhnasjldgifvsdkuhjsmdofasldhjfasolfgiasngouahfmp9284jfqp92384fhjwp90c8jkp04jk34pofj4eo9aWIUEgjaoswdfg8jmp409c8jmwoeifulhnjq34lotgfhnq34g"; 

     private static readonly byte[] _Buffer = Encoding.UTF8.GetBytes(TextToSerialize); 

     public static string WriteFile(string basedir) { 
      string fileName = Path.Combine(basedir, string.Format("{0}.tmp", Guid.NewGuid())); 

      try { 
       IntPtr writeHandle = NativeFileHandler.CreateFile(
        fileName, 
        NativeFileHandler.EFileAccess.GenericWrite, 
        NativeFileHandler.EFileShare.None, 
        IntPtr.Zero, 
        NativeFileHandler.ECreationDisposition.New, 
        NativeFileHandler.EFileAttributes.Normal, 
        IntPtr.Zero); 

       // if file was locked 
       int fileError = Marshal.GetLastWin32Error(); 
       if ((fileError == 32 /* ERROR_SHARING_VIOLATION */) || (fileError == 80 /* ERROR_FILE_EXISTS */)) { 
        throw new Exception("oopsy"); 
       } 

       using (var h = new SafeFileHandle(writeHandle, true)) { 
        using (var fs = new FileStream(h, FileAccess.Write, NativeFileHandler.DiskPageSize)) { 
         fs.Write(_Buffer, 0, _Buffer.Length); 
        } 
       } 
      } 
      catch (IOException) { 
       throw; 
      } 
      catch (Exception ex) { 
       throw new InvalidOperationException(" code " + Marshal.GetLastWin32Error(), ex); 
      } 

      return fileName; 
     } 

     public static void ReadFile(string fileName) { 
      var fileHandle = 
       new SafeFileHandle(
        NativeFileHandler.CreateFile(fileName, NativeFileHandler.EFileAccess.GenericRead, NativeFileHandler.EFileShare.Read, IntPtr.Zero, 
               NativeFileHandler.ECreationDisposition.OpenExisting, NativeFileHandler.EFileAttributes.Normal, IntPtr.Zero), true); 

      using (fileHandle) { 
       //check the handle here to get a bit cleaner exception semantics 
       if (fileHandle.IsInvalid) { 
        //ms-help://MS.MSSDK.1033/MS.WinSDK.1033/debug/base/system_error_codes__0-499_.htm 
        int errorCode = Marshal.GetLastWin32Error(); 
        //now that we've taken more than our allotted share of time, throw the exception 
        throw new IOException(string.Format("file read failed on {0} to {1} with error code {1}", fileName, errorCode)); 
       } 

       //we have a valid handle and can actually read a stream, exceptions from serialization bubble out 
       using (var fs = new FileStream(fileHandle, FileAccess.Read, 1*NativeFileHandler.DiskPageSize)) { 
        //if serialization fails, we'll just let the normal serialization exception flow out 
        var foo = new byte[256]; 
        fs.Read(foo, 0, 256); 
       } 
      } 
     } 

     public static string[] TestWrites(string baseDir) { 
      try { 
       var fileNames = new List<string>(); 
       DateTime start = DateTime.UtcNow; 
       for (int i = 0; i < N; i++) { 
        fileNames.Add(WriteFile(baseDir)); 
       } 
       DateTime end = DateTime.UtcNow; 

       Console.Out.WriteLine("Wrote {0} files to {1} in {2} ms", N, baseDir, end.Subtract(start).TotalMilliseconds); 
       return fileNames.ToArray(); 
      } 
      catch (Exception e) { 
       Console.Out.WriteLine("Failed to write for " + baseDir + " Exception: " + e.Message); 
       return new string[] {}; 
      } 
     } 

     public static void TestReads(string baseDir, string[] fileNames) { 
      try { 
       DateTime start = DateTime.UtcNow; 

       for (int i = 0; i < N; i++) { 
        ReadFile(fileNames[i%fileNames.Length]); 
       } 
       DateTime end = DateTime.UtcNow; 

       Console.Out.WriteLine("Read {0} files from {1} in {2} ms", N, baseDir, end.Subtract(start).TotalMilliseconds); 
      } 
      catch (Exception e) { 
       Console.Out.WriteLine("Failed to read for " + baseDir + " Exception: " + e.Message); 
      } 
     } 

     private static void Main(string[] args) { 
      foreach (string baseDir in args) { 
       Console.Out.WriteLine("Testing: {0}", baseDir); 

       string[] fileNames = TestWrites(baseDir); 

       TestReads(baseDir, fileNames); 

       foreach (string fileName in fileNames) { 
        DeleteFile(fileName); 
       } 
      } 
     } 
    } 
} 

Antwort

6

Das überrascht mich nicht. Sie schreiben/lesen eine relativ kleine Menge an Daten, so dass der Dateisystem-Cache wahrscheinlich die Auswirkungen der physischen Datenträger-E/A minimiert; Im Grunde wird der Engpass die CPU sein. Ich bin nicht sicher, ob der Datenverkehr über den TCP/IP-Stack geht oder nicht, aber zumindest ist das SMB-Protokoll beteiligt. Das bedeutet zum einen, dass die Anfragen zwischen dem SMB-Client-Prozess und dem SMB-Server-Prozess hin- und hergereicht werden. Sie haben also einen Kontextwechsel zwischen drei verschiedenen Prozessen, einschließlich Ihrer eigenen. Mit dem lokalen Dateisystempfad wechseln Sie in den Kernel-Modus und zurück, aber es ist kein anderer Prozess beteiligt. Kontextwechsel ist viel langsamer als der Übergang zum und vom Kernel-Modus.

Es gibt wahrscheinlich zwei unterschiedliche zusätzliche Gemeinkosten, einen pro Datei und einen pro Kilobyte Daten. In diesem speziellen Test ist der SMB-Overhead pro Datei wahrscheinlich dominant. Da sich die Menge der betroffenen Daten auch auf die Auswirkungen der physischen Datenträger-E/A auswirkt, stellen Sie möglicherweise fest, dass dies nur ein Problem bei der Verarbeitung vieler kleiner Dateien ist.

Verwandte Themen