qui*_*har 5 caching mmap linux-kernel page-caching
以另一种方式提出问题,您是否可以确认当您执行mmap()文件时实际上访问的是页面高速缓存中已经存在的确切物理页面?
我问是因为我正在测试的192核心计算机上具有1TB的RAM,在测试之前预先缓存到页面缓存中的400GB数据文件上(通过仅删除缓存,然后对文件进行md5sum) 。
最初,我假设所有192个线程分别单独映射文件,假设它们都(基本上)返回相同的内存区域(或也许是相同的内存区域,但以某种方式多次映射)。因此,我假设使用两个不同映射到同一文件的两个线程都可以直接访问相同的页面。(在此示例中,我们忽略NUMA,尽管显然在线程数较高时,NUMA很重要。)
但是,实际上,我发现当每个线程分别映射文件时,如果线程数量更多,性能将变得很糟糕。当我们删除它,取而代之的是将一个mmap传递到线程中(这样,所有线程都直接访问同一内存区域),则性能得到了显着提高。
太好了,但是我想找出原因。如果实际上映射文件只是授予对现有页面高速缓存的直接访问权限,那么我认为映射它多少次无关紧要-它都应该放在完全相同的位置。
但是考虑到性能成本如此之高,在我看来,实际上每个mmap都是独立且冗余地填充的(可能是通过从页面缓存中复制,或者可能是从磁盘中再次读取)。
您能否评论一下为什么我看到在共享访问同一内存与映射同一文件之间存在如此不同的性能?
谢谢,谢谢您的帮助!
我想我找到了答案,它涉及页面目录。答案是肯定的,同一个文件的两个映射区域将访问相同的底层页面缓存数据。但是,每个映射都需要独立地将每个虚拟页映射到物理页——这意味着要访问相同 RAM 的页目录中的条目数量是原来的 2 倍。
基本上,每个 mmap() 在虚拟内存中创建一个新范围。该范围的每一页对应于物理内存的一页,并且该映射存储在分层页目录中——每 4KB 页有一个条目。因此,大区域的每个 mmap() 都会在页目录中生成大量条目。
我的猜测是它实际上并没有预先定义它们,这就是为什么 mmap() 即使对于一个巨大的文件也可以立即调用。但随着时间的推移,它可能必须建立这些条目,因为 mmapped 范围存在错误,这意味着随着时间的推移它会被填充。填充页面目录的额外工作可能是使用不同 mmap 的线程比共享相同 mmap 的线程慢的原因。而且我敢打赌,内核在取消映射范围时需要擦除所有这些条目——这就是 unmmap() 如此缓慢的原因。
(还有翻译后备缓冲区,但这是每个 CPU 的,而且太小了,我认为这在这里并不重要。)
无论如何,听起来重新映射同一区域只会增加额外的开销,在我看来似乎没有任何好处。