3. 标准页与巨页:4KB页与2MB/1GB页的对比、页表层级差异
在 x86-64 架构下,操作系统默认使用 4KB 标准页 管理内存。对于 DPDK 这类需要大量连续物理内存、频繁进行 TLB 查找的高性能应用,4KB 页会带来严重的性能瓶颈。本章深入对比 4KB 页与 2MB/1GB 巨页在页表层级、TLB 覆盖范围、内存分配开销上的本质差异。
3.1 页表层级:从 4 级到 2 级
x86-64 的分页机制采用多级页表结构。页表层级数直接决定了虚拟地址到物理地址的转换路径长度,以及单次 TLB miss 后遍历内存的代价。
核心差异: 4KB 页需要 4 级页表(PML4 → PDPT → PD → PT),而 2MB 巨页仅需 3 级(PML4 → PDPT → PD),1GB 巨页仅需 2 级(PML4 → PDPT)。层级越少,TLB miss 后的硬件页表遍历(page walk)次数越少,延迟越低。
3.2 关键指标对比表
| 对比维度 | 4KB 标准页 | 2MB 巨页 | 1GB 巨页 |
|---|---|---|---|
| 页表层级 | 4 级(PML4→PDPT→PD→PT) | 3 级(PML4→PDPT→PD) | 2 级(PML4→PDPT) |
| 单页大小 | 4 KB | 2 MB(512 × 4KB) | 1 GB(512 × 2MB) |
| TLB 覆盖范围(典型 64 项 L1 DTLB) | 64 × 4KB = 256 KB | 64 × 2MB = 128 MB | 64 × 1GB = 64 GB |
| 缺页中断次数(映射 1GB 内存) | 262,144 次 | 512 次 | 1 次 |
| 页表占用(映射 1GB 内存) | 约 4 MB | 约 8 KB | 约 4 KB |
| TLB miss 惩罚(page walk 次数) | 4 次内存访问 | 3 次内存访问 | 2 次内存访问 |
| 适用场景 | 通用进程、小内存分配 | DPDK 数据面、数据库、HPC | 虚拟化、大内存数据库、GPU 显存映射 |
TLB 覆盖范围实战意义: DPDK 通常为每个 NUMA 节点预留 1~4 GB 巨页内存。使用 2MB 巨页时,仅需 8~32 个 TLB 条目即可覆盖全部数据面内存,TLB miss 率趋近于零。而使用 4KB 页则需要 256K~1M 个条目,远超 CPU TLB 容量,导致频繁 miss。
3.3 页表结构详解:4KB 页的 4 级转换
以 4KB 标准页为例,虚拟地址被划分为 5 个部分:PML4 索引(9 bit)、PDPT 索引(9 bit)、PD 索引(9 bit)、PT 索引(9 bit)、页内偏移(12 bit)。每次地址转换需要依次读取 4 个页表项,每个页表项 8 字节,共 32 字节内存访问。
3.4 巨页的页表压缩原理
巨页通过 跳过中间页表层级 来压缩页表深度。以 2MB 巨页为例:
- PD 页表项中的 PS 位(Page Size,bit 7) 被置为 1,表示该项直接指向 2MB 物理页,不再需要 PT 级。
- 虚拟地址中原本用于 PT 索引的 9 bit 和页内偏移的 12 bit 合并为 21 bit 偏移(2MB = 221)。
- 页表项数量从 4KB 页的 512 个 PT 项 + 1 个 PD 项,减少为仅 1 个 PD 项。
注意事项: 巨页虽然减少了页表层级和 TLB miss,但分配粒度变大。1GB 巨页的最小分配单位是 1GB,不适合小内存场景。DPDK 通常使用 2MB 巨页作为默认配置,仅在需要超大连续内存(如虚拟化 vhost 后端)时启用 1GB 巨页。
3.5 TLB miss 代价量化对比
假设一次内存访问延迟为 10 ns(L3 cache 命中),TLB miss 后的 page walk 需要逐级读取内存中的页表项:
| 页大小 | page walk 次数 | 理论延迟(L3 命中) | 实际延迟(内存访问) | DPDK 数据面影响 |
|---|---|---|---|---|
| 4KB | 4 次 | 4 × 10 ns = 40 ns | 4 × 100 ns = 400 ns | 每百万包增加 400 ms 延迟 |
| 2MB | 3 次 | 3 × 10 ns = 30 ns | 3 × 100 ns = 300 ns | 每百万包增加 300 ms 延迟 |
| 1GB | 2 次 | 2 × 10 ns = 20 ns | 2 × 100 ns = 200 ns | 每百万包增加 200 ms 延迟 |
实战结论: 在 DPDK 轮询模式(run-to-completion)下,每收到一个数据包都需要访问 mbuf 和 packet buffer。如果使用 4KB 页且 TLB 频繁 miss,每百万包将额外增加 400 ms 的地址转换延迟。对于 10 Gbps 线速(约 14.88 Mpps),这意味着每秒增加约 6 秒的纯地址转换开销。切换到 2MB 巨页后,TLB 覆盖范围扩大 512 倍,miss 率趋近于零,这部分开销几乎完全消除。
3.6 巨页配置对 DPDK 的直接影响
DPDK 在初始化时通过 rte_eal_init() 调用 rte_memseg_contig_walk() 遍历系统巨页。巨页大小直接影响以下关键参数:
- mempool 创建: DPDK 的 mempool 需要从巨页中分配连续物理内存。2MB 巨页可以满足大多数 mempool 需求(通常 1~64 MB),1GB 巨页用于超大 mempool(如 512 MB 以上)。
- Hugepage 文件系统:
/dev/hugepages挂载点下的文件大小等于巨页大小。DPDK 通过 mmap 映射这些文件获取物理地址。 - iommu 映射: 使用 VFIO 驱动时,IOMMU 页表同样受益于巨页。2MB 巨页可减少 IOMMU 页表项数量,降低 DMA 映射开销。
# 查看系统支持的巨页大小
grep Hugepagesize /proc/meminfo
# 查看当前巨页分配情况
cat /proc/meminfo | grep -i huge
# DPDK 启动时指定巨页大小(示例:使用 2MB 巨页)
./dpdk-testpmd -l 0-3 -n 4 --socket-mem 1024,1024 \
--huge-dir /dev/hugepages2M \
--file-prefix pg2M \
-- -i
最佳实践: 对于 DPDK 数据面应用,优先使用 2MB 巨页。它平衡了 TLB 覆盖范围(128 MB 以上)和分配灵活性(最小 2MB)。仅在需要映射超过 1GB 连续物理内存(如 GPU 显存、NVMe 大块 IO)时,才考虑 1GB 巨页。4KB 标准页应完全避免用于数据面内存。