操作系统概念说
考虑使用标准系统调用 open()、read() 和 write()顺序读取磁盘上的文件。每个文件访问都需要系统调用和磁盘访问。
或者,我们可以使用目前讨论的虚拟内存技术将文件 I/O 视为例行内存访问。这种方法称为 内存映射文件,允许虚拟地址空间的一部分与文件进行逻辑关联。正如我们将看到的,这可以显着提高性能. 内存映射文件是通过将磁盘块映射到内存中的一个(或多个页面)来完成的。对文件的初始访问通过普通的请求分页进行,从而导致页面错误。但是,文件的页大小部分从文件系统读取到物理页中(某些系统可能选择一次读取超过页大小的内存块)。对文件的后续读取和写入将作为例行内存访问处理。通过内存操作文件而不是产生使用 read() 和 write() 系统调用的开销,可以简化并加快文件访问和使用。
你能分析一下内存映射文件的性能吗?
如果我是正确的,内存映射文件的工作方式如下。创建内存映射需要系统调用。然后当它访问映射的内存时,就会发生页面错误。页面错误也有开销。
与标准 I/O 系统调用相比,内存映射文件如何显着提高性能?
内存映射文件直接避免了复制缓冲区,这发生在
read()
和write()
调用中。调用read()
并write()
包含指向存储数据的进程地址空间中的缓冲区的指针。内核必须将数据复制到/从这些位置。使用mmap()
将文件映射到进程的地址空间,因此进程可以直接寻址文件而无需副本。如果文件在初始时加载到内存,则在初始调用之后访问内存映射文件时也没有系统调用开销
mmap()
。如果映射文件的某个页面不在内存中,访问将产生错误并要求内核将该页面加载到内存中。读取大块read()
可以比mmap()
在这种情况下更快,如果mmap()
会产生大量错误来读取文件。(可以提前通知内核,madvise()
以便内核可以在访问之前提前加载页面)。有关更多详细信息,请参阅 Stack Overflow 上的相关问题:mmap() vs. reading blocks
首先,在大多数 IO 操作中,底层存储硬件的特性支配着性能。与正确配置和对齐的相比,使用 S/W RAID、块大小不匹配和文件系统未对齐的慢速、内存匮乏的系统上的 29 个 SLOW 5400 rpm SATA 磁盘配置不良的 RAID5 阵列会给您带来较差的性能尽管您可能会尝试任何软件调整,但高性能控制器上的 SSD RAID 1+0。
但是唯一
mmap()
可以显着加快速度的方法是,如果您多次读取相同的数据,并且由于内存压力,您读取的数据在两次读取之间不会被分页。内存映射步骤:
如果进程只对读取的每一位数据执行第 2 步和第 3 步一次,或者由于内存压力而从内存中删除数据,
mmap()
则会变慢。read()
脚步:由于从页面缓存到进程内存的额外副本,内存映射只会在性能方面胜过这一点。但是仅仅复制一页内存(或更少)就必须多次完成,以节省设置映射的成本——可能。多少次取决于您的系统。内存带宽,整个系统的使用方式,一切。例如,如果内核的内存管理用于设置映射的时间无论如何都不会被任何其他进程使用,那么创建映射的成本确实不会很高。相反,如果您的系统上有大量处理涉及大量虚拟内存映射创建/销毁(即大量短期进程),则内存映射 IO 的影响可能会很大。
然后是
read()
使用直接 IO:直接 IO 读取几乎不可能在性能方面超越。但是您必须真正调整您的 IO 模式以适应您的硬件以最大限度地提高性能。
请注意,如果读取数据会导致进程用于读取的缓冲区出现页面错误,进程几乎可以控制。
那么,内存映射文件访问是否更快?也许是,也许不是。
这取决于您的访问模式。 连同您的硬件和 IO 路径中的所有其他内容。
如果您在具有 4 GB RAM 的机器上流式传输 30 GB 视频文件,并且您永远不会返回并重新读取任何数据,那么内存映射文件可能是读取它的最糟糕的方式。
相反,如果您有一个 100 MB 的查找表来查找您在处理过程中随机访问数十亿次的数据,并且有足够的内存使文件永远不会被分页,那么内存映射将粉碎所有其他访问方法。
内存映射文件的一大优势
内存映射文件比其他形式的 IO 有一个巨大的优势:代码简单。像访问内存中一样访问文件的简单性真的很难超越。大多数时候,内存映射文件和执行离散 IO 操作之间的性能差异并没有那么大。