2017-09-13 104 views
2

以另一种方式提出问题,您是否可以确认当您mmap()实际访问已存在于页面缓存中的确切物理页面的文件时?mmap是否直接访问页面缓存或页面缓存的副本?

我问,因为我正在测试一台内存为1TB的192核心机器上,在测试之前预先缓存到页面缓存中的400GB数据文件上(只需删除缓存,然后执行md5sum在文件上)。假设它们都(基本上)返回相同的内存区域(或者可能是相同的内存区域,但以某种方式映射多次),我最初分别有192个线程分别映射文件。因此,我假设两个使用两个不同映射到同一文件的线程都可以直接访问相同的页面。 (让我们忽略NUMA在这个例子中,虽然他们显然是在更高的线程数量显著。)

然而,在实践中,我发现性能会得到可怕在更高线数时,每个线程分别mmapped文件。当我们删除它,而只是做了一个传入线程的mmap(所有线程都直接访问同一个内存区域),那么性能会大大提高

这一切都很棒,但我试图找出原因。如果实际上映射文件只是授予对现有页面缓存的直接访问权限,那么我认为它应该映射多少次无关紧要 - 它应该全部放在同一个地方。

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

你可以评论为什么我看到共享访问同一内存之间的这种不同的性能,而不是映射相同的文件?

谢谢,我感谢您的帮助!

+0

这是一个很好的问题。我不认为我能够回答,但提供一些建议。 1 /为什么不介绍它? perf应该能够很容易地告诉你瓶颈的位置(我希望)。我的猜测是,你打到mmap(小)的开销,但在192线程,它不会扩展。另外,你有没有尝试使用巨大的页面? – Aissen

+0

因为所有有趣的东西都发生在内核的深处,所以配置文件很棘手。就我的应用程序所知,它只是访问内存 - 但是在内存映射,虚拟内存,页面缓存,L3缓存和NUMA节点之间,还有很多移动部件需要注意。这就是说,我同意有更多的工作要做,以解决这个问题,但我希望有一个比我更熟悉内核知识的人可以给出一些建议,至少在*理论中应该发生什么,就像在实践中指导我的测试。 – quinthar

+0

是的,但通常perf会知道内核在哪里花费时间,如果你有正确的符号附加。关于你的问题,我不知道问题的根源是什么。您是否尝试在较小的机器上重现它? – Aissen

回答

1

我想我找到了我的答案,它处理页面目录。答案是肯定的,同一文件的两个mmapped区域将访问相同的基础页面缓存数据。但是,每个映射需要将每个虚拟页面独立映射到物理页面 - 这意味着页面目录中的条目数量是访问相同RAM的数量的两倍。

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

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

(还有转换后备缓冲器,但这是每个CPU,和这么小,我不认为事情在这里多。)

无论如何,这听起来就像重新映射相同的区域只是增加了额外的开销,因为我觉得没有收获。