内存映射文件与标准 I/O 系统调用相比如何显著提高性能?

内存映射文件与标准 I/O 系统调用相比如何显著提高性能?

操作系统概念说

考虑顺序读取磁盘上的文件使用标准系统调用 open()、read() 和 write()每个文件访问都需要系统调用和磁盘访问

或者,我们可以使用到目前为止讨论的虚拟内存技术将文件 I/O 视为常规内存访问。这种方法被称为 内存映射文件,允许虚拟地址空间的一部分与文件逻辑关联。正如我们将看到的,这可能导致性能显着提高。文件的内存映射是通过将磁盘块映射到内存中的一个或多个页面来完成的。对文件的初始访问通过普通的请求分页进行,从而导致页面错误。然而,文件的页面大小部分从文件系统读取到物理页面中(某些系统可能选择一次读入超过页面大小的内存块)。随后对该文件的读取和写入将作为常规内存访问进行处理。通过内存操作文件而不是产生使用 read() 和 write() 系统调用的开销,可以简化并加速文件访问和使用。

您能分析一下内存映射文件的性能吗?

如果我是正确的,内存映射文件的工作原理如下。它需要一个系统调用来创建内存映射。然后当它访问映射的内存时,就会发生页面错误。页面错误也有开销。

内存映射文件与标准 I/O 系统调用相比如何显著提高性能?

答案1

内存映射文件直接避免了复制read()write()调用时发生的缓冲区。调用read()write()包含指向存储数据的进程地址空间中的缓冲区的指针。内核必须将数据复制到这些位置或从这些位置复制数据。使用mmap()将文件映射到进程的地址空间,因此进程可以直接寻址文件而无需复制。

如果文件在初始时加载到内存,则在初始调用后访问内存映射文件时也没有系统调用开销mmap()。如果映射文件的某个页面不在内存中,则访问将产生错误并要求内核将该页面加载到内存中。读取大块read()可能比mmap()在这种情况下更快,如果mmap()会产生大量读取文件的错误。 (可以提前通知内核madvise()以便内核可以在访问之前提前加载页面)。

欲了解更多详情,Stack Overflow 上有相关问题:mmap() 与读取块

答案2

首先,在大多数 IO 操作中,底层存储硬件的特性决定了性能。与正确配置和对齐的系统相比,在使用 S/W RAID 且块大小不匹配和文件系统不对齐的缓慢、内存匮乏的系统上,配置不当的 RAID5 阵列(由 29 个慢速 5400 rpm SATA 磁盘组成)将给您带来较差的性能尽管您可能尝试进行任何软件调整,但高性能控制器上的 SSD RAID 1+0 仍可实现。

但唯一mmap()可以显着加快速度的方法是多次读取相同的数据由于内存压力,您读取的数据不会在读取之间被分页。

内存映射步骤:

  1. 创建虚拟映射的系统调用 - 非常昂贵
  2. 进程第一次访问内存,导致页面错误 - 代价高昂(如果调出,可能需要重复)
  3. 进程实际上读取内存

如果进程只对读取的每一位数据执行步骤 2 和 3 一次,或者由于内存压力而导致数据从内存中删除,则mmap()速度将会变慢。

read()脚步:

  1. 系统调用将数据从磁盘复制到页面缓存(可能会也可能不会页面错误,数据可能已经在页面缓存中,导致跳过此操作)
  2. 从页面缓存复制到进程内存的数据(可能会也可能不会出现页面错误)

由于从页面缓存到进程内存的额外复制,内存映射只会在性能方面胜过这一点。但是,仅仅复制一页内存(或更少)就必须多次完成,才能降低设置映射的成本 - 可能是这样。多少次取决于您的系统。内存带宽,整个系统的使用方式,一切。例如,如果内核内存管理用于设置映射的时间无论如何都不会被任何其他进程使用,那么创建映射的成本实际上并不是很高。相反,如果系统上有大量处理涉及大量虚拟内存映射创建/销毁(即大量短期进程),则内存映射 IO 的影响可能会很大。

然后是read()使用直接IO:

  1. 从磁盘读入进程内存空间的系统调用。 (可能会也可能不会导致页面错误)

直接 IO 读取在性能方面几乎不可能被超越。但您必须真正根据硬件调整 IO 模式才能最大限度地提高性能。

请注意,如果读取数据导致进程用于读取的缓冲区出现页面错误,进程几乎可以控制。

那么,内存映射文件访问速度更快吗?也许是,也许不是。

这取决于您的访问模式。 以及您的硬件和 IO 路径中的其他所有内容。

如果您在具有 4 GB RAM 的计算机上流式传输 30 GB 视频文件,并且您永远不会返回并重新读取任何数据,则内存映射文件可能是最糟糕的的方式来阅读它。

相反,如果您有一个 100 MB 的查找表,用于在处理过程中随机访问数十亿次的某些数据,并且有足够的内存使文件永远不会被调出,那么内存映射将压垮所有其他访问方法。

内存映射文件的一大优势

内存映射文件相对于其他形式的 IO 有一个巨大的优势:代码简单。访问文件就像访问内存中一样简单,这真的很难比拟。大多数时候,内存映射文件和执行离散 IO 操作之间的性能差异并没有那么大。

相关内容