在什么情况下使用大页面可以提高速度?

20
现代x86 CPU具有支持比传统的4K更大的页面大小的能力(即2MB或4MB),并且有操作系统工具(LinuxWindows)可以访问此功能。上面提到的Microsoft链接指出,大页面“增加了翻译缓冲区的效率,这可以提高经常访问的内存性能”。但这并不能预测大页面是否会改善任何特定情况。我对使用巨大页面以及在某些情况下结果带来的性能改进感兴趣,最好是有具体的、可量化的例子。有人有成功的案例吗?我知道一个特殊的情况自己:使用巨大页面可以显著地减少运行大型进程所需的时间(可能因为需要复制的TLB记录数量减少了1000倍)。我想知道在不那么奇特的情况下,巨大页面是否也有益处。
5个回答

18
对性能影响最大的情况是,当您对一个大内存区域进行广泛分散的随机访问时,其中“大”意味着远远大于TLB(现代处理器通常具有多个级别)中所有小页面条目所能映射的范围。4KB页面的TLB条目数通常比2MB页面的条目数更多,但这在处理器之间变化很大。二级TLB中可用的“大页面”条目的数量也有很大的差异。
例如,在AMD Opteron Family 10h Revision D(“Istanbul”)系统上:
  • L1 DTLB:4KB页面:48个条目;2MB页面:48个条目;1GB页面:48个条目
  • L2 TLB:4KB页面:512个条目;2MB页面:128个条目;1GB页面:16个条目
  • 而在Intel Xeon 56xx(“Westmere”)系统上:
  • L1 DTLB:4KB页面:64个条目;2MB页面:32个条目
  • L2 TLB:4KB页面:512个条目;2MB页面:无
  • 在使用小页面之前,两者都可以映射2MB(512 * 4KB),而Westmere系统可以使用其32个2MB TLB条目映射64MB,而AMD系统可以使用其L1和L2 TLB中的176个2MB TLB条目映射352MB。对于大于2MB且小于64MB的内存范围进行随机访问时,任一系统都将通过使用大页面获得显着的加速。对于远远大于2MB的内存范围,AMD系统应继续显示出良好的性能。
    在所有情况下,您要避免的是x86_64分层地址转换的最坏情况(注1)。如果地址转换缓存机制(注2)都无法工作,则需要:

    • 对于映射在4kB页面上的数据,需要5次访问内存
    • 对于映射在2MB页面上的数据,需要4次访问内存
    • 对于映射在1GB页面上的数据,需要3次访问内存

    在每种情况下,最后一次访问内存是获取请求的数据,而其他访问是为了获取页面转换信息的各个部分。

    我看到的最好的描述是在AMD的《AMD64体系结构程序员手册第2卷:系统编程》(出版物24593)的第5.3节中。 http://support.amd.com/us/Embedded_TechDocs/24593.pdf

    注1:上述数字实际上并不是最糟糕的情况。在虚拟机下运行会使这些数字变得更糟。在导致包含各级页表的内存被交换到磁盘的环境下运行会使性能变得非常糟糕。

    注2:不幸的是,即使知道这个细节层面也是不够的,因为所有现代处理器都有额外的缓存用于页转换层次结构的上层。就我所知,这些缓存在公共文档中记录非常少。


    11

    我试图编写一些代码,以最大化使用4k页面的TLB交换,以检查从大页面中可能获得的收益。当libhugetlbfs的malloc提供2M字节页面时,下面的内容运行快2.6倍(比4K页面快)(Intel i7,64位Debian Lenny);希望scoped_timerrandom0n的作用很明显。

      volatile char force_result;
    
      const size_t mb=512;
      const size_t stride=4096;
      std::vector<char> src(mb<<20,0xff);
      std::vector<size_t> idx;
      for (size_t i=0;i<src.size();i+=stride) idx.push_back(i);
      random0n r0n(/*seed=*/23);
      std::random_shuffle(idx.begin(),idx.end(),r0n);
    
      {
        scoped_timer t
          ("TLB thrash random",mb/static_cast<float>(stride),"MegaAccess");
        char hash=0;
        for (size_t i=0;i<idx.size();++i) 
          hash=(hash^src[idx[i]]);
        force_result=hash;
      }
    

    一种更简单的“直线”版本只使用 hash=hash^src[i] 在大页面上仅获得了16%的性能提升,但(纯属猜测)Intel的高级预取硬件可能在访问可预测时有助于4K情况(我想我可以禁用预取以调查是否如此)。


    4
    硬件预取不会跨越4k页面边界,但在直线情况下,你可能看到的是页表访问非常可预测,因此当TLB未命中时发生的页行走很可能会命中全部在L1中的页面(这些页面条目可能确实是通过预取带入的)。 - BeeOnRope

    3

    我看到了在一些HPC/Grid场景下的改善 - 特别是在具有大量RAM的机器上运行非常大的物理模型的情况下。此外,运行该模型的进程是该机器上唯一活动的程序。我怀疑,并没有进行过测量,某些DB函数(例如批量导入)也会受益。

    个人认为,除非您拥有非常好的分析/理解内存访问配置文件并且它会进行大量的大内存访问,否则您不太可能看到任何显著的改进。


    3
    这个话题有些深奥,但是在Intel Xeon Phi(MIC)体系结构中使用Huge TLB页面进行DMA内存传输(通过PCIe从主机到Phi)可以产生显着的差异。这个Intel链接描述了如何启用巨大页面。我发现,对于正常的TLB页面大小(4K),将DMA传输大小增加到8 MB以上开始降低性能,一旦传输大小达到512 MB,性能会下降到不到1 GB/s。

    启用巨大的TLB页面(2MB)后,数据传输速率继续增加,对于512 MB的DMA传输速度可达5 GB/s以上。


    2

    在拥有大量内存(≥64GB)运行大型进程的服务器上,我可以获得约5%的加速。例如,对于一个16GB的Java进程,它有4M x 4kB页面,但只有4k x 4MB页面。


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