为什么mmap()(内存映射文件)比read()更快

时间:2015-03-18 03:36:24

标签: java c linux io operating-system

我最近正在研究Java NIO的MappedByteBuffer。 我已经阅读了一些关于它的帖子,所有人都提到“mmap()比read()更快”(

在我的结论中:

  1. 我对待MappedByteBuffer ==内存映射文件== mmap()

  2. read()必须通过以下方式读取数据:disk file - >内核 - >应用程序,因此它具有上下文切换和缓冲区复制

  3. 他们都说mmap()比read()拥有更少的复制或系统调用,但据我所知,它还需要在您第一次访问文件数据时从磁盘文件中读取。所以它第一次阅读:虚拟地址 - >记忆 - >页面错误 - >磁盘文件 - >内核 - >记忆。除了你可以随机访问它,最后3个步骤(磁盘文件 - >内核 - >内存)与read()完全相同,那么mmap()如何比read()更少复制或系统调用?

  4. mmap()和交换文件之间的关系是什么,os是否会将最少使用的内存文件数据放入交换(LRU)?因此,当您第二次访问这些数据时,操作系统会从交换而不是磁盘文件中检索它们(无需复制到内核缓冲区),这就是为什么mmap()复制和系统调用较少的原因?

  5. 在java中,MappedByteBuffer是从堆中分配的(它是直接缓冲区)。因此,当您从MappedByteBuffer中读取时,是否意味着需要从Java堆外部再添加一个额外的内存副本到Java堆中?

  6. 有人能回答我的问题吗?谢谢:))

2 个答案:

答案 0 :(得分:6)

1:是的,这基本上就是MappedByteBuffer。

2:“磁盘文件 - >内核”不一定涉及复制。

3:使用内存映射文件,一旦内核将文件读入其缓存,它就可以简单地将缓存的那部分映射到您的进程中 - 而不必将数据从缓存复制到您的位置进程指定。

4:如果内核决定从内存映射文件换出页面,它将不会将页面写入页面文件;在丢弃页面之前,它会将页面写入原始文件(它映射的文件)。将其写入页面文件是不必要的,浪费页面文件空间。

5:是的。例如,如果您调用get(byte[]),则数据将从堆外映射复制到您的阵列中。请注意,get(byte[])等函数需要复制任何类型缓冲区的数据 - 这不是特定于内存映射文件的。

答案 1 :(得分:1)

你在比较苹果和橘子。 mmap()read()'快,因为它不执行任何I / O.当您访问由映射产生的内存地址时,I / O将延迟到。 那个 I / O与read(),非常相似, I / O是否比read()更快是一个非常有用的话题。在我接受之前,我希望看到一个合适的基准。

  

我会对待MappedByteBuffer ==内存映射文件== mmap()

行。

  

read()必须通过以下方式读取数据:磁盘文件 - >内核 - >应用程序,因此它有两次上下文切换和缓冲区复制

与什么相比?

  

他们都说mmap()比read(),

拥有更少的复制或系统调用

系统调用较少。它是否具有较少的复制取决于实现。当然可以通过DMA直接读取和写入数据,但是特定的操作系统是否是特定于操作系统的。

  

但据我所知,它还需要在您第一次访问文件数据时从磁盘文件中读取。

正确。

  

所以它第一次阅读:虚拟地址 - >记忆 - >页面错误 - >磁盘文件 - >内核 - >记忆。除了你可以随机访问它,最后3个步骤(磁盘文件 - >内核 - >内存)与read()完全相同,那么mmap()如何比read()更少复制或系统调用?

由于DMA,如果已实施。

  

mmap()和交换文件

之间的关系是什么

分配给地图的内存是进程地址空间的一部分,它是虚拟的,并且需要交换,并且交换文件中必须有空间,就像任何其他内存一样。

  

os是否会将最少使用的内存文件数据放入交换(LRU)?

没有

  

因此,当您第二次访问这些数据时,操作系统会从交换而不是磁盘文件中检索它们(无需复制到内核缓冲区),这就是为什么mmap()复制和系统调用较少的原因?

没有。做所有这些都是错误的。

  

在java中,MappedByteBuffer是从堆中分配的(它是直接缓冲区)。

这没有意义。直接缓冲区不会从堆中分配,它们由mmap()或平台API分配,作为 new 内存。不在堆中。你是正确的MappedByteBuffer是直接缓冲区。

  

因此,当您从MappedByteBuffer中读取时,是否意味着需要从Java堆外部再添加一个额外的内存副本到Java堆中?

是的,但不是出于上述原因。原因是你必须致电MappedByteBuffer.get()/put(),,这本身就是一个额外的步骤。