【问题标题】:What's the fastest way to read/write to disk in .NET?在 .NET 中读取/写入磁盘的最快方法是什么?
【发布时间】:2010-11-08 06:25:36
【问题描述】:

我有一个在磁盘上读写文件的小程序。将其分解为最简单的级别,它从一个文件流中读取字节并将它们写入另一个文件流。它可以很好地履行职责,但不是最快的。

我见过其他应用程序可以以惊人的速度完成千兆字节或更多的读取/写入。显然,它们比小型 .NET 应用程序更接近金属。

什么是最有效的 .NET API 用于向/从磁盘进行流式传输?哪些 win32 API 可用(并且值得 p/调用)用于快速磁盘访问?

【问题讨论】:

  • 我不明白为什么 WinAPI 调用应该比 .NET 类更快——毕竟后者在内部使用前者。除此之外,内存映射文件 (en.wikipedia.org/wiki/Memory_mapped_file) 是否合适?
  • 为什么 Dot.net 有不止一种写入文件的方法?读取和写入文件是非常基本的,具有“快速”和“慢速”形式没有任何意义 - 因为没有人会使用“慢速”版本,因为它们都有相同的目标。
  • 在半小时内,我可以设置一个测试来比较 .net 文件操作(可能是幼稚的实现,这是问题的一部分)和具有密集 IO 的本机应用程序(例如 QuickPAR),它将关闭 .NET 应用程序的大门。这就是问题的重点——如何在 .NET 中实现最佳磁盘吞吐量?

标签: .net winapi streaming disk


【解决方案1】:

您是否分析过您的应用程序以确定磁盘 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)
  • 问题不在于如何,而在于如何快速。感谢您提供有关 System.IO.File 的提示(讽刺,ftw)。
【解决方案2】:

BinaryReaderBinaryWriter 具有合适的缓冲区大小非常快。如果您正在阅读结构,in this article 描述的不安全方法将使您阅读速度更快,写作也类似。我也同意仔细检查 I/O 确实是瓶颈的建议。由于这样的错误,我第一次看到那篇文章。

【讨论】:

    【解决方案3】:

    .NET 文件支持足够快(可与本机 Win32 函数相媲美)。可以帮助您提高绩效的几个选项:

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

    【讨论】:

    • 部分问题是关于正确使用它,这个答案至少试图完成。谢谢。
    【解决方案4】:

    快速文件 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 completion ports - 一个相对较低级别的 API - 来最大化吞吐量。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 2015-05-29
      • 1970-01-01
      • 2020-12-29
      • 1970-01-01
      • 1970-01-01
      • 2012-06-01
      • 2011-06-15
      • 1970-01-01
      • 2018-04-29
      相关资源
      最近更新 更多