当使用BinaryReader读取大文件(>1 GB)时,最佳缓冲区大小是多少?

10

我正在阅读二进制文件,这里是一个样例:

public static byte[] ReadFully(Stream input)
{
    byte[] buffer = new byte[16*1024];
    int read;
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        ......
    }

}

显然,缓冲区大小(16*1024)在性能方面起着重要作用。我读过这取决于I/O技术(SATASSDSCSI等)以及文件所在分区的片段大小(我们可以在格式化分区时定义)。

但是问题在于: 是否有公式或最佳实践来定义缓冲区大小?目前,我是根据试错法来定义的。

编辑: 我已经在我的服务器上使用不同的缓冲区大小测试了应用程序,并且我得到了4095*256*16(16 MB)的最佳性能!!! 4096较慢4秒。

以下是一些旧帖子,非常有帮助,但我仍然无法理解原因:


http://research.microsoft.com/pubs/64538/tr-2004-136.doc - m4ngl3r
您帖子的标题与示例代码不符…另外,如果您返回单个字节数组(Stream.Length 对于文件流可以正常工作),我不确定为什么要分块读取。 - Alexei Levenkov
@Alexei 很难预测 OP 真正在做什么,返回的数组可能不是读取的数组。 - ST3
@ST3 我真的希望 ReadFully 意味着读取流并返回完整内容... 但你是绝对正确的,无法预测方法实际上在做什么/是否反映了实际问题... - Alexei Levenkov
1
附注:请考虑提供一些证据,表明“缓冲区大小显然在性能方面起着重要作用”。在常规文件IO期间涉及到许多层高速缓存的情况下,“显然”并不那么明显。 - Alexei Levenkov
2个回答

7
"Sequential File Programming Patterns and Performance with .NET"是一篇关于I/O性能提升的优秀文章。在PDF文件的第8页中,显示了缓冲区大小大于8字节时的带宽是恒定的。需要考虑到这篇文章是2004年编写的,并且硬盘驱动器为“Maxtor 250 GB 7200 RPM SATA disk”,最新的I/O技术可能会有不同的结果。
如果您想要最佳的性能,请查看pinvoke.net或PDF文件的第9页,未缓冲文件性能测量显示出更好的结果:
在未缓冲的I/O中,磁盘数据直接在应用程序地址空间和设备之间移动,没有任何中间复制。
总结:
  • 对于单个磁盘,请使用.NET Framework的默认值-它们为顺序文件访问提供了出色的性能。
  • 在创建文件时预先分配大型顺序文件(使用SetLength()方法)。与碎片文件相比,这通常可以提高约13%的速度。
  • 至少目前来看,磁盘阵列需要未缓冲的I/O才能实现最高性能-缓冲I/O可能比未缓冲I/O慢8倍。我们希望这个问题会在.NET Framework的后续版本中得到解决。
  • 如果您自己进行缓冲,请使用大请求大小(64 KB是一个好的起点)。使用.NET Framework,单个处理器可以使用未缓冲的I/O读写磁盘阵列超过800 Mbytes/s。

4

并没有最好或最差的缓冲区大小,但你需要考虑一些方面。

由于你正在使用C#,因此在Windows上运行,Windows使用NTFS,其页面大小为4 MB,因此建议使用4096的倍数。所以你的缓冲区大小是16*1024 = 4*4096,这是一个很好的选择,但无法说它比16*4096更好或更差。

一切都取决于情况和程序的要求。请记住,在这里你不能选择最佳选项,只能选择一些更好的选项。我建议使用4096,但你也可以使用自己的4*4096甚至16*4096,但请记住,该缓冲区将分配在堆上,因此其分配需要一些时间,因此你不想分配一个大缓冲区,例如128*4096


1
+1。超过80K将强制缓冲区进入LOH并带来自己的问题(主要针对32位进程)... 4-64K可能是大多数情况下应坚持的范围。 - Alexei Levenkov
@Alexei,请查看有关运行时 - 垃圾回收性能改进的发布说明:https://blogs.msdn.microsoft.com/dotnet/2017/10/17/announcing-the-net-framework-4-7-1/ - juFo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接