大页技术是操作系统中优化内存访问延迟的一种技术,其优化原理与CPUTLB硬件有直接关系,而其优化效果不仅受CPUTLB硬件影响,还需要看应用访存特点。只考虑Arm和x86两种平台,已知的大页技术包括透明大页、hugetlbfs、16k和64k全局大页。在合适的场景,大页技术可以提升应用性能达10%以上,尤其是针对当前云上应用逐年增长的内存使用趋势,使用大页技术是其中重要的提升“性能-成本”比例的优化手段。透明大页从2011年开始在Linux内核中已经支持起来,其通过一次性分配2M页填充进程页表,避免多次缺页开销,更深层次从硬件角度优化了TLB缺失开销,在最好情况下,对应用的优化效果达到10%左右。除以上优点外,透明大页使用过度也会导致严重的内存碎片化、内存膨胀和内存利用率低等问题,这就是当前透明大页没有在数据库中使用的核心原因,只能感叹“卿本巧技,奈何有坑”。
代码大页在透明大页的基础上,将支持扩展到可执行二进制文件,包括进程二进制文件本身、共享库等可执行数据。与透明大页相比,由于代码大页仅将占比较低且有限的可执行文件页部分转换为大页,从根本上避开了内存碎片以及内存不足的问题。与此由于代码类数据和普通堆栈数据访问热度对整体性能影响不同,导致代码类数据使用大页所提升的性能远大于同样分量的透明大页。所以推广和完善代码大页相比透明大页更加简单和容易。
02大页现状
当前Linux内核支持的大页包括THP和hugetlb,其页大小分别是:
不考虑其他架构,在x86和Arm架构中,提到THP,我们可以一股脑地认为其大小就是2MB,当前内核暂时还不支持1GBTHP,技术上实现没有什么问题,社区隐隐约约也有人曾经发过相关补丁...。回到这里,Arm64相比x8hugtlb多两种大页支持,主要是contiguousbit,该特性主要是针对TLBentry的优化。这里的64KB和32MB的由来就是16*4KB和16*2MB。
在全局页粒度的支持上,Arm64也比x86更会玩,提供的16KB和64KB两种选择,这两种页相比4KB页,在TLB和cache上都有明显的优化相关,从而优化宏观指标,当然也并非所有benchmark表现出性能提升,例如在SPECjvm2008和stream中,我们就发现有多项指标在使用CONFIG_64KB的时候有较严重的性能下降。除以上问题,还想再啰嗦一下,CONFIG_64KB中,THP的大小着实有点“吓人”,有512MB。
大伙对16KB、64KB还是又爱又恨。
Mysql是一个多线程模式的数据库,其代码段大小一般18M左右。THP不敏感,打开THP,大约仅有不到3%的性能提升。跨NUMA敏感,本地虚拟机32核验证跨NUMA抖动在5~7%左右。
多进程模型,代码段大小大约10M左右。应用iTLB-load-misses较高,大约41%左右。
多线程模型,代码段大小大约200M~280M。一般独占单机使用,性能验证过程中并发数要求高:121000、1500。THP本地验证不敏感。
这些数据库大约至少有两个共同点:代码段大、iTLBMiss高。本文也是基于这两个特征进行的优化,当然代码大页优化目标也不局限于这三种数据库。
04代码大页
1整体结构与实现
基于透明大页异步整合大页的框架:
上所展示的代码大页方案主要包括三个部分:
映射首地址对齐:这个部分主要是在elfbinary和DSO建立映射的过程中,优先考虑分配2M对齐的虚拟地址空间,便于映射到2M大页。
异步khugepaged扫描整合以及加速:与THP相似,单独设计用户态接口hugetext_enabled控制。复用khugepaged整合2M大页。此外,由于hugetext与THP共用khugepaged,在THP=always时,也能整合部分符合条件的代码大页;关于加速部分,我们解决了在THP使能的场景中,代码段整合慢的问题,这也是我们改进READ_ONLY_THP_FOR_FS带来的挑战之
注意:首地址2M对齐的本意是mmapaddr=mmappgoff,由于elfbinary和DSO的可执行LOAD段的pgoff一般为0,这里为了叙述方便,我们简称地址2M对齐。
在开发和测试过程中,我们的实现方案解决了fileTHP相关的三个bug的补丁,Linux社区已经合入,glibc社区也合入了一个p_align修复补丁。除以上部分,我们目前已经完成的自适应代码大页已经完成,主要解决x86平台使用代码大页过多的问题。详细见“自适应处理”一节。
1代码段填充功能
前面,我们已经描述代码大页方案支持动态链接库和二进制文件本身,对于libhugetlbfs方案,其仅仅支持二进制文件本身的大页转换,虽转换比较完全,但是它无法同时让DSO使用大页。显而易见,与libhugetlbfs相比,我们提供的hugetext_enabled方式更加完整,性能优势更大。填充功能主要是我们为弥补在某些场景中,其大量的热点发生在代码段的尾部,libhugetlbfs天生可以做到,所以我们也不得不解决这个问题。言归正传,回到代码段填充。首先用一张来表示代码段填充具体是什么。
大多数应用并不能完全保证其代码段都会2M对齐,如上所示,一个应用第一个r-xp有2M,其中2M我们的hugetext_enabled本身即支持,后面的2M我们不得不向后填充这个vma,保证其映射大小2M对齐,其填充的内容其实就来自于下一个r--p的数据。当然我们在填充过程中会判断填充后大小不会超过原文件大小和不会与下一个r--p属性的vma重叠。下面是我们新引入的代码大页填充使能开关,例如将0x1000写入hugetext_pad_threshold,表示需填充内容超过4k时填充功能才会使能。
/sys/kernel/mm/transparent_hugepage/hugetext_pad_threshold
最后再夹带一点私货,以上代码段填充主要是在不重新编译应用程序的情况的一种内核方式,还有一种方式是在应用的链接脚本中加入“.=ALIGN”,将代码段直接按照2M对齐,如此不需要填充或进行填充更加安全。例如以一个简单的测试程序align.out为例。设置链接脚本后Section出现对齐:
$ readelf -l align.out
Elf file type is EXEC (Executable file)
Entry point 0x400520
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001b 0x000000000000001b R 0x1
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000200194 0x0000000000200194 R E 0x200000
LOAD 0x0000000000400000 0x0000000000a00000 0x0000000000a00000
0x0000000000010040 0x0000000000200008 RW 0x200000
DYNAMIC 0x0000000000400010 0x0000000000a00010 0x0000000000a00010
0x00000000000001d0 0x00000000000001d0 RW 0x8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x000000000020004c 0x000000000060004c 0x000000000060004c
0x000000000000004c 0x000000000000004c R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000400000 0x0000000000a00000 0x0000000000a00000
0x0000000000010000 0x0000000000010000 R 0x1
Section to Segment mapping:
$ gcc main.c test.c -o align.out -Wl,-T ld-align.lds -z max-page-size=0x200000
链接脚本加hugetext_pad_threshold结合使用,可以让应用对代码大页使用更加友好。
2快速试用
为了方便业务使用,我们扩展了两种打开方式:启动参数和sysfs接口。
启动参数
打开:hugetext=1or2or3关闭:缺省即为关闭
sysfs接口
文章为作者独立观点,不代表股票交易接口观点