caching - mmap 是直接访问页面缓存,还是页面缓存的副本?

标签 caching linux-kernel mmap page-caching

换个方式问这个问题,你能否确认当你 mmap() 一个文件时,你实际上访问了页面缓存中已经存在的确切物理页面?

我问这个问题是因为我正在一台具有 1TB RAM 的 192 核机器上测试一个 400GB 的数据文件,该数据文件在测试之前预先缓存到页面缓存中(只需删除缓存,然后在文件)。

最初,我让所有 192 个线程分别对文件进行 mmap,假设它们都会(基本上)返回相同的内存区域(或者可能是相同的内存区域,但以某种方式映射多次)。因此,我假设使用两个不同映射到同一文件的两个线程都可以直接访问相同的页面。 (在这个例子中我们忽略 NUMA,尽管显然它在更高的线程数下很重要。)

但是,在实践中,我发现当每个线程单独映射文件时,线程数较高时性能会变得糟糕。当我们删除它并只执行传递到线程中的单个 mmap(这样所有线程都直接访问相同的内存区域)时,性能将显着提高。

这一切都很棒,但我正在尝试找出原因。如果实际上映射一个文件只是授予对现有页面缓存的直接访问权限,那么我认为映射它多少次并不重要 - 它应该全部到达完全相同的位置。

但考虑到存在这样的性能成本,在我看来,实际上每个 mmap 都是独立且冗余地填充的(可能通过从页面缓存复制,或者可能通过从磁盘再次读取)。

您能否评论一下为什么我在共享访问同一内存与映射同一文件之间看到如此不同的性能?

谢谢,非常感谢您的帮助!

最佳答案

我想我找到了答案,它涉及页面目录。答案是肯定的,同一文件的两个映射区域将访问相同的底层页面缓存数据。然而,每个映射都需要独立地将每个虚拟页映射到物理页——这意味着页目录中的条目数量是访问相同 RAM 的 2 倍。

基本上,每个 mmap() 在虚拟内存中创建一个新范围。该范围内的每一页都对应于物理内存的一页,并且该映射存储在分层页目录中——每 4KB 页有一个条目。因此,一个大区域的每个 mmap() 都会在页目录中生成大量条目。

我的猜测是它实际上并没有预先定义它们,这就是为什么 mmap() 即使对于一个巨大的文件也可以立即调用。但随着时间的推移,它可能必须建立这些条目,因为映射范围上存在错误,这意味着随着时间的推移它会被填充。填充页面目录的额外工作可能就是使用不同 mmap 的线程比共享相同 mmap 的线程慢的原因。我敢打赌,在取消映射范围时,内核需要删除所有这些条目——这就是 unmmap() 如此慢的原因。

(还有翻译后备缓冲区,但这是针对每个 CPU 的,而且很小,我认为这在这里并不重要。)

无论如何,听起来重新映射同一区域只会增加额外的开销,在我看来没有任何收获。

关于caching - mmap 是直接访问页面缓存,还是页面缓存的副本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46190787/

相关文章:

caching - RavenDB:如何防止 RAM 利用率过高?

java - 缓存 KeyStore 的内容并将其转换为 InputStream

reactjs - 如何避免 create-react-app 的缓存

linux-kernel - 如何删除失败的内核模块

.net - ObjectCache 的异步版本?

linux-kernel - 'i2c_get_clientdata"和 "i2c_set_clientdata"有什么用

c - 使用 -save-temps 构建内核 - 这些 9 MB 的 *.i 文件是全部吗?

Python mmap 对象提示 Python 3.5.2 上的字符串模式(不在 Python 2.6.6 中)

c - 如何从文本文件中使用 mmap 读取 double 组

file-io - mmap 寻求行而不是字节偏移量?