为什么从内存映射文件读取数据速度如此之快?

18

我对内存映射输入/输出没有太多经验,但是第一次使用后,我被它们的速度惊呆了。在我的性能测试中,我发现从内存映射文件读取比通过常规C++ stdio读取快30倍。

我的测试数据是一个3GB的二进制文件,其中包含20个大型双精度浮点数数组。我的测试程序结构是,调用外部模块的读取方法,该方法在幕后使用内存映射I/O。每次调用读取方法时,这个外部模块都会返回一个指向数据的指针和大小。从这个方法返回后,我调用memcpy将返回缓冲区的内容复制到另一个数组中。因为我正在使用memcpy从内存映射文件中复制数据,所以我预期内存映射读取不会比普通stdio快得多,但我很惊讶它快了30倍。

为什么从内存映射文件读取如此快?

PS:我使用的是Windows机器。我对我的I/O速度进行了基准测试,我的机器最大磁盘传输速率约为90 MiB/s。


你可以在这里找到答案:https://dev59.com/3XVC5IYBdhLWcg3ww0Dr - Steve Lorimer
@SteveLorimer:在发布之前我确实阅读了那个页面。从那个帖子中我所了解到的是,如果数据不在内存中,那么操作系统必须从磁盘中获取数据。但是,在我的测试中,我没有看到任何磁盘I/O与3GB数据传输相对应,我只看到了一个总计为2630字节的传输量。然而,当我检查memcpy数组的内容时,它们逐字节地匹配预期的数据。 - DigitalEye
10
标准基准风险。点击此处查看如何重置Windows文件缓存的方法。请注意,译文已经尽力使内容更加通俗易懂,但不会改变原意,并且不会包含解释或其他信息。 - Hans Passant
4
感谢@HansPassant提供的链接。在我清空Windows文件缓存后进行样本运行,我发现我的超快速内存映射I/O速度比之前慢了30倍! - DigitalEye
@DigitalEye在这种情况下,您是否仍然认为以下答案是正确的解释? - Baiyan Huang
@BaiyanHuang:好久不见了,但当我重新阅读这个线程时,我似乎观察到所有通过内存映射I/O实现的性能提升都在清除文件缓存后被抵消了。换句话说,我似乎已经证明了内存映射I/O并不比标准I/O更快!我需要重新审视那些数字以确保没有错误。但是,内存映射I/O确实存在于性能方面,并且接受的答案描述了它如何实现这些收益。我想要进行一项新的基准测试。 - DigitalEye
1个回答

31

操作系统内核IO例程,如读取或写入调用,仍然只是函数。这些函数被编写为将数据从用户空间缓冲区复制到内核空间结构,然后再复制到设备上。考虑到存在用户缓冲区、IO库缓冲区(例如stdio buf)、内核缓冲区和文件等,数据可能需要通过三个副本在程序和磁盘之间传输。IO例程还必须具有鲁棒性,最后,系统调用本身会造成延迟(陷入内核、上下文切换、再次唤醒进程)。

当你内存映射一个文件时,你可以跳过很多步骤,消除缓冲区的复制。通过有效地将文件视为一个大型虚拟数组,使得通过随机访问而不必经过系统调用开销,从而减少每个IO的延迟,如果原始代码效率低下(许多小的随机IO调用),则开销可以被大大降低。

虚拟内存、多处理器操作系统的抽象是有代价的,这就是它。

但是,在某些情况下,您可以通过禁用缓冲来提高IO效率,例如大容量连续写入。但除此之外,你无法在不彻底取消操作系统的情况下改善内存映射IO的性能。


1
那么,可以这样说,在我的情况下,数据是直接从磁盘传输到我的数组中的,也就是说只有一次复制,而不是您所描述的三次:从磁盘到内核缓冲区,从内核缓冲区到I/O缓冲区,最后从I/O缓冲区到我的程序内存? - DigitalEye
3
是的。此外,如果内核将您的文件映射到一组页面,并且这些页面不存在(还未被加载),则内核会发生页错误并直接读取这些页面。 - codenheim

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