结合 FileStream 和 MemoryStream,以避免在接收千兆字节数据时进行磁盘访问/分页

本文关键字:数据 字节数 字节 分页 访问 磁盘 MemoryStream FileStream 结合 | 更新日期: 2023-09-27 18:37:25

我正在接收一个文件作为 byte[] 数据包流(总大小事先不知道),我需要将其存储在某个地方,然后在收到后立即处理它(我无法即时处理)。接收的文件总大小可以从小到 10 KB 到超过 4 GB 不等。

  • 存储接收到的数据的一个选项是使用MemoryStream,即一系列MemoryStream.Write(bufferReceived, 0, count)调用来存储接收到的数据包。这很简单,但显然会导致大文件的内存不足异常。
  • 另一种选择是使用FileStream,即 FileStream.Write(bufferReceived, 0, count) .这样,就不会发生内存不足异常,但我不确定的是磁盘写入导致性能不佳(只要内存仍然有足够的可用,我就不想发生这种情况) - 我想尽可能避免磁盘访问,但我不知道控制它的方法。

我做了一些测试,大多数时候,连续 10 000 次调用 MemoryStream.Write()FileStream.Write() 之间的性能差异似乎很小,但很大程度上似乎取决于缓冲区大小和有问题的数据总量(即写入次数)。显然,MemoryStream规模重新分配也是一个因素。

  1. 使用 MemoryStreamFileStream 的组合是否有意义,即默认写入内存流,但是一旦接收的数据总量超过 500 MB,请将其写入 FileStream ; 然后,从两个流中读取块以处理接收到的数据(第一个处理距离MemoryStream 500 MB, 处理它,然后从FileStream中读取)?

  2. 另一种解决方案是使用自定义内存流实现,该实现不需要连续的地址空间进行内部阵列分配(即内存流的链接列表);这样,至少在64位环境中,内存不足异常应该不再是问题。缺点:额外的工作,更多的错误空间。

那么,FileStreamMemoryStream读/写在磁盘访问和内存缓存方面的行为如何,即数据大小/性能平衡。我希望只要有足够的 RAM 可用,FileStream无论如何都会从内存(缓存)内部读取/写入,而虚拟内存将负责其余的工作。但我不知道FileStream在写入磁盘时多久会显式访问一次磁盘。

任何帮助将不胜感激。

结合 FileStream 和 MemoryStream,以避免在接收千兆字节数据时进行磁盘访问/分页

不,试图优化这一点没有任何意义。 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