我有一个BitVector
类,可以使用new
动态分配内存,也可以mmap
一个文件。使用小文件时,性能没有明显差异,但使用16GB文件时,我发现mmap文件比用new
分配的内存慢得多。 (比较慢10倍或更多。)请注意我的机器有64GB的RAM。
有问题的代码是从大型磁盘文件加载值并将它们放入使用我的BitVector
类进行存储的Bloom过滤器中。
起初我认为这可能是因为mmap文件的支持与我加载的文件位于同一磁盘上,但这似乎不是问题。我将这两个文件放在两个物理上不同的磁盘上,并且性能没有变化。 (虽然我相信他们在同一个控制器上。)
然后,我使用mlock
尝试强制所有内容进入RAM,但mmap实现仍然非常慢。
所以,暂时我只是直接分配内存。我在这个比较的代码中唯一改变的是BitVector
构造函数的标记。
请注意,为了衡量效果,我要查看top
并观察每秒可以添加到布隆过滤器的状态数。使用top
时,CPU使用率甚至都没有在mmap
上注册 - 尽管jbd2/sda1-8
开始向上移动(我在Ubuntu服务器上运行),这看起来是一个进程正在处理驱动器的日志。输入和输出文件存储在两个HDD上。
任何人都可以解释这种巨大的性能差异吗?
谢谢!
答案 0 :(得分:3)
首先,mmap
是用于访问系统虚拟内存的系统调用或接口。
现在,在linux中(我希望您正在使用* nix),通过延迟加载或更常见的 Copy-On-Write 的。
对于mmap,也实现了这种延迟加载。
当你在文件上调用mmap时,内核不会立即为要映射的文件分配主内存页面。实际上,它等待程序从幻觉页面写入/读取,在哪个阶段,发生 页面错误 ,然后相应的中断处理程序将实际加载该页面框架中可以保存的特定文件部分(页面表也会更新,所以下次,当您正在读/写同一页面时,它指向一个有效的帧)。
现在,您可以使用mlock
,madvise
,MAP_POPULATE
标记mmap等来控制此行为。
带有mmap的MAP_POPULATE
标志告诉内核在调用返回之前将文件映射到内存页面而不是每次访问新页面时都会出现页面错误。因此,在文件加载之前,该函数将被阻止。
来自Man Page:
MAP_POPULATE (since Linux 2.5.46) Populate (prefault) page tables for a mapping. For a file mapping, this causes read-ahead on the file. Later accesses to the mapping will not be blocked by page faults. MAP_POPULATE is supported for private mappings only since Linux 2.6.23.