为什么 mmap() (Memory Mapped File) 比 read() 快

2022-09-04 21:37:38

我最近在研究Java NIO的MappedByteBuffer。我读过一些关于它的帖子,他们都提到“mmap()比read()快”

结论:

  1. I treat MappedByteBuffer == Memory Mapped File == mmap()

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

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

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

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

任何人都可以回答我的问题吗?谢谢:)


答案 1

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

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

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

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

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


答案 2

你正在比较苹果和橙子。 比“快”,因为它不执行任何 I/O。I/O 将延迟到您访问映射生成的内存地址时。I/ O 与 I/O 是否比一个非常有争议的问题大致相同。在我接受之前,我希望看到一个适当的基准。mmap()read()read(),read()

我处理 == 内存映射文件 ==MappedByteBuffermmap()

还行。

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

与什么相比?

他们都说mmap()的复制或系统调用比read(),

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

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

正确。

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

由于 DMA,如果实施。

mmap() 和交换文件之间有什么关系

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

操作系统是否会将内存中最少使用的文件数据放入交换(LRU)中?

哈哈

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

哈哈做这一切将是完全错误的。

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

这是没有道理的。直接缓冲区不是从堆中分配出来的,而是由平台 API 分配的,或者由平台 API 分配的,或者作为内存。不在堆中。您是对的,a 是直接缓冲区。mmap()MappedByteBuffer

因此,当您从MappedByteBuffer读取时,这是否意味着它需要从java堆外部再有一个额外的内存复制到java堆中?

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


推荐