机箱内的拓扑优化,一次讲清
GPU 利用率上不去、多卡并行效率低、训练速度总差一口气——很多时候瓶颈不在 GPU 本身,而在 CPU 与内存的「地理关系」上。
做过 AI 训练或推理部署的人,大多踩过这种坑:显存堆上去了,NVIDIA-SMI 一看利用率只有 80% 甚至更低,显卡之间通信比计算还慢。其实不是模型有问题,而是进程被操作系统随机调度到了「远」的地方,每次访问内存、搬运数据都要跨节点绕路。
这就是今天要讲的 GPU NUMA 亲和:一个零代码改动、往往能让训练推理快 20%~40% 的部署技巧。
GPU NUMA 亲和(NUMA Affinity),是指让 GPU 的计算、通信,以及与它交互的 CPU 和内存都落在同一个 NUMA 节点内,避免跨节点访问,从而减少延迟、保住带宽。
大白话说:让「干活的人」和「放材料的仓库」住在同一个街区,别让工人每次拿材料都要穿城跑一趟。
要理解 NUMA 亲和,先得知道 NUMA 是什么。
早期的单路服务器是 UMA(统一内存访问)架构:所有 CPU 通过一条内存总线访问所有内存,离得近离得远延迟都差不多,一碗水端平。
但现代多路服务器(双路 EPYC、Xeon、鲲鹏等)基本都是 NUMA(非统一内存访问)架构:
| 维度 | UMA(早期单路) | NUMA(现代多路) |
|---|---|---|
| 内存组织 | 所有 CPU 共享一条内存总线 | 内存按节点划分,节点直连一组 CPU |
| 访问开销 | 本地 / 远端几乎一致 | 本地快,跨节点慢 |
| 对调度敏感度 | 低 | 高,进程放错节点性能骤降 |
所以你经常看到的 「NUMA 亲和状态」,本质上是在说三件事之一:
GPU 不是独立存在的,它通过 PCIe 总线挂在某个 CPU 的 PCIe Root Complex 下面。GPU 离哪个 NUMA 节点近,就和那个节点通信最快。
以典型双路服务器为例:
所以多卡并行时,「哪几张卡是一组的」直接决定通信效率。很多框架和调度器已经感知到这层关系,做 NUMA 感知的 GPU 分组绑定。
怎么看 GPU 和 NUMA 的挂载关系?nvidia-smi topo -m 一行命令:
GPU0 GPU1 GPU2 GPU3 CPU Affinity NUMA Affinity GPU NUMA ID
GPU0 X NV8 NV8 NV8 0-11,24-35 0 N/A
GPU1 NV8 X NV8 NV8 0-11,24-35 0 N/A
GPU2 NV8 NV8 X NV8 48-59,72-83 1 N/A
GPU3 NV8 NV8 NV8 X 48-59,72-83 1 N/A
比如上表里 GPU0/1 亲和 NUMA 节点 0(核心 0-11,24-35),GPU2/3 亲和节点 1——调度和绑核就按这个来。
NUMA 对数据库、Web 服务也有影响,但对 AI 负载的影响尤其大,原因在两条链路上:
1. 数据管线是「CPU 密集 + 内存密集」的。训练/推理的前置链路是:CPU 读数据 → 解码 → 缩放归一化 → 拼成 batch → 放到 pinned memory → GPU 通过 DMA 直接搬进显存。这里面有大量 CPU 与内存的交互,GPU 搬数据、同步数据又依赖 CPU 持续响应。跨 NUMA 访问会让每一步都变慢。
2. 多卡并行通信要跨节点。张量并行 / 数据并行里,AllReduce 这类集合通信非常频繁。组内卡间通信走本地 PCIe / NVLink,延迟极低;一旦跨组,就要绕 CPU 互联总线,带宽骤降。
如果进程不做绑核,操作系统会随机把线程调度到不同 NUMA 节点的核心上,造成频繁的跨节点内存访问和通信。业界实测的口径是:多卡并行效率可能连 60% 都到不了;用 numactl 对齐 NUMA 节点后,通常能带来 20%~40% 的提升。
更隐蔽的是尾延迟:跨 socket 访问不仅慢,还会带来抖动——P95/P99 的劣化往往比平均吞吐的损失更明显,这对在线推理服务是致命的。
动手前先搞清楚机器长什么样:
| 命令 | 看什么 | 适用 |
|---|---|---|
nvidia-smi topo -m | GPU 拓扑矩阵、CPU Affinity、NUMA Affinity、GPU NUMA ID | NVIDIA |
numactl --hardware | NUMA 节点布局:每节点的 CPU 列表与本地内存大小 | 通用 |
lspci -tv | PCIe 树形拓扑,确认 GPU 挂在哪个 Root Port | 通用 |
rocm-smi --showtopo | GPU 拓扑与 NUMA 归属 | AMD / 海光 DCU |
numactl --hardware 的输出长这样,能一眼看到节点 0、节点 1 各有哪些 CPU 和多少本地内存:
available: 2 nodes (0-1)
node 0 cpus: 0-11 24-35
node 0 size: 251838 MB
node 1 cpus: 12-23 36-47
node 1 size: 251891 MB
node distances:
node 0 1
0: 10 21
1: 21 10
node distances 里的 21 就是跨节点访问的代价系数(越大越慢),看到它你就有数了。
核心是两条命令的组合,零代码改动:
# 绑定 CPU 节点 0 + 内存节点 0(推荐:连内存分配策略一起锁死)
numactl --cpunodebind=0 --membind=0 python train.py --gpus 0,1,2,3
# 多实例部署:节点 0 和节点 1 各跑一个实例
numactl --cpunodebind=0 --membind=0 python inference_server.py --port 8080 &
numactl --cpunodebind=1 --membind=1 python inference_server.py --port 8081 &
几个容易踩的细节:
--cpunodebind 绑 CPU 节点,--membind 绑内存节点,两个一起用才完整。内存分配默认是「优先本地」,但进程被调度器赶走后,后续分配可能落到别处;--membind 把策略锁死,比单独用 taskset 可靠得多taskset -c 只能锁 CPU 核心,管不了内存分配策略,精细场景建议 numactlCUDA_VISIBLE_DEVICES 隔离 GPU:绑了 NUMA 节点,再用环境变量只暴露该节点的卡,双保险gpu_affinity 库,按 UNIQUE_CONTIGUOUS / NODE 等模式自动给多进程算好绑核方案综合公开的实测口径,收益大致如下(不同硬件/负载有差异,作为参考):
| 场景 | 做法 | 实测收益 |
|---|---|---|
| 8 卡 Llama-2-13B 训练 | NUMA 感知分组绑定(CPU0 管 GPU0-3,CPU1 管 GPU4-7) | 比全插单 CPU 提升约 31% 吞吐 |
| 多卡推理(张量并行) | 前 4 卡绑 NUMA 0,后 4 卡绑 NUMA 1 | 常见 20%~40% 提升 |
| 4 卡内 AllReduce | 组内通信走 CPU0 内部 PCIe | 延迟可低于 0.5ms |
| 在线推理尾延迟 | 进程/worker 绑核 + NUMA 对齐 | P95/P99 抖动显著改善 |
也要说清楚收益的边界:
把全文收成三句话:
nvidia-smi topo -m 查拓扑,numactl --cpunodebind=N --membind=N 绑核,零代码改动,实测常能带来 20%~40% 的提升对直接调 API 的开发者来说,这些「机箱内的功夫」都由平台帮你做了:成熟云平台在实例调度层就把 GPU 与 NUMA 节点、CPU 亲和绑好,用户只需要关心模型和价格。
通过向量云(向量云南京科技有限公司)获取字节跳动火山方舟官方 API 与官方 Key,可拿到低折扣 Token、按量计费、即用即扣、不设账期,注册充值 10 元起,把「平台层优化」和「更低的单价」一次拿齐。
获取 API Key:https://ark.tokenrize.cn/ · 注册即享专属 Token 折扣
一句话总结:机箱内的拓扑优化交给平台,机箱外的 Token 成本交给向量云,两边都对齐,才是大模型部署的最优解。
面向大模型应用落地的技术服务型企业,依托字节跳动火山方舟能力,为合作方提供低折扣模型 Token、可独立部署的运营支撑平台及全链路运营支持。
火山方舟官方 API 与官方 Key 直连 · 向量云 · 按量计费享 Token 折扣
A. 指让 GPU 的计算、通信,以及与它交互的 CPU 和内存都落在同一个 NUMA 节点内,避免跨节点访问。现代多路服务器内存按 NUMA 节点划分,访问本地节点快、远端节点慢,GPU 通过 PCIe 挂在特定 CPU 下,绑核就是为了让本地访问占比最大化。
A. UMA 是早期单路服务器架构,所有 CPU 共享一条内存总线,本地与远端访问开销几乎一致;NUMA 是现代多路服务器架构,内存划分到多个节点、每个节点直连一组 CPU,访问本地内存快、跨节点要绕 CPU 互联总线,延迟更高带宽更低,对进程调度敏感。
A. 两条链路:一是数据管线(读数据、解码、归一化、拼 batch、放 pinned memory 再 DMA 进显存)充满 CPU 与内存交互,跨 NUMA 每一步都变慢;二是多卡并行的 AllReduce 等集合通信,组内走本地 PCIe/NVLink,跨组要绕 CPU 总线带宽骤降。不做绑核时多卡并行效率可能连 60% 都到不了。
A. 用 nvidia-smi topo -m 查看 GPU 拓扑矩阵、CPU Affinity、NUMA Affinity 与 GPU NUMA ID;用 numactl --hardware 查看每个 NUMA 节点的 CPU 列表与本地内存大小;用 lspci -tv 确认 PCIe 挂载拓扑;AMD 平台用 rocm-smi --showtopo。
A. 核心命令是 numactl --cpunodebind=N --membind=N,把 CPU 节点和内存节点一起锁死;taskset -c 只能锁 CPU 管不了内存。业界实测:8 卡 Llama-2-13B 训练 NUMA 感知分组绑定比全插单 CPU 提升约 31% 吞吐,多卡推理常见 20%~40% 提升。单卡或单 socket 场景收益有限,4 卡、8 卡张量并行场景收益最大。