NUMA(Non-Uniform Memory Access):CPU 访问本地内存 ~80ns,跨 socket 访问远端内存 ~140-200ns。多路服务器上忽略 NUMA 可能丢 30% 性能。
一、先看图
flowchart LR
C0[CPU 0-15<br/>Node 0] --> M0[DRAM Node 0<br/>256GB, ~80ns]
C1[CPU 16-31<br/>Node 1] --> M1[DRAM Node 1<br/>256GB, ~80ns]
C0 -.跨 QPI/UPI.-> M1
C1 -.跨 QPI/UPI.-> M0
CXL[CXL 设备] -.CXL.mem.-> M2[CXL 内存<br/>Node 2, ~200ns]
classDef local fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef remote fill:#f0883e22,stroke:#f0883e,color:#adbac7;
classDef cxl fill:#a371f722,stroke:#a371f7,color:#adbac7;
class C0,C1,M0,M1 local
class CXL,M2 cxl
二、拓扑发现
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 ... 15
# node 0 size: 262144 MB
# node distances:
# node 0 1
# 0: 10 21
# 1: 21 10
lscpu | grep NUMAnode distance 矩阵:10=本地,21=一跳,31=两跳(更大机器)。
三、mempolicy
Linux 给每个进程/VMA 一个内存分配策略:
- default:优先本地 node
- bind:只在指定 node 分配
- interleave:轮询所有 node(适合初始化大数据结构)
- preferred:优先某 node,不够再退
- preferred_many(5.15+):多个 preferred node
- local:严格本地
# 绑定 node 0
numactl --membind=0 ./app
# 交错分配
numactl --interleave=all ./app代码级
#include <numaif.h>
set_mempolicy(MPOL_BIND, nodemask, maxnode);
// per-VMA
mbind(addr, len, MPOL_BIND, nodemask, maxnode, MPOL_MF_MOVE);四、AutoNUMA / NUMA balancing
4.1 问题
手动 numactl 太粗——进程运行中可能被调度器迁移到远端 CPU,或 fork 后子进程在远端 node 跑着用本地内存。
4.2 AutoNUMA 方案
内核定期把 PTE 标为不可访问(PROT_NONE)→ 触发 NUMA hint fault → 记录”谁在哪个 node 访问”→ 如果页和任务不同 node → migrate page 或 migrate task。
cat /proc/sys/kernel/numa_balancing # 1=开启(默认)
cat /proc/vmstat | grep numa
# numa_hit numa_miss numa_foreign
# numa_pages_migrated4.3 代价
NUMA hint fault 本身有开销(额外 page fault)。对 I/O 密集不明显,对计算密集可达 1-3% CPU。
关闭:
sysctl kernel.numa_balancing=0五、page migration
内核能在 node 间迁移物理页:
migrate_pages(pid, old_nodes, new_nodes); // syscall或 numactl --hardware +
migratepages 工具。
内部: 1. 在目标 node 分配新页 2. 复制内容 3. 更新所有 PTE(rmap walk) 4. 释放老页
开销:每页几 μs(含 TLB 刷新)。大批量迁移可达 ms。
六、NUMA 与调度
C-24 讲了调度器的 NUMA 感知。总结交叉:
- 调度器尽量把任务放在其内存所在 node
sched_domain层级区分 LLC / Node / System- NUMA balancing 和调度器协同——如果迁移任务更便宜就迁任务,否则迁页
七、CXL 与分级 NUMA
CXL(Compute Express Link)能把远端 DRAM 或持久内存挂成新的 NUMA node:
Node 0: 本地 DDR5 (~80ns)
Node 1: 远端 socket DDR5 (~140ns)
Node 2: CXL DRAM (~200-300ns)
Linux 6.x+ 支持 CXL memory hotplug、auto-node 创建。
memory tiering
2 级以上的 NUMA 层次需要分层策略:
- 热页放快层(Node 0/1)
- 冷页降级到慢层(Node 2 CXL)
- 内核
memory_tier/ demotion 框架(6.1+)
cat /sys/devices/system/memtier/ # 查看 tier 结构八、生产调优
8.1 数据库
# MySQL / PostgreSQL
numactl --interleave=all mysqld # buffer pool 均匀分布
# 或 membind 配合 CPU 绑定为什么 interleave?DB buffer pool 几十 GB,所有 CPU 都访问,interleave 避免单 node 热点。
8.2 JVM
numactl --localalloc java -Xmx32g ...
# JVM 大堆用 -XX:+UseNUMA(G1/ZGC 支持)8.3 HPC
# MPI 进程绑 NUMA
mpirun --map-by numa --bind-to core ./simulation8.4 容器
Kubernetes topology-manager(TopologyPolicy:
best-effort / restricted / single-numa-node)保证 pod 的 CPU
和内存在同一 node。
九、诊断
numastat # per-node hit/miss/foreign
numastat -p java # per-process per-node
perf stat -e node-loads,node-load-misses,node-stores,node-store-misses ./app
cat /proc/$$/numa_maps
# addr policy=default file=/lib/x86_64-linux-gnu/libc.so.6 mapped=123 ...numa_miss / numa_foreign 高 = 跨 node 访问严重。
十、常见问题
A:MySQL 性能波动大 原因:内核 default
policy → buffer pool 全分到启动时的 node → 另一半 CPU 全跨
node。修复:numactl --interleave=all。
B:NUMA balancing 导致 CPU 毛刺 大内存
Java 服务 NUMA hint fault
频繁。numa_balancing=0 + 手动绑定。
C:CXL 节点意外参与分配
/sys/devices/system/node/nodeN/memtier
没配好。确认 demotion 顺序。
十一、小结
- NUMA 拓扑上内存不平等——本地 vs 远端差 2-3x
- mempolicy 控制分配位置(default/bind/interleave/preferred)
- AutoNUMA 自动做 page/task migration,但有开销
- CXL 扩展出更多 NUMA 层级——需要 memory tiering
- 生产:DB 用 interleave,实时用 membind,容器用 topology-manager
参考文献
Documentation/admin-guide/mm/numa_memory_policy.rst- Rik van Riel, “AutoNUMA.” Red Hat 2012
mm/mempolicy.c、mm/migrate.c- Dan Williams, “CXL memory in Linux.” LPC 2022
Documentation/admin-guide/mm/memory-tiers.rst
工具
numactl、numastat、migratepages/proc/$$/numa_mapsperf stat -e node-*lscpu、lstopo(hwloc)- Kubernetes topology-manager
上一篇:OOM Killer 下一篇:HugeTLB 与 THP
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】可拆分 OS
CXL/RDMA 会把 OS 拆成什么形状?LegoOS 分离式内核、CXL 2.0/3.0 内存池、Far Memory、远程页面错误、failover 语义——本文讲硬件解耦对 OS 的冲击。
【操作系统百科】OS 的下一个十年
下一代 OS 应为 AI workload、异构计算、CXL、内核旁路做什么?加速器主导的调度、sched_ext + ML、分离执行、OS 与 runtime 的重新分工——以工程案例收束全系列。
【操作系统百科】spinlock 家族
内核 spinlock 从关中断到 qspinlock 演化了四代。本文讲原始 spinlock、ticket lock、MCS lock、qspinlock、paravirt qspinlock、spin_lock_irqsave 的代价与 NUMA 友好性。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。