mmap与使用new分配的内存有什么区别?

3
我有一个BitVector类,可以使用new动态分配内存,也可以映射文件。对于小文件,使用它没有明显的性能差异,但是在使用16GB文件时,发现mmap文件比使用new分配的内存慢得多(大约慢了10倍或更多)。请注意,我的机器有64GB的RAM。
涉及的代码正在从大型磁盘文件加载值,并将它们放入Bloom过滤器中,该过滤器使用我的BitVector类进行存储。
起初,我认为这可能是因为mmap文件的后备存储与我要加载的文件在同一磁盘上,但似乎并不是这个问题。我将两个文件放在两个物理不同的磁盘上,性能没有改变。(虽然我相信它们在同一个控制器上。)
然后,我使用来尝试将所有内容都强制存入RAM,但mmap实现仍然非常慢。
因此,目前我只是直接分配内存。这次比较中我所做的唯一更改就是标志BitVector构造函数。
请注意,为了测量性能,我既在查看top命令,也在观察每秒可以向Bloom过滤器中添加多少个状态。当使用时,CPU使用率甚至不在top命令中注册,尽管开始移动(我正在运行Ubuntu服务器),这看起来是处理驱动程序的日志记录的进程。输入和输出文件存储在两个HDD上。
有人能解释这种巨大的性能差异吗?
谢谢!

据我所知,mmap 用于读取大页面的默认策略并不是很好。您能否在设置 MAP_POPULATE 标志并使用 madvise 时重新运行您的案例,同时使用 MADV_SEQUENTIAL 标志(如果您的访问是顺序的)或 MADV_RANDOM 标志(如果是随机访问),并与 MADV_WILLNEED 标志进行 OR 运算(以防止页面重新加载)。 - Arunmu
这确实是一个意外的行为,特别是因为 operator new 使用 malloc,而 malloc 对于大请求使用 mmap!当您说“使用一个16GB文件”时,这表明您正在使用一个 真实 的文件,而不是来自 /dev/shm 的文件?如果是这种情况,尤其是因为您注意到日志记录过程正在增加,减速可能是由于磁盘访问造成的预读和错误故障(虽然我不知道空零页面应该在磁盘上执行什么操作)。 - Damon
@Damon - 我正在使用一个真实的文件,而不是临时文件。我是随机访问它的,尽管如果它被锁定在内存中,似乎这并不重要。 - Nathan S.
@ArunMu - 好的建议。机器现在很忙,但我明天会尝试一下。 - Nathan S.
请注意,/dev/shm并不是真正的临时文件,而是一种向操作系统请求“给我虚拟内存”的方式,因此如果您想要分配内存(而不是读取实际文件),那么这更接近您想要的内容。页面错误应该复制零页,并且不需要锁定。 - Damon
显示剩余3条评论
1个回答

3
首先,mmap是提供访问系统虚拟内存的系统调用或接口。
现在,在Linux中(我希望你正在使用*nix),通过惰性加载或更常见的称为写时复制来实现大量性能改进。

对于mmap,也实现了这种惰性加载。

当您调用将文件映射到内存映射区时,内核不会立即为文件分配主内存页。相反,它等待程序从虚拟页面中读取/写入,在此阶段会发生页面故障,然后相应的中断处理程序将实际加载可以保存在该页框中的特定文件部分(还更新了页表,因此下次您读取/写入同一页时,它指向有效的框架)。

现在,您可以使用,,标志与mmap等控制此行为。
使用mmap的标志告诉内核映射文件到内存页,而不是每次访问新页时都引起页面错误。因此,在文件加载完成之前,函数将被阻止。

从Man页(手册页):
MAP_POPULATE(自Linux 2.5.46以来)
填充(预读)映射的页面表。对于文件映射,这会导致文件预读取。后续访问映射将不会受到页面错误的阻塞。
自Linux 2.6.23以来,仅支持MAP_POPULATE的私有映射。

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