使用流进行文件I/O操作 - 最佳内存缓冲区大小

60

我正在编写一个小型的I/O库,以协助完成一个更大(业余)项目。该库的一部分通过FileStream对象读取/写入文件,并执行各种操作。在每个StreamReader.Read(...)调用时,我会触发一个事件,用于在主应用程序中显示进度信息。循环中进行的处理是多样化的,但时间消耗不太大(例如可以只是简单的文件复制,或者可能涉及加密...)。

我的主要问题是:使用哪种最佳内存缓冲区大小?考虑到物理磁盘布局,我可以选择2k,这将覆盖CD扇区大小,并且是512字节硬盘扇区的好倍数。在更高的抽象层次上,您可以选择更大的缓冲区,它可以一次读取整个FAT簇。我意识到在今天的PC上,我可以选择更耗费内存的选项(例如几MiB),但这会增加UI更新之间的时间,并导致用户感知到一个反应较慢的应用程序。

另外,我最终希望为托管在FTP / HTTP服务器上的文件提供类似的接口(通过本地网络/较快的DSL)。对于那些文件,什么是最佳的内存缓冲区大小(再次是“最佳情况”下感知响应速度和性能之间的权衡)?


这可能会有帮助:https://dev59.com/8WIk5IYBdhLWcg3wIq9U#19837238?noredirect=1#19837238 - Amir Pournasserian
我本以为操作系统或Windows会维护自己的硬件能力和速度配置文件,并提供一个服务,推荐给定存储卷和活动(例如随机读/写与顺序读/写)的最佳缓冲区大小 - 这将消除猜测。 - Dai
4个回答

89

文件已经被文件系统缓存了。你只需要选择一个缓冲区大小,不要过于频繁地强制FileStream调用本地Windows ReadFile() API来填充缓冲区。不要低于1KB,超过16KB会浪费内存并对CPU的L1缓存(通常为16或32KB的数据)不友好。

4KB是一种传统的选择,尽管这只会偶然地跨越一个虚拟内存页。很难进行分析;你最终会测量读取缓存文件需要多长时间。如果数据在缓存中可用,则运行速度为RAM速度,每秒5GB及以上。第二次运行您的测试时,它将在缓存中,而在生产环境中不会经常发生。文件I/O完全受到磁盘驱动器或NIC的支配,并且速度非常慢,复制数据则很快。4KB的缓冲区大小可以很好地工作。


低缓冲区大小,如4-8kb也是首选,因为CPU缓存可以容纳这些量。如果太小,你可能会积累大量的内核转换开销。 - usr
@HansPassant:我的应用程序同时处理许多小文件和大文件。对于小于4KB的文件,4KB的大小会对性能产生不利影响吗? - Raheel Khan
4
4KB是.NET Framework默认使用的值:http://msdn.microsoft.com/en-us/library/dd783870.aspx - giammin
1
如果文档是正确的,在4.5中,它们将默认值增加到81920。 - Justin Helgerson
11
文档正确,.NET Reflector显示_DefaultCopyBufferSize的值为0x14000(81920或80K)。但是,这仅适用于从流复制到另一个流时的缓冲区大小,并非数据缓冲区。BufferedStream类_DefaultBufferSize0x1000(4096或4k),这将更好地了解.NET框架用于流的缓冲区大小。 - Owain Williams
我了解使用 FileStream 时,如果要获得最佳的异步性能,缓冲区大小应至少为1兆字节,以便在等待重叠磁盘IO完成时异步开销的成本值得。我不记得从哪里得到这个细节,但你同意吗? - Dai

4

当我直接通过流对象处理文件时,我通常使用4096字节。在多个I/O领域(本地文件系统、LAN/SMB、网络流等)中似乎相当有效,但我没有对其进行剖析或其他操作。很久以前,我看到过几个示例使用这个大小,它就一直留在我的记忆中。但这并不意味着它是最好的。


好的。我永远不会使用低于4k的任何东西,因为它是虚拟内存系统管理的最小块(磁盘缓存基于此)。 - Ben Voigt

3

"这取决于。"

你需要使用不同的缓冲区大小来测试你的应用程序,以确定哪个是最好的。你不能提前猜测。


0

我认为默认值通常是最好的选择 - 因此我在FileStream类中使用4096B,它基于internal const int变量DefaultBufferSize


4
默认选项并不总是最佳选择,它只是在更常见的情况下的一个好的折中方案,而不是适用于所有负载的最优解。 - Hejazzman

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