Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

分页、TLB、Huge Page

一句话

页面大小是硬件工程师决定的硬件常数, 它决定了 TLB 的覆盖范围、cache line 数量、IO 单位, 同时直接影响用户程序性能. 但页大小不是任选的 —— 4 KB 是历史甜点, 大页是工程妥协, 这章把"页大小为何是这个数字、改大会怎样、改小会怎样" 一次讲清.

页大小为什么是 4 KB

回头看上世纪 80 年代 VAX-11/780

  • 物理 RAM 几 MB;
  • 寻址 32-bit, 4 GB 虚拟空间;
  • 页表按 1 项 4 字节算: 4KB page = 1 M 项 × 4 = 4 MB 表 / process;
  • 4 KB page + 4 MB 表 —— 把页表塞到一页可以放进 L1 cache 内, 极合理.

4 KB 是历史习惯的甜点, 同时这一常数一直被物化保留:

  • x86 4 KB 一直保留;
  • OS page cache 一直按 4 KB;
  • NVMe 块层一直按 4 KB;
  • 文件系统 metadata 一直按 4 KB;

但 64-bit 时代虚拟空间暴涨、TLB 覆盖跟不上, huge page = x86 同时支持 2 MB / 1 GB 是补丁.

TLB 覆盖范围

TLB 是一个非常小的 cache (~128 entry). 它覆盖的内存量 = entry 数 × page 大小:

4 KB page, 128 entry TLB  → 覆盖 512 KB;
2 MB huge page, 128 entry → 覆盖 256 MB;
1 GB huge page, 128 entry → 覆盖 128 GB; (!)

memory footprint > covered range 时, TLB 持续 miss → page walk 频繁 → 全程被加 7~30 cycle 翻译延迟. 一次 page walk 通常: 4 级 × 几百 ps ≈ 几 ns, 但上层 cache miss 也会 = 几十 ns.

这就是 huge page 的真正价值, 不是"省内存", 而是把"覆盖范围" 抬高一 / 两个量级.

三种 huge page 模式

1. 显式 mmap 大页

mmap(NULL, sz, PROT_READ|PROT_WRITE,
     MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB, -1, 0);

要求 OS 配合: echo N > /proc/sys/vm/nr_hugepages 预留大页池.

缺点: 大页池是预留, 不使用也 占用, 不能跑普通程序. 需要管理员权限配置.

2. Transparent Huge Page (THP)

echo always > /sys/kernel/mm/transparent_hugepage/enabled

内核在后台同步把 4 KB 进程合并成 2 MB 大页. 无需 app 适配, 但有如下坑:

  • khugepaged 在后台扫描要 CPU, 1-5% 在大内存机器上看得到;
  • 一旦扩容了, 拆回 4 KB 是 O(N) 反过来;
  • 小内存进程 (几十 MB) 几乎无收益, 反而加回收代价;
  • HRT 场景下偶尔有 STALL.

3. 1 GB 大页 (gbpage)

echo 5 > /proc/sys/vm/nr_hugepages-1g  # 假写法, 看具体内核

只适合大内存 + 大数据集:

  • HPC / ML 训练大 batch GPU memory pool;
  • 大巨型 Redis cache (>100 GB);
  • ML training 矢量库(vector db).

huge page 在查询热服务上的实测

Redis benchmark, NUMA 4-socket, 256 GB 内存, 1000 万键:

  • 4 KB page + 默认配置: QPS 250k, P99 0.9 ms;
  • THP enabled: QPS 320k, P99 0.4 ms;
  • 显式 2 MB huge page mmap: QPS 340k, P99 0.35 ms.

吞吐 30% 上升, 尾延迟 P99 减 60%. TLB 这一只性能的代价不可忽略.

thunk: 减少 page walk 的 4 个工程 trick

1. 使用 huge page 提升覆盖范围;
2. 避免大 working set 跨 progress;
3. 内存局部化 (NUMA-aware allocation); 
4. 拒绝频繁 mmap/munmap (TLB shootdown);

NUMA 与 huge page 的协同

NUMA 节点 CPU 访问本地 vs 跨 socket 差 2-4×. huge page 落地哪个 socket 影响 cache.

  • libnuma / mpol 配 huge page 优先在本地 node 分配;
  • HPC 框架 OpenMP + numactl --membind / mmap MAP_HUGETLB 组合;
  • Go runtime GOGC + GOMAXPROCS 配 NODE 不要瞎选.

FPGA / GPU 视角: huge page 同构

GPU 内存也是按一个"页" 概念. CUDA virtual mem management API 让你能 2 MB align, 减少跨 SM page swap. 类比 huge page 同构.

FPGA 上 BRAM 是固定 1-4 MB, 也可以做"凑成"大页-enable 的数据通路. 每一层抽象都把"页" 当成 cache 单位的引擎, 让 TLB / cache / prefetcher 模型成立.

调优现场

# 查 TLB 基本信息
cat /proc/meminfo | grep -i huge

# 查进程是否用了 huge page
cat /proc/<pid>/smaps | grep -i Huge

# 强制进程使用 huge page (THP)
echo always > /sys/kernel/mm/transparent_hugepage/enabled

# 查看 TLB miss 实际触发
perf stat -e dTLB-loads,dTLB-load-misses,iTLB-loads,iTLB-load-misses ./yourapp

this->带走的东西

  • 4 KB 是历史甜点, 64-bit 时代靠 huge page 补丁;
  • TLB 覆盖范围 = entry × page 大小, huge page 把覆盖率抬一个量级;
  • 三种 huge page 模式有各自适用 / 性能脾气;
  • TLB shootdown 在多核 HPC 下是常见惊人延迟;
  • huge page 与 NUMA 协同才能彻底发挥.

下一节 → 页面置换与 working set