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

Why mmap() (Memory Mapped File) is faster than read()

最近在研究Java NIO的MappedByteBuffer。 我读过一些关于它的 posts,他们都提到 "mmap() is faster than read()"

我的结论是:

  1. 我对待MappedByteBuffer == Memory Mapped File == mmap()

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

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

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

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

有人可以回答我的问题吗?谢谢:)

你在比较苹果和橘子。 mmap() 是 'faster than read()' 因为它不做任何 I/O。当您访问映射产生的内存地址时, I/O 被推迟到。 That I/O 与 read(), 非常相似,that I/O 是否比 [=11 快=] 是一个很有争议的问题。在我接受之前,我希望看到一个合适的基准。

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

好的。

read() has to read data through : disk file -> kernel -> application, so it has twice context switch and buffer copying

与什么相比?

They all said mmap() has less copying or syscall than read(),

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

but as I know it also need to read from disk file the first time you access the file data.

正确。

So the first time it read : virtual address -> memory -> page fault -> disk file -> kernel -> memory. Except you can access it randomly, the last 3 steps (disk file -> kernel -> memory) is exactly the same as read(), so how mmap() could be less copying or syscall than read() ?

因为 DMA,如果实现的话。

what's the relationship between mmap() and swap file

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

Is that the os will put the least used file data of memory into swap (LRU)?

没有

So when the second time you access these data, OS retrieves them from swap but not disk file(no need to copy to kernel buffer), that's why mmap() has less copying and syscall?

没有。这样做就大错特错了。

In java, MappedByteBuffer is allocated out of heap (it's a direct buffer).

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

So when you read from MappedByteBuffer, does it mean it need one more extra memory copy from outside the java heap into java heap?

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

1:是的,这就是 MappedByteBuffer 的本质。

2:"disk file -> kernel"不一定涉及复制

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

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

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