什么是C ++高性能顺序文件I / O最快的方法?
假设以下…
输出:
该文件被打开…
数据“stream”到磁盘。 内存中的数据是在一个大的连续缓冲区。 它直接从缓冲区以原始forms写入磁盘。 缓冲区的大小是可configuration的,但在stream的持续时间内是固定的。 缓冲区被一个接一个地写入文件。 没有进行寻找操作。
…文件已closures。
input:
从头到尾从磁盘读取一个大文件(按上述顺序写入)。
是否有普遍接受的准则,以实现C ++中最快的顺序文件I / O?
一些可能的考虑:
- select最佳缓冲区大小的指导原则
- 像boost :: asio这样的可移植的库是否会被抽象出来,以暴露特定平台的复杂性,或者他们可以被认为是最优的?
- asynchronousI / O总是优于同步? 如果应用程序没有CPU限制,该怎么办?
我意识到这将有平台特定的考虑。 我欢迎一般准则以及特定平台的准则。
(我最感兴趣的是Win x64,但是我也对Solaris和Linux感兴趣)
是否有普遍接受的准则,以实现C ++中最快的顺序文件I / O?
规则0:衡量。 使用所有可用的分析工具并了解它们。 这几乎是编程中的一个命令,如果你不测量它,你不知道它有多快,而I / O更是如此。 如果可能的话,确保在实际工作条件下进行testing。 对于I / O系统没有竞争的过程可以过度优化,对于实际负载下不存在的条件进行微调。
-
使用映射内存而不是写入文件。 这并不总是更快,但它允许有机会以操作系统特定但相对便携的方式优化I / O,避免不必要的复制,并利用操作系统对磁盘实际使用情况的了解。 (如果使用包装器,而不是特定于操作系统的API调用,则为“便携式”)。
-
尽可能尝试并线性化你的输出。 不得不跳过内存寻找要写入的缓冲区可以在优化的条件下有明显的效果,因为caching行,分页和其他内存子系统问题将开始重要。 如果你有很多的缓冲区,请考虑为你尝试做线性化的分散/聚集I / O。
一些可能的考虑:
- select最佳缓冲区大小的指导原则
初学者的页面大小,但准备从那里调整。
- 像boost :: asio这样的可移植的库是否会被抽象出来,以暴露特定平台的复杂性,或者他们可以被认为是最优的?
不要以为是最佳的。 这取决于图书馆在您的平台上的运用有多彻底,以及开发人员为实现这一目标付出了多less努力。 话虽如此,便携式I / O库可以非常快,因为在大多数系统上都存在快速抽象,通常可以提供一个覆盖很多基础的通用API。 Boost.Asio是我有限的知识中最好的,对于它所在的特定平台来说是相当不错的:有一整套操作系统和操作系统特定的API来实现快速的asynchronousI / O(例如epoll , / dev / epoll , kqueue , Windows重叠的I / O ),Asio将它们全部包装起来。
- asynchronousI / O总是优于同步? 如果应用程序没有CPU限制,该怎么办?
asynchronousI / O在原始意义上不比同步I / O更快。 asynchronousI / O所做的是确保您的代码不会浪费时间等待I / O完成。 比其他方法更快速,比如使用线程的其他方法,因为当I / O准备好而不是之前,它会调用你的代码。 没有错误的开始或关于空闲的线程需要被终止。
一般的build议是closures缓冲和读/写在大块(但不是太大,那么你会浪费太多的时间等待整个I / O完成,否则你可以开始在第一兆字节咀嚼已经。用这个algorithmfind最佳位置是微不足道的,只有一个旋钮:块大小)。
除此之外,对于inputmmap()
文件共享和只读(如果不是最快的话)是最有效的方式。 调用madvise()
如果你的平台有它,告诉内核你将如何遍历文件,所以它可以做预读,然后迅速抛出页面。
对于输出来说,如果你已经有了一个缓冲区,可以考虑使用文件(也可以使用mmap()
)来支持它,所以你不必复制用户空间中的数据。
如果mmap()
不符合你的喜好,那么就有fadvise()
,对于非常难的asynchronous文件I / O。
(以上都是POSIX,Windows的名字可能不一样)。
对于Windows,如果您select使用平台特定的Windows API调用,则需要确保在CreateFile()调用中使用FILE_FLAG_SEQUENTIAL_SCAN。 这将优化I / O的caching。 就缓冲区大小而言,通常build议缓冲区大小是磁盘扇区大小的倍数。 8K是一个很好的起点,没有什么可以从更大的获得。
本文讨论Windows上的asynchronous和同步之间的比较。
http://msdn.microsoft.com/en-us/library/aa365683(VS.85).aspx
正如你上面提到的,这一切都取决于你正在使用的机器/系统/库。 一个系统上的快速解决scheme可能在另一个系统上很慢。
尽pipe一般的指导方针是尽可能大的写入大块。
通常一次写入一个字节是最慢的。
要知道的最好的方法是编写几种不同的方法并描述它们。
你问过关于C ++的问题,但是听起来好像你已经过去了,并准备好了一点平台。
在Windows上,带有文件映射的FILE_FLAG_SEQUENTIAL_SCAN
可能是最快的方法。 实际上,您的进程可以在文件实际进入磁盘之前退出。 如果没有明确封锁的刷新操作,Windows可能需要5分钟才能开始写入这些页面。
如果文件不在本地设备上,而是在networking驱动器上,则需要小心。 networking错误将显示为SEH错误,您将需要准备处理。
在* nixes上,您可能会获得更高的性能,顺序写入裸磁盘设备。 这也可能在Windows上,但不是由API支持。 这将避免一点文件系统开销,但它可能不足以有用。
松散地说,RAM比磁盘快1000倍以上,而且CPU也更快。 可能没有太多的逻辑优化可以帮助,除非尽可能地避免磁头移动(寻找)。 这个文件专用的磁盘在这里可以帮助很大。
您将通过使用CreateFile
和ReadFile
获得绝对最快的性能。 使用FILE_FLAG_SEQUENTIAL_SCAN
打开文件。
用两个幂的缓冲区大小读取。 只有基准可以确定这个数字。 我曾经看到它是8K。 还有一次,我发现它是8M! 这个变化很大。
这取决于CPU高速caching的大小,操作系统预读的效率以及与执行许多小写操作相关的开销。
内存映射不是最快的方法。 它有更多的开销,因为您无法控制块大小,并且操作系统在所有页面中都需要出错。
在Linux上,缓冲区的读写操作会加快速度,越来越多的缓冲区大小越来越多,但是回报正在减less,您通常希望使用BUFSIZ
(由stdio.h
定义),因为较大的缓冲区大小无济于事。
mmap
ing提供了对文件的最快访问,但是mmap
调用本身相当昂贵。 对于小文件(16KiB) read
和write
系统调用win(请参阅https://stackoverflow.com/a/39196499/1084774查看通过;read
和mmap
读取的数字)。