我有一个问题,我找不到原因。
我正在创建一个自定义存档文件。我使用MemoryStream
存储数据,最后使用FileStream
将数据写入磁盘。
我的硬盘是 SSD ,但是速度太慢。当我尝试仅将95 MB的内容写入文件时,花了12秒钟才能写入!
我尝试过Filestream.Write
和File.WriteAllBytes
,但相同。
最后我有了一个复制的想法,它快了 100倍!
我需要知道为什么会这样以及写函数出了什么问题。
这是我的代码:
//// First of all I create an example 150MB file
Random randomgen = new Random();
byte[] new_byte_array = new byte[150000000];
randomgen.NextBytes(new_byte_array);
//// I turned the byte array into a MemoryStream
MemoryStream file1 = new MemoryStream(new_byte_array);
//// HERE I DO SOME THINGS WITH THE MEMORYSTREAM
/// Method 1 : File.WriteAllBytes | 13,944 ms
byte[] output = file1.ToArray();
File.WriteAllBytes("output.test", output);
// Method 2 : FileStream | 8,471 ms
byte[] output = file1.ToArray();
FileStream outfile = new FileStream("outputfile",FileMode.Create,FileAccess.ReadWrite);
outfile.Write(output,0, output.Length);
// Method 3 | FileStream | 147 ms !!!! :|
FileStream outfile = new FileStream("outputfile",FileMode.Create,FileAccess.ReadWrite);
file1.CopyTo(outfile);
此外,file1.ToArray()
仅需要90毫秒即可将MemoryStream转换为字节。
为什么会这样,其背后的原因和逻辑是什么?
答案 0 :(得分:5)
Dmytro Mukalov有权利。当您实际进行FileStream
时,通过扩展Flush
内部缓冲区获得的性能将被取消。我进行了更深入的研究,并进行了一些基准测试,看来Stream.CopyTo
和FileStream.Write
之间的区别在于Stream.CopyTo
更智能地使用I / O缓冲区并通过逐块复制来提高性能。最后,CopyTo
在引擎盖下使用Write
。最佳缓冲区大小已在here中进行了讨论。
最佳缓冲区大小与许多因素有关:文件系统 块大小,CPU缓存大小和缓存延迟。大多数文件系统是 配置为使用4096或8192的块大小。理论上,如果您 配置缓冲区大小,以便读取的字节数比 磁盘块,文件系统的操作可能非常繁琐 效率低下(即如果您将缓冲区配置为在以下位置读取4100字节 一次,每次读取将需要文件系统进行2次块读取)。如果 块已经在缓存中,然后您最终付出代价 RAM-> L3 / L2缓存延迟。如果您不走运,但障碍却不是 在缓存中,您还需要付出磁盘-> RAM延迟的代价。
所以要回答您的问题,就您而言,使用Write
时使用的是未优化的缓冲区大小,而使用CopyTo
时则进行了优化,或者更好的说Stream
本身会针对你。
通常,您可以通过扩展CopyTo
内部缓冲区来强制未优化的FileStream
,在这种情况下,结果应比未优化的Write
慢。
FileStream outfile = new FileStream("outputfile",
FileMode.Create,
FileAccess.ReadWrite,
FileShare.Read,
150000000); //internal buffer will lead to inefficient disk write
file1.CopyTo(outfile);
outfile.Flush(); //don't forget to flush data to disk
我对Write
和FileStream
的{{1}}方法进行了分析,结果是MemoryStream
始终使用内部缓冲区来复制数据,并且非常快。 MemoryStream
本身有一个开关,如果请求的FileStream
(在您使用默认count >= bufferSize
缓冲区的情况下是正确的)上,则默认情况是FileStream
。在这种情况下,4096
除了本地FileStream
根本不使用缓冲区。
技巧是通过覆盖默认缓冲区大小来强制Win32Native.WriteFile
使用缓冲区。试试这个:
FileStream
n.b。我并不是说这是最佳的缓冲区大小,只是为了解释发生了什么。要使用// Method 2 : FileStream | 8,471 ms
byte[] output = file1.ToArray();
FileStream outfile = new FileStream("outputfile",
FileMode.Create,
FileAccess.ReadWrite,
FileShare.Read,
output.Length + 1); // important, the size of the buffer
outfile.Write(output, 0, output.Length);
检查最佳缓冲区大小,请参考link。