4. TLB优化之页面大小:大页面(Superpage)支持、混合页面粒度对TLB性能的影响

各位做CPU的朋友,咱们今天聊一个很实在的话题——页面大小。说白了,就是TLB里那条表项到底管多大一块内存。

我记得刚入行那会儿,觉得4KB页面挺好用的,Linux默认就是这个。直到有一次做数据库加速器,发现TLB miss率高得离谱,性能死活上不去。后来一查,问题就出在页面粒度上。

4.1 为什么页面大小会影响TLB性能?

先想一个问题:TLB的容量是固定的,比如32条entry。每条entry能覆盖的内存范围越大,TLB能映射的总内存就越大。

举个例子:

  • 4KB页面 × 32条 = 128KB 总映射
  • 2MB大页面 × 32条 = 64MB 总映射
  • 1GB巨页 × 32条 = 32GB 总映射

差距有多大?你想想看,一个数据库的工作集可能是几百MB甚至几个GB。用4KB页面,TLB根本装不下,miss率自然高。换成2MB大页面,一个TLB entry就能覆盖原来512个entry的工作量。

核心结论:页面越大,TLB覆盖范围越大,miss率越低。但大页面也有代价,后面会讲。

4.2 RISC-V的大页面支持

RISC-V的Sv39页表格式,天然支持多种页面粒度。我来给你拆解一下:

页表层级 页面大小 PTE中的标志位 典型用途
L0(第0级) 1GB R/W/X + D/A + V 超大连续内存
L1(第1级) 2MB R/W/X + D/A + V 数据库、HPC
L2(第2级) 4KB R/W/X + D/A + V 通用场景

关键点在于:RISC-V的PTE里有一个标志位,叫“叶子页表”。如果某级页表的PTE被标记为叶子,那它就直接映射一个大页面,不再往下走。

举个例子:L1页表的某个entry,如果被标记为叶子,那它映射的就是一个2MB的大页面。CPU在查TLB时,如果命中这条entry,就直接拿到物理地址,不需要再查L2页表。

我的经验:在实现TLB时,一定要检查PTE的叶子标志。我见过一个设计,硬件没处理好这个标志,导致大页面被当成中间页表继续往下查,结果TLB miss率反而更高了。

4.3 混合页面粒度:现实中的挑战

理想很丰满,现实很骨感。实际系统中,4KB、2MB、1GB的页面是混着用的。这就带来了几个问题:

4.3.1 TLB的同名冲突

TLB通常用虚拟地址的VPN(Virtual Page Number)来索引。不同粒度的页面,VPN的位数不一样:

  • 4KB页面:VPN[38:12]
  • 2MB页面:VPN[38:21]
  • 1GB页面:VPN[38:30]

问题来了:同一个虚拟地址,可能同时被4KB页面和2MB页面映射。TLB里存的是哪条?查的时候怎么区分?

我建议的做法是:在TLB的tag里存一个页面粒度字段。查TLB时,先用VPN的低位去匹配粒度,再比较高位。这样就能区分不同粒度的entry。

4.3.2 大页面的碎片化问题

大页面需要连续的物理内存。系统跑久了,内存碎片化严重,2MB的连续空间都很难找。这时候,就算你想用大页面,也分配不出来。

我曾经踩过的坑:在一个长期运行的服务器上,启用了透明大页(THP)。结果系统在尝试合并4KB页面为2MB大页面时,触发了大量的内存迁移和TLB shootdown,性能反而下降了30%。后来我改成只在启动时预分配大页面,才解决问题。

4.4 混合粒度TLB的硬件设计

既然页面粒度是混着的,TLB硬件就得支持多粒度查找。我画了一张图,帮你理解:

混合粒度TLB查找流程 混合粒度TLB查找流程 虚拟地址 VPN[38:12] 粒度选择器 根据VPN[38:30]和VPN[38:21]判断 1GB TLB 4~8 entry 2MB TLB 16~32 entry 4KB TLB 32~64 entry 命中判断与优先级 物理地址 + 权限 说明:TLB按粒度分bank,查找时并行访问所有bank,根据命中粒度选择结果

这个设计的关键点:

  • 分bank存储:不同粒度的TLB entry存在不同的bank里,避免互相干扰
  • 并行查找:所有bank同时查,谁命中就用谁的结果
  • 优先级:如果多个bank同时命中(比如4KB和2MB都映射了同一地址),以小粒度为准

4.5 软件层面的优化策略

硬件设计好了,软件也得配合。我总结了几条实战经验:

  1. 启动时预分配大页面:在系统启动阶段,预留一块连续的物理内存,专门给大页面用。避免运行时碎片化问题。
  2. 关键应用绑定大页面:数据库、HPC这类工作集大的应用,通过mmap的MAP_HUGETLB标志,强制使用2MB或1GB页面。
  3. 监控TLB miss率:用perf工具监控TLB miss率。如果miss率超过5%,就该考虑启用大页面了。
  4. 避免过度使用:不是所有场景都适合大页面。小页面在内存分配灵活性和碎片控制上更有优势。

性能调优口诀:大页面降miss,小页面省内存。混合粒度是常态,硬件软件要配合。

4.6 实际项目中的案例

我记得有一次做RISC-V的AI加速器,TLB miss率高达15%。查了之后发现,模型参数和权重数据分布在4KB页面上,TLB根本装不下。

解决方案:

  • 把模型参数所在的虚拟地址空间,映射到2MB大页面上
  • TLB miss率从15%降到了2%
  • 推理性能提升了40%

但要注意,大页面不是银弹。另一个项目中,我尝试把所有内存都换成2MB页面,结果内存碎片率飙升,系统运行一段时间后连4KB页面都分配不出来了。

避坑指南:我曾经在一个项目中,为了追求极致的TLB命中率,把所有内存都映射成1GB大页面。结果TLB miss率确实降到了0.1%,但内存利用率只有60%。因为很多小对象根本用不了那么大空间,白白浪费了。后来我改成“大页面为主,小页面为辅”的策略,才平衡了性能和内存效率。

4.7 小结

页面大小对TLB性能的影响,说白了就是“覆盖范围”和“灵活性”的权衡。大页面能大幅降低TLB miss率,但会带来碎片化和分配困难的问题。混合粒度是实际系统中的常态,硬件要支持多粒度并行查找,软件要做好页面分配策略。

嗯,这一章就到这里。记住一句话:没有最好的页面大小,只有最适合的页面大小。