2013-12-09 30 views
3

我在读向量为:连续调用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); 
} 
在主

()我有两个连续调用(纯粹作为测试):

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 

一看系统调用指示块被differend内存:

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

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

回答

4

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

所以,在第一次调用可能有:

  1. 分配页面
  2. 映射页面插入进程的地址空间
  3. 从这些网页中的数据复制到您的载体(可能是断层从数据盘,因为它去)

第二个电话可能发现的页面仍然在缓存中,并且只需要:

  1. 映射页面插入进程的地址空间
  2. 从这些页面插入载体复制数据(它们是预故障这段时间,所以这是一个简单的存储操作)

实际上,我跳过了一个步骤:评论中的open/fstat步骤可能是,也可通过inode缓存加速

+0

是的 - linux - 我刚刚添加上面的标签。所以这个过程为第一个呼叫建立页表,而不是第二个呼叫。我松散地将此与创建新的虚拟内存区域联系起来。这是否总是只在这个过程中进行第一次调用,或者是因为调用的相似性而在此处执行了一次? –

+0

进程拥有的页表由每个mmap/munmap调用修改。插入/从表中插入的页面由内核页面缓存拥有,并保留在调用之间。 – Useless

3

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

更多DETAL:

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