连续调用mmap,任何缓存?

时间:2013-12-09 16:32:56

标签: c++ linux memory mmap

我在矢量中读到:

int readBytes(string filename, vector<uint32_t> &v)
{
    // fstat file, get filesize, etc.

    uint32_t *filebuf = (uint32_t*)mmap(0,filesize,PROT_READ,
                                        MAP_FILE|MAP_PRIVATE,
                                        fhand,0);
    v = std::vector<uint32_t>(filebuf,filebuf+numrecords);
    munmap(filebuf, filesize);
}
在main()中我有两个连续的调用(纯粹作为测试):

vector<uint32_t> v(10000);    
readBytes(filename, v);
readBytes(filename, v);
// ...

第二次通话几乎总能提供更快的时钟时间:

Profile time [1st call]: 0.000214141 sec
Profile time [2nd call]: 0.000094109 sec

查看系统调用表明内存块是不同的:

mmap(NULL, 40000, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe843ac8000
mmap(NULL, 40000, PROT_READ, MAP_PRIVATE, 4, 0) = 0x7fe843ac7000

为什么第二次通话更快?巧合?什么,如果有的话,缓存?

2 个答案:

答案 0 :(得分:4)

假设你在谈论某些事情* NIX-ish,可能有一个页面缓存,其工作正是缓存这种数据以获得这种加速。除非在从缓存中逐出这些页面的调用之间出现其他内容,否则它们仍将存在。

因此,第一个电话可能需要:

  1. 分配页面
  2. 将页面映射到您的进程地址空间
  3. 将这些页面中的数据复制到您的向量中(可能会导致数据从磁盘中出错)
  4. 第二次调用可能会发现页面仍在缓存中,只需要:

    1. 将页面映射到您的进程地址空间
    2. 将这些页面中的数据复制到您的向量中(这次它们已经预先出现故障,所以这是一个简单的内存操作)

    3. 事实上,我已经跳过了一个步骤:评论中的open / fstat步骤可能通过inode缓存加速。

答案 1 :(得分:3)

请记住,您的程序会看到虚拟内存。有一个映射表(“页表”),它将程序看到的虚拟地址映射到真实的物理内存。操作系统将确保两个mmap()调用将程序看到的两个不同的虚拟地址映射到同一个物理内存。因此,只需从磁盘加载一次数据。

更多信息:

  • 首先是mmap():操作系统只记录映射
  • 当您实际尝试读取数据时:发生“页面错误”,因为数据不在内存中。操作系统捕获,将数据从磁盘读取到其磁盘缓存,并更新页表,以便程序可以直接从该磁盘缓存读取,然后自动恢复程序。
  • 第一个munmap():操作系统禁用映射,并更新页面表,因此您无法再读取该文件。请注意,该文件仍在操作系统的磁盘缓存中。
  • 第二个mmap():操作系统只记录映射
  • 当您实际尝试读取数据时:发生“页面错误”,因为数据未映射。操作系统发现,通知数据已经在其磁盘缓存中,并更新页表,以便程序可以直接从该磁盘缓存中读取,然后自动恢复程序。
  • 第二个munmap():操作系统禁用映射,并更新页面表,因此您无法再读取该文件。请注意,该文件仍在操作系统的磁盘缓存中。