我正在接收一个文件作为 byte[] 数据包流(总大小事先不知道),我需要将其存储在某个地方,然后在收到后立即处理它(我无法即时处理)。接收的文件总大小可以从小到 10 KB 到超过 4 GB 不等。
- 存储接收到的数据的一个选项是使用
MemoryStream
,即一系列MemoryStream.Write(bufferReceived, 0, count)
调用来存储接收到的数据包。这很简单,但显然会导致大文件的内存不足异常。 - 另一种选择是使用
FileStream
,即FileStream.Write(bufferReceived, 0, count)
.这样,就不会发生内存不足异常,但我不确定的是磁盘写入导致性能不佳(只要内存仍然有足够的可用,我就不想发生这种情况) - 我想尽可能避免磁盘访问,但我不知道控制它的方法。
我做了一些测试,大多数时候,连续 10 000 次调用 MemoryStream.Write()
与 FileStream.Write()
之间的性能差异似乎很小,但很大程度上似乎取决于缓冲区大小和有问题的数据总量(即写入次数)。显然,MemoryStream
规模重新分配也是一个因素。
-
使用
MemoryStream
和FileStream
的组合是否有意义,即默认写入内存流,但是一旦接收的数据总量超过 500 MB,请将其写入FileStream
; 然后,从两个流中读取块以处理接收到的数据(第一个处理距离MemoryStream
500 MB, 处理它,然后从FileStream
中读取)? -
另一种解决方案是使用自定义内存流实现,该实现不需要连续的地址空间进行内部阵列分配(即内存流的链接列表);这样,至少在64位环境中,内存不足异常应该不再是问题。缺点:额外的工作,更多的错误空间。
那么,FileStream
与MemoryStream
读/写在磁盘访问和内存缓存方面的行为如何,即数据大小/性能平衡。我希望只要有足够的 RAM 可用,FileStream
无论如何都会从内存(缓存)内部读取/写入,而虚拟内存将负责其余的工作。但我不知道FileStream
在写入磁盘时多久会显式访问一次磁盘。
任何帮助将不胜感激。
不,试图优化这一点没有任何意义。 Windows本身已经缓存了文件写入,它们由文件系统缓存缓冲。 所以你的测试是准确的,MemoryStream.Write() 和 FileStream.Write() 实际上都写入 RAM,并且没有明显的性能差异。 文件系统驱动程序在后台延迟将其写入磁盘。
用于文件系统缓存的 RAM 是进程声称其 RAM 需求后剩余的内存。 通过使用内存流,可以降低文件系统缓存的有效性。 或者换句话说,你用一个换另一个而没有好处。 事实上,你的情况更糟,你使用了两倍的 RAM。
不要帮忙,这已经在操作系统内部进行了大量优化。
最新版本的Windows默认启用写入缓存,因此我认为您可以简单地使用FileStream
,让Windows管理何时或是否实际写入物理硬盘驱动器。
如果这些文件在您收到后没有保留,您可能应该将这些文件写入临时目录并在处理完它们后将其删除。
使用允许您定义缓冲区大小的 FileStream 构造函数。例如:
using (outputFile = new FileStream("filename",
FileMode.Create, FileAccess.Write, FileShare.None, 65536))
{
}
默认缓冲区大小为 4K。使用 64K 缓冲区可减少对文件系统的调用次数。缓冲区越大,写入次数越少,但每次写入开始花费更长的时间。经验数据(多年使用这些东西)表明64K是一个非常好的选择。
正如其他人指出的那样,文件系统可能会进行进一步的缓存,并在后台进行实际的磁盘写入。您接收数据的速度极不可能比写入FileStream
快