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

26
换一种方式问,您能否确认当您使用mmap()映射文件时,您实际上可以访问已经存在于页面缓存中的确切物理页面?
我询问这个问题是因为我正在对拥有1TB RAM的192核心机器上的400GB数据文件进行测试,该文件在测试之前被预先缓存在页面缓存中(通过清除缓存,然后对文件执行md5sum)。
最初,我让所有192个线程分别映射文件,假设它们都会获得(基本上)相同的内存区域(或者可能是相同的内存区域,但以某种方式多次映射)。因此,我假设两个使用不同映射的线程都可以直接访问相同的页面。(在这个例子中,让我们忽略NUMA,尽管显然在更高的线程数下它很重要。)
然而,在实践中,当每个线程单独对文件进行mmap时,性能会在更高的线程数下变得非常糟糕。当我们移除它,并改为只进行一次映射并将其传递到线程中时(以使所有线程直接访问相同的内存区域),性能得到了极大的改善。
这很好,但我试图弄清楚原因。如果事实上映射文件只是授予对现有页面缓存的直接访问权,则我认为映射多少次都无所谓,因为它们都应该进入完全相同的位置。
但是,考虑到存在这样的性能成本,我认为实际上每个映射都是独立且冗余地填充的(可能通过从页面缓存中复制,或者可能通过重新从磁盘读取)。
您能否评论一下为什么在共享对相同内存的访问与映射相同文件之间会看到如此不同的性能?
谢谢,感谢您的帮助!

这是一个很好的问题。我不认为我能回答,但可以提供一些建议。1/ 为什么不对其进行分析?perf应该能够很容易地告诉您瓶颈在哪里(我希望如此)。我猜测您正在遇到mmap(小)开销,但在192个线程时,它无法扩展。另外,您尝试使用过Huge Pages吗? - Aissen
很难对其进行分析,因为所有有趣的东西都发生在内核深处。 就我的应用程序而言,它只是访问RAM - 但是在内存映射,虚拟内存,页面缓存,L3缓存和NUMA节点之间,有很多需要确定的移动部件。 话虽如此,我同意还有更多工作要做才能弄清楚这一点,但我希望比我更了解内核的人可以就理论上应该发生什么给出一些建议,因为这将指导我的实际测试。 - quinthar
是的,但通常情况下,如果您附加了正确的符号,perf会知道内核在哪里花费时间。关于您的问题,我不知道问题的根源在哪里。您是否尝试在较小的机器上重现它? - Aissen
1个回答

9
我认为我找到了答案,并且与页面目录有关。答案是:是的,同一文件的两个mmapped区域将访问相同的底层页面缓存数据。然而,每个映射需要独立地将每个虚拟页面映射到物理页面 - 这意味着需要2倍的页面目录条目才能访问相同的内存。

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

我猜它实际上并没有预先定义所有这些条目,这就是为什么即使对于巨大的文件,调用mmap()也是瞬间完成的原因。但随着在mmapped范围上出现故障,它可能需要建立这些条目,这意味着随着时间的推移,它会被填满。填充页面目录的这个额外工作可能是不同mmaps的线程比共享同一mmap的线程要慢的原因。我敢打赌内核需要在取消映射范围时擦除所有这些条目 - 这就是为什么unmmap()如此缓慢的原因。

(还有翻译后备缓冲区,但它是每个CPU的,所以我认为在这里没有太大的意义。)

总之,重新映射相同区域只会增加额外的开销,而对我来说没有任何好处。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接