是否有可能通过 mmap 匿名内存“打孔”?

2024-05-10

考虑一个使用大量大致页面大小的内存区域(例如 64 kB 左右)的程序,每个内存区域的寿命都相当短暂。 (在我的特定情况下,这些是绿色线程的替代堆栈。)

如何最好地分配这些区域,以便一旦该区域不再使用,它​​们的页面可以返回到内核?天真的解决方案显然是简单地mmap每个区域单独,以及munmap当我处理完它们后,我会立即再次处理它们。不过,我觉得这是一个坏主意,因为这样的人太多了。我怀疑 VMM 一段时间后可能会开始严重扩展;但即使没有,我仍然对理论案例感兴趣。

如果我只是mmap我自己有一个巨大的匿名映射,我可以根据需要从中分配区域,有没有办法通过该映射为我已经完成的区域“打孔”?就像madvise(MADV_DONTNEED),但不同之处在于这些页面应该被视为已删除,这样内核实际上不需要将其内容保留在任何地方,而只需在它们再次出现故障时重用归零的页面即可。

我使用的是 Linux,在这种情况下,我不会因为使用特定于 Linux 的调用而烦恼。


我在某个时候对这个主题做了很多研究(为了不同的用途)。就我而言,我需要一个人口非常稀疏的大型哈希图+能够时不时将其归零的能力。

mmap解决方案:

最简单的解决方案(便携式,madvise(MADV_DONTNEED)是 Linux 特定的)将这样的映射归零是mmap其上方有一个新的映射。

 void * mapping = mmap(MAP_ANONYMOUS);
 // use the mapping

 // zero certain pages
 mmap(mapping +  page_aligned_offset, length, MAP_FIXED | MAP_ANONYMOUS);

最后一次调用在性能方面相当于后续调用munmap/mmap/MAP_FIXED,但是线程安全。

从性能角度来看,该解决方案的问题在于,在发出中断和上下文更改的后续写入访问中,页面必须再次出现故障。仅当一开始出现故障的页面很少时,此方法才有效。

memset解决方案:

在经历了如此糟糕的性能之后,如果必须取消映射大部分映射,我决定手动将内存清零memset。如果大约超过 70% 的页面已经出现故障(如果不是,则它们是在第一轮检查之后)memset)那么这比重新映射这些页面更快。

mincore解决方案:

我的下一个想法实际上只是memset在那些之前出现过问题的页面上。这个解决方案是不是线程安全的。呼唤mincore确定页面是否有故障,然后有选择地memset将它们归零是一个显着的性能改进,直到超过 50% 的映射出现故障,此时memset整个映射变得更简单(mincore是一个系统调用,需要一次上下文更改)。

核心表解决方案:

我随后采取的最后一种方法是拥有自己的核心表(每页一位),该表表示自上次擦除以来是否已使用过该表。这是迄今为止最有效的方法,因为您实际上只会将实际使用的每一轮中的页面清零。显然它也不是线程安全的,并且需要您跟踪用户空间中已写入哪些页面,但如果您需要这种性能,那么这是迄今为止最有效的方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

是否有可能通过 mmap 匿名内存“打孔”? 的相关文章

随机推荐