NUMA 是什么?内存离 CPU 有远近,远程访问慢 1.5~2 倍
答案:NUMA(非统一内存访问)下,每个 CPU 节点有自己的本地内存,访问本节点快、跨节点慢(约慢 1.5~2 倍)。排查用 numastat,优化用 numactl 绑定 CPU 与内存节点。
核心概念(30 秒理解)
┌─ Node 0 ──────┐ ┌─ Node 1 ──────┐
│ CPU0 CPU1 │ │ CPU2 CPU3 │
│ 本地内存(DDR) │◄──┤ 本地内存(DDR) │
└──────┬────────┘ └──────┬────────┘
└───── 互联总线 ─────┘- CPU 访问本节点内存 = 快(本地访问);
- CPU 访问对方节点内存 = 走互联总线(远程访问),慢 1.5~2 倍;
- 程序性能问题常是:线程在 A 节点跑,内存却散落在 B 节点。
排查三件套
# 1. 看拓扑:哪些 CPU 属于哪个节点,节点间距离
numactl --hardware
# 2. 看进程内存分布:N0/N1 各分配多少
numastat -p <PID>
# 3. 看当前 NUMA 策略
numactl --show关键判断:numastat 里 local_node 占比低、other_node 占比高 → 内存大量跨节点,性能被远程访问拖累。
优化手段
# 绑定 CPU 与内存到同一节点(最常用)
numactl --cpunodebind=0 --membind=0 ./app
# 仅绑定 CPU,内存由内核就近(对某些场景更灵活)
numactl --cpunodebind=0 ./app- 绑定 CPU 节点 + 内存节点:
--cpunodebind+--membind同节点; - first-touch 陷阱:Linux 按首次访问分配页面——初始化数据时用哪个线程碰,页面就落哪个节点,初始化线程要"留在正确的节点";
- 代码级控制:
mbind()、move_pages()、set_mempolicy()可精细控制。
深度入口
- NUMA 原理与实验:NUMA 详解
- NUMA 观测工具:numastat/numactl 指南
- 内核参数调优:NUMA 相关参数
FAQ
Q: NUMA 是什么? A: NUMA(非统一内存访问)架构下,每个 CPU 节点有自己的本地内存,访问本节点内存快、访问其他节点内存慢(远程访问),性能差距约 1.5~2 倍。
Q: 为什么会有 NUMA? A: 单路内存控制器带宽有限,多路 CPU 各自带内存控制器并联,CPU 数多了必然出现"谁离谁的控制器近"的问题,于是分节点、有远近。
Q: 怎么查看 NUMA 拓扑和内存分布? A: numactl --hardware 看拓扑;numastat -p <PID> 看进程内存落在哪些节点;numactl --show 看当前策略;/proc/zoneinfo 看每节点内存。
Q: NUMA 下程序变慢怎么排查? A: 1) numastat 看远程分配比例是否高;2) 看进程内存是否跨节点散落;3) 用 numactl --cpunodebind 绑定 CPU 节点,配合内存节点绑定。
Q: 怎么让程序内存本地化? A: 用 numactl --cpunodebind=N --membind=N 运行程序(cpu 与内存绑同节点);代码里可用 mbind/move_pages 控制;大内存优先分配注意首触页 (first-touch)。
Q: 首触页(first-touch)是什么? A: Linux 默认按"首次访问"分配内存页,谁先碰页面,页就落在谁的节点。因此初始化时用哪个线程访问,就决定数据在哪个节点——初始化线程要挑对。
一句话总结:NUMA 的核心是"内存有远近";用 numastat 看分布、numactl --cpunodebind --membind 绑同节点、注意 first-touch,就能把远程访问开销压下来。