代码之家  ›  专栏  ›  技术社区  ›  Adam Holmberg

C++中最快的顺序文件I/O的最快方法是什么?

  •  46
  • Adam Holmberg  · 技术社区  · 15 年前

    假设以下为…
    输出:
    文件已打开…
    数据被“流”到磁盘。内存中的数据在一个大的连续缓冲区中。它直接从缓冲区以原始形式写入磁盘。缓冲区的大小是可配置的,但在流的持续时间内是固定的。缓冲区逐个写入文件。不执行搜寻操作。
    …文件已关闭。

    输入:
    从磁盘开始到结束读取一个大文件(按上述顺序写入)。


    在C++中实现最快的顺序文件I/O是否有普遍接受的准则?

    一些可能的考虑:

    • 选择最佳缓冲区大小的指南
    • 像boost::asio这样的可移植库是否过于抽象,以至于无法暴露特定平台的复杂性,或者可以假定它们是最佳的?
    • 异步I/O总是比同步I/O更好吗?如果应用程序不受CPU限制怎么办?

    我知道这将有特定于平台的考虑。我欢迎一般性的指导方针以及针对特定平台的指导方针。
    (我对Win X64最感兴趣,但我也对Solaris和Linux的评论感兴趣)

    7 回复  |  直到 7 年前
        1
  •  31
  •   quark    15 年前

    在C++中实现最快的顺序文件I/O是否有普遍接受的准则?

    规则0:度量值。使用所有可用的分析工具并了解它们。这几乎是编程中的一条戒律,如果你不测量它,你就不知道它有多快,而对于I/O来说,这甚至更为真实。确保在 实际工作条件 如果可能的话。对于I/O系统没有竞争的过程,可以过度优化,针对实际负载下不存在的条件进行微调。

    1. 使用映射内存而不是写入文件。这并不总是更快,但它允许通过避免不必要的复制,并利用操作系统对磁盘实际使用方式的了解,以操作系统特定但相对可移植的方式优化I/O。(如果使用包装器,而不是操作系统特定的API调用,则为“可移植”)。

    2. 尽量使输出线性化。在优化条件下,为了找到要写的缓冲区而必须在内存中跳跃,这会产生明显的效果,因为缓存线、分页和其他内存子系统问题将开始变得重要。如果有很多缓冲区,请查看对 分散收集I/O 它试图为你做线性化。

    一些可能的考虑:

    • 选择最佳缓冲区大小的指南

    页面大小适合初学者,但要做好从中进行调整的准备。

    • 像boost::asio这样的可移植库是否过于抽象,以至于无法暴露出复杂性? 一个特定的平台,或者可以假设它们是最优的?

    不要以为它是最佳的。这取决于库在您的平台上得到了多彻底的锻炼,以及开发人员投入了多大的精力使它快速运行。说了一个便携式I/O库 可以 要非常快,因为大多数系统上都存在快速抽象,而且通常可以想出一个涵盖很多基础的通用API。据我所知,ASIO非常适合它所处的特定平台:有一整套操作系统和操作系统变种特定的API用于快速异步I/O(例如 epoll , /dev/epoll , kqueue , Windows overlapped I/O 阿西奥把它们都包起来了。

    • 异步I/O总是比同步I/O更好吗?如果应用程序不受CPU限制怎么办?

    异步I/O在原始意义上并不比同步I/O快。异步I/O所做的是确保 你的 代码不会浪费等待I/O完成的时间。一般来说,它比另一种不浪费时间的方法(即使用线程)更快,因为它将在I/O准备好时(而不是在I/O之前)调用代码。不存在需要终止的空闲线程的错误启动或问题。

        2
  •  11
  •   sth ypicasso    7 年前

    一个一般的建议是关闭缓冲和大容量读/写(但不要太大,这样您将浪费太多时间等待整个I/O完成,否则您就可以开始大嚼第一兆字节了。用这个算法找到最佳位置是很简单的,只有一个旋钮可以转动:块大小)。

    除此之外,输入 mmap() 共享和只读文件是(如果不是最快的,那么)最有效的方法。呼叫 madvise() 如果您的平台拥有它,那么就告诉内核您将如何遍历该文件,这样它就可以进行预读,然后快速地再次抛出页面。

    对于输出,如果已经有一个缓冲区,请考虑使用一个文件(也可以使用 MAMP() ,因此您不必在用户空间中复制数据。

    如果 MAMP() 不是你喜欢的,那么 fadvise() 而且,对于真正困难的情况,异步文件I/O。

    (以上都是POSIX,Windows名称可能不同)。

        3
  •  6
  •   Michael A. McCloskey    15 年前

    对于Windows,如果您选择使用平台特定的Windows API调用,那么您需要确保在createfile()调用中使用文件_标志_顺序_扫描。这将优化I/O的缓存。就缓冲区大小而言,通常建议缓冲区大小是磁盘扇区大小的倍数。8K是一个很好的起点,从变大的过程中几乎得不到什么好处。

    本文讨论了Windows上异步和同步的比较。

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

        4
  •  3
  •   KPexEA    15 年前

    如上所述,这完全取决于您使用的机器/系统/库。一个系统上的快速解决方案可能会比另一个系统慢。

    不过,一般的指导方针是尽可能多地写块。
    通常一次写入一个字节是最慢的。

    最好的确定方法是用不同的方式编写代码并分析它们。

        5
  •  2
  •   Marsh Ray    15 年前

    你问过C++,但是听起来你已经过了,准备好了一个特定的平台。

    在Windows上, FILE_FLAG_SEQUENTIAL_SCAN 使用文件映射可能是最快的方法。实际上,您的进程可以在文件实际到达磁盘之前退出。如果没有显式阻止刷新操作,Windows可能需要5分钟才能开始写入这些页。

    如果文件不在本地设备上,而是在网络驱动器上,则需要小心。网络错误将显示为SEH错误,您需要做好处理准备。

    在*nixes上,您可能会得到一个更高的性能,按顺序写入原始磁盘设备。这在Windows上也是可能的,但API并不支持。这将避免一点文件系统开销,但可能不够有用。

    不严格地说,RAM比磁盘快1000倍或更多,CPU也更快。除了尽可能避免移动磁盘头(SEEK),可能没有太多的逻辑优化可以帮助您。专门用于这个文件的磁盘在这里有很大的帮助。

        6
  •  2
  •   kkm -still wary of SE promises    9 年前

    通过使用 CreateFile ReadFile . 打开文件时使用 FILE_FLAG_SEQUENTIAL_SCAN .

    读取缓冲区大小为2的幂。只有基准测试才能确定这个数字。我见过一次是8公里。又一次我发现它是8米!这变化很大。

    它取决于CPU缓存的大小、操作系统预读的效率以及与执行许多小的写入相关的开销。

    内存映射是 最快的方法。它有更多的开销,因为您不能控制块的大小,并且操作系统需要在所有页面上出错。

        7
  •  2
  •   Community uzul    7 年前

    在Linux上,缓冲的读写速度会大大提高,随着缓冲区大小的增加,速度也会越来越快,但回报却在减少,您通常希望使用 BUFSIZ (由 stdio.h )因为较大的缓冲区大小不会有太大帮助。

    mmap ING提供了对文件的最快访问,但是 MMAP 打电话本身很贵。小文件(16kib) read write 系统调用获胜(请参见 https://stackoverflow.com/a/39196499/1084774 关于阅读的数字 阅读 MMAP )