2011-05-06 6 views
12

Estoy tratando de determinar la peor velocidad de disco de caso, así que escribí la siguiente función.Cómo hacer grabaciones de archivos no en caché en la aplicación winform C#

static public decimal MBytesPerSec(string volume) 
{ 
    string filename = volume + "\\writetest.tmp"; 

    if (System.IO.File.Exists(filename)) 
     System.IO.File.Delete(filename); 

    System.IO.StreamWriter file = new System.IO.StreamWriter(filename); 

    char[] data = new char[64000]; 
    Stopwatch watch = new Stopwatch(); 
    watch.Start(); 

    int i = 0; 

    for (; i < 1000; i++) 
    { 
     file.Write(data); 
     if (watch.ElapsedMilliseconds > 2000) 
     { 
      break; 
     } 
    } 

    watch.Stop(); 
    file.Close(); 

    System.IO.File.Delete(volume + "\\test.txt"); 
    decimal mbytessec = (i * 64/watch.ElapsedMilliseconds); 
    return mbytessec; 
} 

La función funciona bien, pero las escrituras están consiguiendo almacena en caché, por lo que la velocidad no es peor de los casos.

En WIN32 C++, yo simplemente crear el archivo con FILE_FLAG_NO_BUFFERING, FILE_FLAG_WRITE_THROUGH opciones y, a continuación, asegúrese de seguir las reglas sin almacenamiento en caché de escritura (escribir en el fichero en desplazamientos tamaño del sector, con un mínimo de 4k escribe)

Encontré un article que analiza la técnica .NET.

Así que escribí una nueva función (ignore los errores matemáticos).

static public decimal MBytesPerSecNonCached(string volume) 
{ 
    const FileOptions FILE_FLAG_NO_BUFFERING = (FileOptions)0x20000000; 

    string filename = volume + "\\writetest.tmp"; 

    using (FileStream fs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, 1024, FileOptions.WriteThrough | FILE_FLAG_NO_BUFFERING)) 
    { 
     byte[] data = new byte[65535]; 
     int i = 0; 

     Stopwatch watch = new Stopwatch(); 
     watch.Start(); 

     for (; i < 1000; i++) 
     { 
      fs.Write(data, 0, 65535); 
      if (watch.ElapsedMilliseconds > 2000) 
      { 
       break; 
      } 
     } 

     watch.Stop(); 
     fs.Close(); 

     System.IO.File.Delete(filename); 

     decimal mbytessec = (i * 64/watch.ElapsedMilliseconds); 

     return mbytessec; 
    } 
} 

Esta función se puede utilizar para 4k, 16K y 32K escriben tamaños, pero una vez que lo intento 64K escribir tamaños, me sale una excepción:

IO operation will not work. Most likely the file will become too long or the handle was not opened to support synchronous IO operations.

Entonces, ¿cómo puedo solucionar este problema para que pueda probar con tamaños de escritura mayores a 32KB (64KB a 4096KB)?

+2

.NET Framework fue desarrollado para eliminar esas decisiones del desarrollador y permitir que el marco lo administre "para usted". La buena noticia es que, para el 99% de las necesidades, es bastante bueno en eso. La mala noticia es que no quiere darte ese control cuando sientes que lo necesitas (o simplemente lo QUIERES). La solución, si está interesado en hacerlo, "usted mismo" es usar PInvoke para direccionar las DLL de Win32 y luego hacerlo de la misma manera que lo haría en C++. –

Respuesta

11

Pruebe algo de código no administrado:

[DllImport("kernel32", SetLastError = true)] 
     static extern unsafe SafeFileHandle CreateFile(
      string FileName,   // file name 
      uint DesiredAccess,  // access mode 
      uint ShareMode,   // share mode 
      IntPtr SecurityAttributes, // Security Attr 
      uint CreationDisposition, // how to create 
      uint FlagsAndAttributes, // file attributes 
      IntPtr hTemplate // template file 
      ); 
const uint FILE_FLAG_NO_BUFFERING = 0x20000000; 

SafeFileHandle handle = CreateFile("filename", 
          (uint)FileAccess.Write, 
          (uint)FileShare.None, 
          IntPtr.Zero, 
          (uint)FileMode.Open, 
          FILE_FLAG_NO_BUFFERING, 
          IntPtr.Zero); 

var unBufferedStream = new FileStream(handle,FileAccess.Read,blockSize,false); 

Ahora usted debería tener acceso a una corriente sin memoria intermedia que se puede leer y escribir sin embargo que por favor sin restricciones

Para el registro se puede .... también deshabilitar el almacenamiento en caché como este:

[DllImport("KERNEL32", SetLastError = true)] 
     public extern static int DeviceIoControl(IntPtr hDevice, uint IoControlCode, 
      IntPtr lpInBuffer, uint InBufferSize, 
      IntPtr lpOutBuffer, uint nOutBufferSize, 
      ref uint lpBytesReturned, 
      IntPtr lpOverlapped); 
     [DllImport("KERNEL32", SetLastError = true)] 
     public extern static int CloseHandle(
     IntPtr hObject); 

[StructLayout(LayoutKind.Sequential)] 
     public struct DISK_CACHE_INFORMATION 
     {    
      public byte ParametersSavable;    
      public byte ReadCacheEnabled;    
      public byte WriteCacheEnabled; 
      public int ReadRetentionPriority;//DISK_CACHE_RETENTION_PRIORITY = enum = int 
      public int WriteRetentionPriority;//DISK_CACHE_RETENTION_PRIORITY = enum = int 
      public Int16 DisablePrefetchTransferLength;//WORD    
      public byte PrefetchScalar;    
     } 

public void SetDiskCache(byte val) 
     { 
      IntPtr h = CreateFile("\\\\.\\PHYSICALDRIVE0", (uint)FileAccess.Read | (uint)FileAccess.Write, (uint)FileShare.Write, IntPtr.Zero, (uint)FileMode.Open, 0, IntPtr.Zero); 
      DISK_CACHE_INFORMATION sInfo = new DISK_CACHE_INFORMATION(); 
      IntPtr ptrout = Marshal.AllocHGlobal(Marshal.SizeOf(sInfo)); 
      Marshal.StructureToPtr(sInfo, ptrout, true);    
      uint dwWritten = 0; 
      int ret = DeviceIoControl(h,IOCTL_DISK_GET_CACHE_INFORMATION,IntPtr.Zero,0,ptrout,(uint)Marshal.SizeOf(sInfo),ref dwWritten,IntPtr.Zero);    
      sInfo = (DISK_CACHE_INFORMATION)Marshal.PtrToStructure(ptrout,typeof(DISK_CACHE_INFORMATION));    
      sInfo.ReadCacheEnabled = val; 
      // acuma trimite structura modificata 
      IntPtr ptrin = Marshal.AllocHGlobal(Marshal.SizeOf(sInfo)); 
      Marshal.StructureToPtr(sInfo, ptrin, true);    
      ret = DeviceIoControl(h, IOCTL_DISK_SET_CACHE_INFORMATION, ptrin, (uint)Marshal.SizeOf(sInfo), IntPtr.Zero, 0, ref dwWritten, IntPtr.Zero);    
      CloseHandle(h);    
     } 
+0

Gracias Tudor Lo probaré mañana y veré qué pasa. Tengo curiosidad por cuánto tiempo dura la inhabilitación de la memoria caché en una unidad. sería un truco malo para jugar con alguien;) –

+0

El flujo de búfer en este código no administrado aún da el mismo error: 'Operación IO no funcionará. Lo más probable es que el archivo sea demasiado largo o no se abrió el controlador para admitir operaciones de IO sincrónicas. –

+0

Como la documentación para las notas CreateFile, el indicador FILE_FLAG_NO_BUFFERING requiere que todas las operaciones de E/S en el manejador de archivos estén en múltiplos del tamaño del sector y que los búferes de E/S también estén alineados en direcciones que son múltiplos del tamaño del sector. – TheLegendaryCopyCoder

Cuestiones relacionadas