2012-10-15 10 views
7
Bazen çalışıyorum Bazı kod uzun UNC yollara başvurmak gerekiyor

(örneğin \\? \ UNC \ MachineName \ Yolu), ama biz hiçbir konuda tesbit ettik dizinin bulunduğu yerde, aynı makinede bile, UNC yolu üzerinden yerel yoldan daha kolay erişilir.UNC yolu

Örneğin, bir dosyaya anlamsız bir dize yazar bazı kıyaslama kod yazdık, daha sonra geri, birden çok kez okudum.

  • C: \ Temp
  • \\ MachineName \ Temp
  • ben kod aynı makinede çalışan, benim dev makine üzerinde aynı paylaşılan dizine erişmek için 6 farklı şekillerde ile test ediyorum ? \\ \ C:? \ Sıcaklık
  • \\ \ UNC \ MakineAdı \ Sıcaklık
  • \\ 127.0.0.1 \ Sıcaklık
  • \\ \ UNC \ 127.0.0.1 Temp \

Ve burada sonuçlarıdır:

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 

Ben DNS sorunu ekarte etmek IP adresini çalıştı. Her dosya erişiminde kimlik bilgilerini veya izinleri kontrol ediyor olabilir mi? Öyleyse, önbelleğe almanın bir yolu var mı? Yalnızca diske doğrudan erişmek yerine TCP/IP üzerinden her şeyi yapması gereken bir UNC yolu olduğu varsayılıyor mu? Okuma/yazma için kullandığımız kodda bir sorun mu var? Karşılaştırma için ilgili bölümleri sökük, aşağıda görüldüğü gibi gördüm:

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); 
       } 
      } 
     } 
    } 
} 

cevap

6

Bu beni şaşırtmadı. Çok az miktarda veri yazıyor/okuyorsunuz, bu yüzden dosya sistemi önbelleği muhtemelen fiziksel disk G/Ç'nin etkisini en aza indiriyor; Temel olarak, darboğaz CPU olacak. Trafiğin TCP/IP yığını üzerinden mi olacağına ya da olmayacağına emin değilim, fakat en azından SMB protokolü söz konusu. Bir şey için, talepler SMB istemci süreci ile SMB sunucu süreci arasında ileri ve geri aktarıldığı anlamına gelir, böylece kendi aralarında dahil olmak üzere üç farklı süreç arasında içerik geçişi yapabilirsiniz. Yerel dosya sistemi yolunu kullanarak çekirdek moduna ve geri dönüyorsunuz, ancak başka hiçbir işlem söz konusu değil. Bağlama geçişi, çekirdek moduna geçişten çok daha yavaştır.

İki ayrı ek genel gider, dosya başına bir tane ve kilobayt veri başına bir tane olması muhtemeldir. Bu özel testte, dosya başına SMB yükünün baskın olması muhtemeldir. İlgili veri miktarı, fiziksel disk G/Ç'nin etkisini de etkilediğinden, bunun çok sayıda küçük dosyayla uğraşırken gerçekten bir sorun olduğunu görebilirsiniz.