Why'sTheDesign(Why'sTHEDesign)是计算机领域编程决策的系列文章。在本系列的每篇文章中,我们都会提出一个特定的问题,并从不同的角度讨论这种设计的优缺点及其对具体实现的影响。内存是计算机的重要资源。虽然现在大部分服务对内存的需求都没有这么高,但是数据库、Hadoop全家桶等服务都会消耗大量的内存。它们在生产环境中经常占用GB和TB的内存。为了提高计算速度,Linux操作系统引入了许多策略来更好更快地管理这些内存并减少开销。今天要介绍的是HugePages,即大页面[^1]。大多数CPU架构都支持更大的页面,但不同的操作系统使用不同的术语,例如:Linux上的HugePages、BSD上的SuperPages和Windows上的LargePages。这些不同的术语都代表相似的大页面。页面功能。图1-CPU架构和更大的页面我们都知道Linux以页面为单位管理内存,默认页面大小为4KB,虽然有些处理器使用8KB、16KB和64KB作为默认页面大小,但4KB仍然是主流操作系统的默认页面配置[^2]。虽然64KB的页面是4KB的16倍,但是和最小的2MBHugePages相比,64KB的页面确实不够大,更何况是默认的4KB:图2-默认和大页面大小2MB一般是HugePages的默认大小,甚至在arm64和x86_64架构上都支持1GB大页面,是Linux默认页面大小的262,144倍。Wecanusethefollowingcommandtoviewthecurrent机器上HugePages的相关信息:$cat/proc/meminfo|grepHugeAnonHugePages:71680kBShmemHugePages:0kBFileHugePages:0kBHugePages_Total:0HugePages_Free:0HugePages_Rsvd:0HugePages_Surp:0Hugepagesize:2048kBHugetlb:0kB通过上面的输出结果,我们可以看到当前机器上的主页默认大小为2MB,大页数也为0,即没有进程在申请或使用大页。读者可以尝试在Linux上执行上述命令。如果机器上没有额外的配置,使用上面的命令得到的输出和这里不会有太大区别。/proc/sys/vm/nr_hugepages中存储的数据是hugepages的个数。虽然它的值默认为0,但我们可以通过改变文件内容在操作系统中申请或释放大页面:$echo1>/proc/sys/vm/nr_hugepages$cat/proc/meminfo|grepHugePages_HugePages_Total:1HugePages_Free:1...在Linux中,和其他内存申请和释放方式一样,我们可以在mmap系统调用中通过MAP_HUGETLB标记操作系统的大页面,使用munmap来释放内存[^3]。使用如下代码片段在操作系统中申请2MB大页面:size_ts=(2UL*1024*1024);char*m=mmap(NULL,s,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB/*flags*/,-1,0);munmap(m,s);HugePages的使用方式虽然类似于默认内存,但它实际上是作为操作系统管理的一种特殊资源,Linux会在/proc/meminfo中单独显示HugePages相关的数据,而容器编排系统Kubernetes也会考虑huge页面作为不同于内存的独立资源。下图的Pod也需要单独申请Hugepageresource[^4]:apiVersion:v1kind:Podmetadata:name:huge-pages-examplespec:containers:-name:example...volumeMounts:-mountPath:/hugepages-2Miname:hugepage-2mi-mountPath:/hugepages-1Giname:hugepage-1giresources:limits:hugepages-2Mi:100Mihugepages-1Gi:2Gimemory:100Mirequests:memory:100Mivolumes:-name:hugepage-2miemptyDir:medium:HugePages-2Mi:hugepage-1giempty目录:中:胡gePages-1Gi是Linux从2.6.32开始引入的新特性。HugePages可以提升数据库、Hadoop全家桶等占用大量内存的服务的性能。此功能对常见的Web服务和后端服务没有帮助。相反,它可能会影响服务的性能。在这篇文章中,我们将介绍为什么HugePages可以提高数据库等服务的性能:HugePages可以减少内存页的管理开销;HugePages可以锁定内存,禁止操作系统的内存交换和释放;虽然管理开销启用HugePages大多需要开发或运维工程师额外配置,但是在应用中启用HugePages可以在以下几个方面减少内存页的管理开销:较大的内存页可以减少内存中页表的层级,从而不仅可以减少页表的内存占用,还可以减少虚拟内存到物理内存转换的性能损失;更大的内存页意味着更高的缓存命中率,CPU更有可能直接在TLB(Translationlookasidebuffer)中获取对应的物理地址;更大的内存页可以减少获取大内存的次数,使用HugePages每次可以获取2MB内存,比默认的4KB页效率提高512倍;因为进程的地址空间是虚拟的,所以CPU和操作系统需要记录页面和进程的对应关系。操作系统中的页面越多,我们就越需要花更多的时间在如下图的五层页表结构中寻找虚拟内存对应关系。物理内存,我们会根据虚拟地址依次访问页表中的目录,最终找到对应的物理内存:图3-默认页的五层页表如上图所示,如果我们使用Linux内存页默认4KB,那么CPU在访问相应内存时需要读取PGD、PUD、PMD和PTE来获取物理内存,但是2MB的大内存可以减少目录访问次数:图4-页表而largepage因为2MB的内存页占用了21位地址,所以我们不再需要五级页表中的PTE结构,这样不仅减少了转换虚拟地址时的页表访问次数,也减少了页表的内存使用CPU总能通过上述复杂的目录结构找到虚拟页对应的物理页,但是每次转换虚拟地址都要使用上述结构是一个非常昂贵的操作。操作系统使用TLB作为缓存来解决这个问题。TLB是内存管理组件(MemoryManagementUnit)的一部分,其中缓存的页表条目可以帮助我们快速翻译虚拟地址:图5-TLB更大的内存页意味着更高的缓存命中率,因为TLB缓存的容量是一定的,它只能缓存指定数量的页面。在这种情况下,缓存2MB大页面可以提高系统的缓存命中率,从而提高系统的整体性能。除了减少页表条目和提高缓存命中率外,使用更大的页面还可以提高内存访问效率。对于同样的1GB内存,使用4KB内存页需要系统处理262144次,而使用2MB大页只需要512次,可以将系统获取内存所需的事务处理次数减少几个数量级。锁定内存使用HugePages锁定内存,禁止操作系统交换和释放内存。Linux系统提供了交换分区(Swap)机制,当内存不足时会从内存中复制一部分内存页到磁盘,释放内存页占用的内存空间,当相应的内存进程访问时,它会被交换到内存中,这种机制可以为进程制造一种内存充足的错觉,但也会导致各种问题。图6-Swap分区在WhyNUMAaffectsprogramlatency一文中,我们介绍了开启NUMA时Swap可能会影响数据库的性能[^5]。系统中偶尔的Swap不是不能接受,但是频繁的读写磁盘会明显拖慢操作系统。HugePages不同于其他内存页。它由系统工程师使用操作系统上的命令预先分配。当进程通过mmap或其他系统调用申请大页面时,它们会获得预先分配的资源。Linux中的HugePages是锁在内存中的,所以即使系统内存不足时,也不会交换到磁盘上,可以从根源上杜绝重要内存频繁换入换出的可能[^6]。REHL6引入了TransparentHugePages(THP),这是一个抽象层,可以自动创建、管理和使用大页面。它可以为系统管理员和开发人员隐藏使用大页面的复杂性,但不建议在数据库和类似工作负载中启用它。[^7]总结随着单机内存越来越大,服务消耗的内存越来越多,Linux等操作系统引入了类似HugePages的功能。服务性能:HugePages可以减少内存页的管理开销,它可以减少进程中的页表项,提高TLB缓存的命中率和内存访问效率;HugePages可以锁定内存,禁止操作系统的内存交换和释放,不会被交换到磁盘上为其他请求让路;虽然HugePages的管理相对复杂,需要系统管理员进行额外的特定配置,但是对于某些类型的工作负载,它确实可以减少管理开销和锁定内存的作用,从而提高系统性能。最后,让我们看看一些未解决的相关问题。有兴趣的读者可以仔细思考以下问题:TransparentHugePages(THP)可能会导致哪些问题?手册管理系统中的HugePages有哪些?优点?如果您对文章内容有任何疑问,或者想更多地了解软件工程中一些设计决策背后的原因,可以在博客下留言。作者会及时回复与本文相关的问题,并选择合适的话题作为后续内容。原文链接:https://draveness.me/whys-the-design-linux-hugepages/本文转载自微信公众号“毫无逻辑”,可通过以下二维码关注。转载本文请联系真诺逻辑公众号。
