我有一个大量读取和写入文件的应用程序(自定义格式),我被告知通过使用直接非托管代码来提高性能。 在尝试使用真正的应用程序之前,我做了一些小测试,只是为了看看性能如何提升,但令我惊讶的是,非托管版本似乎比使用简单文件流慢8倍。
以下是托管功能:
private int length = 100000;
private TimeSpan tspan;
private void UsingManagedFileHandle()
{
DateTime initialTime = DateTime.Now;
using (FileStream fileStream = new FileStream("data2.txt", FileMode.Create, FileAccess.ReadWrite))
{
string line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890123";
byte[] bytes = Encoding.Unicode.GetBytes(line);
for (int i = 0; i < length; i++)
{
fileStream.Write(bytes, 0, bytes.Length);
}
fileStream.Close();
}
this.tspan = DateTime.Now.Subtract(initialTime);
label2.Text = "" + this.tspan.TotalMilliseconds + " Milliseconds";
}
以下是非托管方式:
public void UsingAnUnmanagedFileHandle()
{
DateTime initialTime;
IntPtr hFile;
hFile = IntPtr.Zero;
hFile = FileInteropFunctions.CreateFile("data1.txt",
FileInteropFunctions.GENERIC_WRITE | FileInteropFunctions.GENERIC_READ,
FileInteropFunctions.FILE_SHARE_WRITE,
IntPtr.Zero,
FileInteropFunctions.CREATE_ALWAYS,
FileInteropFunctions.FILE_ATTRIBUTE_NORMAL,
0);
uint lpNumberOfBytesWritten = 0;
initialTime = DateTime.Now;
if (hFile.ToInt64() > 0)
{
string line = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890123";
byte[] bytes = Encoding.Unicode.GetBytes(line);
uint bytesLen = (uint)bytes.Length;
for (int i = 0; i < length; i++)
{
FileInteropFunctions.WriteFile(hFile,
bytes,
bytesLen,
out lpNumberOfBytesWritten,
IntPtr.Zero);
}
FileInteropFunctions.CloseHandle(hFile);
this.tspan = DateTime.Now.Subtract(initialTime);
label1.Text = "" + this.tspan.TotalMilliseconds + " Milliseconds";
}
else
label1.Text = "Error";
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern unsafe IntPtr CreateFile(
String lpFileName, // Filename
uint dwDesiredAccess, // Access mode
uint dwShareMode, // Share mode
IntPtr attr, // Security Descriptor
uint dwCreationDisposition, // How to create
uint dwFlagsAndAttributes, // File attributes
uint hTemplateFile); // Handle to template file
[DllImport("kernel32.dll")]
public static extern unsafe int WriteFile(IntPtr hFile,
// byte[] lpBuffer,
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer, // also tried this.
uint nNumberOfBytesToWrite,
out uint lpNumberOfBytesWritten,
IntPtr lpOverlapped);
使用FileStream的迭代在我的计算机中大约需要70毫秒。 使用WriteFile的人需要大约550毫秒。
我测试了几次并进行了几次迭代,性能上的差异是一致的。
我不知道为什么非托管代码比托管代码慢。
修改
非常感谢您的解释,伙计们。我认为有一些“魔法”正在经历FileStream,你已经解释得很好。 所以,我知道现在没有简单的途径可以获得这部分的性能,我想问你对其他简单方法获得速度的意见。该文件是实际应用程序中的随机访问,大小范围可以从1MB到1GB。
答案 0 :(得分:3)
您的非托管调用会在FileStream缓冲时尽快将数据写入磁盘(即大多数操作在内存中并且应该更少地调用底层非托管调用)
如果您希望进一步调整性能,可以使用constructors on FileStream来控制缓冲区大小。
答案 1 :(得分:2)
嗯,FileStream是一个围绕CreateFile / WriteFile的包装器。它是由一群聪明人写的。所以我认为没有合理的解释为什么你认为你的那个应该更快:P。
如前所述,FileStream可能在调用WriteFile()之前进行额外缓冲,从而最大限度地减少了非托管方法调用。这很重要 - 只有在必要时才进行非托管呼叫。他们花了。缓冲区大小通常是磁盘扇区大小的倍数。您可以尝试不同的大小,但这取决于操作系统,并且很可能会在其他计算机上产生其他结果。
但是知道WriteFile()也进行内部缓冲也很重要。它不像你调用WriteFile()和bam它写入文件。一旦它的时间它将被刷新到HDD。
我认为有不必要的byte []封送正在进行中。例如,当您调用WriteFile()时,系统会复制您的缓冲区。应该通过unsafe()关键字和一点点黑客来避免它。
还有无法通过FileStream(afaik)访问的FILE_FLAG_SEQUENTIAL_SCAN,它应该让系统知道你将只按顺序执行文件写入/读取。这可能会在理论上提高性能。
答案 2 :(得分:1)
不同之处在于,对WriteFile
的调用是同步的,而对FileStream
的调用则不是。
默认情况下,CreateFile
将创建一个同步文件句柄,因此在写入数据之前,对WriteFile
的调用不会返回。如果您将FILE_FLAG_OVERLAPPED
添加到CreateFile
电话,则未管理的实施将与管理的实施时间大致相同。
请参阅CreateFile
defini