如何在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  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技术的文章 。

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

 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  2000) { break; } } watch.Stop(); fs.Close(); System.IO.File.Delete(filename); decimal mbytessec = (i * 64 / watch.ElapsedMilliseconds); return mbytessec; } } 

此函数适用于4k,16K和32K写入大小,但一旦我尝试64K写入大小,我得到一个例外:

IO操作无效。 最有可能的是文件太长或者没有打开句柄来支持同步IO操作。

那么,我该如何解决这个问题,以便我可以测试大于32KB的写入大小(64KB到4096KB)?

尝试一些非托管代码:

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