2011-05-06 18 views
12

我试图确定最坏情况下的磁盘速度,所以我写了下面的函数。如何在C#winform应用程序中执行非缓存文件写入

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

函数工作正常,但写入缓存,所以速度不是最坏的情况。

在WIN32 C++,我只想创建FILE_FLAG_NO_BUFFERINGFILE_FLAG_WRITE_THROUGH选项的文件,并确保遵循非缓存书写规则(写在扇区大小偏移文件,用4k最低写入)

我找到了一个讨论.NET技术的article

所以我写了一个新的函数(忽略数学错误)。

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

此功能对于4K,16K和32K写大小,但一旦我尝试64K写大小,我得到一个异常:

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

所以,我怎么能解决这个问题,所以我可以测试大于32KB的写入大小(64KB到4096KB)?

+2

.NET框架的开发旨在从开发人员那里删除这些决定,并允许框架管理“为您”。好消息是,99%的需求确实非常好。坏消息是,当你觉得你需要它的时候,它不想给你这种控制(或者只是简单地想要它)。这个解决方案,如果你热衷于这样做,那么'你的自我'就是使用PInvoke来解决Win32 dll的问题,然后就像你在C++中一样。 – 2011-05-06 21:23:54

回答

11

尝试一些非托管代码:

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

现在你应该有机会获得一个缓冲流,你可以读取和写入但是你没有约束

为了记录请....你可以也可以像这样禁用缓存:

[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

谢谢都铎王朝明天我会试一试,看看会发生什么。我很好奇禁用驱动器上的缓存持续多久。对某人来说是一个诡计;) – 2011-05-08 18:09:51

+0

这个非托管代码中的缓冲流仍然给出相同的错误:'IO操作不起作用。文件很可能会变得太长,或者该句柄未打开以支持同步IO操作。 – 2014-12-20 13:40:22

+0

作为CreateFile笔记的文档,FILE_FLAG_NO_BUFFERING标志要求文件句柄上的所有I/O操作都是扇区大小的倍数,并且I/O缓冲区也要在扇区大小倍数的地址上对齐。 – TheLegendaryCopyCoder 2017-02-01 11:55:28

相关问题