在.NET中,最快的读写磁盘的方法是什么?

6
我有一个小程序,可以读写磁盘上的文件。简单来说,它从一个文件流中读取字节并将其写入另一个文件流。虽然它能够完成任务,但速度不是最快的。
我见过其他应用程序以惊人的速度处理一千兆字节或更多的读/写操作。显然,它们比一个小的.NET应用程序更接近底层。
哪些是最有效的.NET API用于流式传输到/从磁盘?哪些win32 API可用(值得p/invoking)以实现快速的磁盘访问?

3
我不明白为什么WinAPI调用比.NET类更快——毕竟,后者在内部使用前者。除此之外,也许使用内存映射文件(http://en.wikipedia.org/wiki/Memory_mapped_file)会更合适? - Noldorin
为什么Dot.net会有多种写文件的方式?读写文件非常基础,拥有“快速”和“慢速”形式毫无意义 - 因为既然两者都具有相同的目标,没有人会使用“慢速”版本。 - mP.
在半小时内,我可以设置一个测试,比较.NET文件操作(可能是天真的实现,这是问题的一部分)和具有密集IO的本地应用程序(例如QuickPAR),这将使.NET应用程序失色。 这就是问题的要点 - 如何在.NET中实现最佳磁盘吞吐量? - user1228
4个回答

12

快速文件 I/O 不仅仅关乎你所做的具体 API 调用,而是关于如何设计你的应用程序来处理 I/O。

例如,如果你在单个线程上按顺序执行所有 I/O 操作:

  1. 将块读入内存
  2. 以某种方式在内存中处理块
  3. 将块写出到文件
  4. 重复以上步骤直到完成...

那么你会在单个线程的处理循环中限制系统的 I/O 带宽。一种替代方案是将应用程序多线程化,以最大化吞吐量并避免等待时间。这样可以使系统同时利用 CPU 和 I/O 控制器带宽。这种设计的典型形式如下:

  1. 一个或多个工作线程从磁盘读取数据并将其添加到共享输入队列中
  2. 一个或多个工作线程从共享输入队列中读取块,处理它们并将它们添加到共享输出队列中
  3. 一个或多个工作线程从共享输出队列中读取已处理的块,并将它们写入相应的输出文件中。

这不是一个容易设计的架构,需要仔细考虑,以避免创建内存锁争用或同时进行的 I/O 请求过多。你还需要提供控制元数据,以便输出处理状态不是在线程的调用堆栈中管理,而是在输入/输出工作队列中管理。你还必须确保按正确顺序转换和写入输出,因为在多线程 I/O 中,无法保证工作以保证顺序放置在输入队列中。它很复杂,但是可能,并且与串行方法相比,可以显著提高吞吐量。

如果您真的有时间并想从系统中挤出每一点性能,您也可以使用I/O完成端口 - 一个相对较低级别的API - 来最大化吞吐量。
祝好运。

7

.NET文件支持速度足够快(与本机Win32函数相当)。有几个选项可以帮助您提高性能:

  1. 如果您的读写是顺序的,请在实例化FileStream时应用适当的策略来帮助缓存管理器 - 提供RandomAccess或SequentalScan
  2. 考虑使用较大的内存缓冲区来存储读取的数据。
  3. 如果要复制许多小文件,可以先将许多文件一次性读入内存缓冲区中(参见2),然后将文件写入磁盘。
  4. 如果源流和目标流位于不同的位置(即不在同一硬盘驱动器上,可能一个文件在网络上,另一个在本地硬盘驱动器上等),则可以使用异步模式来加速,使用BeginRead读取数据,然后使用BeginWrite写入数据,并在数据被写入时使用BeginRead读取下一个数据块。
  5. 如果仍然认为性能不够(但从我的测试结果来看,它与内部Windows复制相当甚至更快),可以使用CopyFileEx Win32函数(但此函数仅适用于文件,不适用于流)。

1
问题的一部分是关于正确使用它,这个答案至少试图达到这个目的。谢谢。 - user1228

0
你是否对应用程序进行了分析,以确定磁盘I/O是否成为瓶颈?
你运行这个程序的硬件类型是什么?硬件配置如何?
在.NET中,你可以尝试使用System.IO.File命名空间。
对于Win32函数,你可以尝试使用CreateFile、WriteFile和ReadFile系列函数。
以下是一个示例:

http://msdn.microsoft.com/en-us/library/bb540534(VS.85).aspx

这绝对不是一成不变的。这全取决于测试和测量。


如果磁盘IO是问题的话,我个人会非常惊讶...我从未遇到过任何使用.NET基元时磁盘IO达到最大的问题...(除非他正在运行.NET 1,在那里我相信文件流没有内置缓冲区) - jerryjvl
1
问题不是如何,而是有多快。感谢您关于System.IO.File的提示(讽刺,胜利)。 - user1228

0

BinaryReaderBinaryWriter使用适当的缓冲区大小非常快。如果您正在读取结构体,则在本文中描述的不安全方法将使您快速读取,写入也类似。我也同意建议再次检查I/O是否真正成为瓶颈。我之所以首先看到那篇文章,是因为犯了这样的错误。


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