《从裸机到推理服务》· 05 / 12
同型号的卡、同版本的驱动、一字不差的命令,一台跑 250 GB/s,一台 108 GB/s------差别在卡与卡之间那几厘米。
预计 1 小时 | 前置:一台至少 4 卡的机器,装了 nccl-tests;单卡也能读,第一、二节照样成立
.13 刚救回来,驱动 535.129.03 装好,8 张卡全部认到。
跑一次单机 AllReduce,busbw 108 GB/s。
同一条命令在 .11 上是 250 GB/s。
这一篇的产出
一张拓扑矩阵的读法、一条单机 baseline 命令(以及它的输出该怎么读),和一句能说出口的判断:慢在卡之间,还是慢在网线上。
一、先读拓扑:8 张卡之间的路是怎么铺的
单机之内,GPU 之间的通信不走网线。
↳ NVLink:NVIDIA 自己的卡间直连通道,一条链路 25 GB/s 量级,一张卡上并 12 条。
↳ NVSwitch:机内的交换芯片,把 8 张卡两两全连。有了它,任意两张卡之间都是全速,不必绕路。
出机箱才轮到 02 篇那条参数面。所以「单机 8 卡」其实包含两段完全不同的路:卡与卡之间的一跳,和机器与机器之间的一跳。先看第一跳。
nvidia-smi topo -m 打出来的是这张路网图:
bash
nvidia-smi topo -m
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 CPU Affinity NUMA Affinity
GPU0 X NV12 NV12 NV12 NV12 NV12 NV12 NV12 0-31 0
GPU1 NV12 X NV12 NV12 NV12 NV12 NV12 NV12 0-31 0
GPU2 NV12 NV12 X NV12 NV12 NV12 NV12 NV12 0-31 0
GPU3 NV12 NV12 NV12 X NV12 NV12 NV12 NV12 0-31 0
GPU4 NV12 NV12 NV12 NV12 X NV12 NV12 NV12 32-63 1
GPU5 NV12 NV12 NV12 NV12 NV12 X NV12 NV12 32-63 1
GPU6 NV12 NV12 NV12 NV12 NV12 NV12 X NV12 32-63 1
GPU7 NV12 NV12 NV12 NV12 NV12 NV12 NV12 X 32-63 1
Legend:
X = Self
NV# = Connection traversing a bonded set of # NVLinks
NV12 是 12 条 NVLink 绑成一组:25.78 GB/s × 12 ≈ 310 GB/s。整张矩阵没有别的符号,说明任意两张卡都是直连,而且都是全带宽。
同一张表在 .13 上长得不一样:
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7
GPU0 X NV4 NV4 NV4 SYS SYS SYS SYS
GPU1 NV4 X NV4 NV4 SYS SYS SYS SYS
GPU2 NV4 NV4 X NV4 SYS SYS SYS SYS
GPU3 NV4 NV4 NV4 X SYS SYS SYS SYS
GPU4 SYS SYS SYS SYS X NV4 NV4 NV4
GPU5 SYS SYS SYS SYS NV4 X NV4 NV4
GPU6 SYS SYS SYS SYS NV4 NV4 X NV4
GPU7 SYS SYS SYS SYS NV4 NV4 NV4 X
两组各 4 张卡,组内 NV4(4 条,约 103 GB/s),跨组 SYS------要经 CPU 和 socket 之间的互联绕一圈。8 张卡装在同一个机箱里,实际是两个 4 卡小组。01 篇那句「同型号的两台机器,拓扑可能一台 NV12、一台 NV4」,在这里第一次见到实物。
符号的含义只有六个,认全就够用:
| 符号 | 含义 | 大致速度 |
|---|---|---|
NV# |
绑定的 # 条 NVLink | 最快,单卡可到 300 GB/s 级 |
PIX |
同一颗 PCIe switch | 一个 PCIe switch 内的上限 |
PXB |
跨 PCIe switch,但仍走板载桥 | 比 PIX 慢一档 |
PHB |
经 CPU 的 PCIe 桥 | 再慢一档 |
NODE |
同 NUMA 内跨 PHB | 接近 PHB |
SYS |
跨 NUMA、跨 socket,经 UPI | 最慢,且延迟最高 |
看矩阵只问两件事:最快的符号是什么,有没有 SYS。 有 SYS 就说明存在一段绕 CPU 的路,所有 AllReduce 都会经过它。
二、量出来:单机 8 卡 AllReduce
拓扑是设计值,数字才是事实。用同一批参数跑两台,只改卡数:
bash
all_reduce_perf -b 8 -e 8G -f 2 -g 8
# size count type redop root time algbw busbw #wrong #outs
# (B) (elements) (us) (GB/s) (GB/s)
8589934592 2147483648 float sum -1 60012.7 143.1 250.4 0 0
busbw 250 GB/s,对 8 卡 NVLink 的 310 GB/s 是 80%------全连机型的正常水平。algbw 143 是每张卡的有效吞吐,两者差 1.75 倍,就是 Ring 的冗余算法(03 篇点过这个口径)。
同一条命令在 .13 上:
8589934592 2147483648 float sum -1 139240.8 61.7 108.0 0 0
108 对 250 是 43%。这个比例和矩阵里的 SYS 对得上:组内跑得飞快,每轮都要跨组一次,跨组那一步把整体拖到一半以下。
| 机器 | 拓扑 | busbw | 相对 .11 |
|---|---|---|---|
| .11 | 全连 NV12 |
250 GB/s | --- |
| .13 | NV4 + SYS |
108 GB/s | 43% |
| .11 + .12 跨机(03 篇) | 16 卡,走参数面 | 181.6 GB/s | 73% |
表里第三行值得多看一眼:跨机的 181.6 比 .13 单机的 108 还高。 「单机一定比跨机快」是个错觉------快慢由路怎么铺决定,不由机器在不在一个机箱里决定。
三、差一个数量级:Ring 的流量账
Ring AllReduce 里,每张卡要发 2(n−1)/n 倍的数据量,8 卡是 1.75 倍。所以每张卡能发多快,直接决定整体速度,而它能发多快由最快那条路给死:
- 全连
NV12:310 GB/s 上限,实测 250; - 组内
NV4:103 GB/s 上限,组内满速,但每轮跨组一次; - 纯 PCIe Gen4 x16:32 GB/s 上限,实测 25 上下,比 NVLink 机型掉一个数量级。
这就是「8 张卡」这四个字不构成任何性能承诺的原因。判断一台机器能不能真的「当一张卡用」,不看卡数,看矩阵里最快的符号和有没有 SYS。
四、日志确认它走了哪条路
数字低的时候,要知道它到底走的是 NVLink、PCIe 还是主存。NCCL 自己会说:
bash
NCCL_DEBUG=INFO all_reduce_perf -b 8G -e 8G -g 8
node11:20431:20777 [0] NCCL INFO Channel 00/00 : 0 1 2 3 4 5 6 7
node11:20431:20777 [0] NCCL INFO via P2P/direct pointer
node11:20431:20777 [0] NCCL INFO NVLS multicast support is available on all ranks
node11:20431:20777 [0] NCCL INFO Connected all rings
via P2P/direct pointer 是卡之间直接读写对方显存,没经 CPU;NVLS multicast 是走 NVSwitch 的硬件多播,一次写多卡,比 Ring 再省一截带宽;Connected all rings 说明 8 个 rank 都进了环,没有掉队的。
反过来,日志里出现 via shared memory,就是数据被搬回主存中转------这条路比 NVLink 慢 5---10 倍,数字会难看得很明显。它通常不是配置错,而是 P2P 根本不可用,见坑三。
五、数字低,按这个顺序处置
从「改得动」排到「改不动」:先确认是不是设计如此,再查软件开关,最后才轮到换机器。
bash
nvidia-smi topo -p2p r
GPU0 GPU1 GPU2 GPU3
GPU0 X OK OK NS
GPU1 OK X OK OK
GPU2 OK OK X OK
GPU3 NS OK OK X
OK 是两张卡能直接读对方显存,NS 是不能。只要有一对 NS,NCCL 就会在这一对之间退回主存中转,整体数字跟着掉一截。
| 看到 | 判断 | 动作 | 归谁 |
|---|---|---|---|
矩阵最快是 NV4,或一片 SYS |
机型如此,不是故障 | 不修,改用法:8 卡任务拆成两个 4 卡任务,或让张量并行留在组内、数据并行跨组 | 采购 / 平台 |
日志里是 via shared memory |
P2P 没起来 | 用 topo -p2p r 找 NS,再查 IOMMU、ACS、Above 4G |
自己 → 交付 |
| 拓扑对,数字只到七成 | 进程跨了 NUMA | numactl --cpunodebind=0 --membind=0 绑到卡所在的节点 |
自己 |
| 同一台机器两次跑差很多 | 别的任务在抢卡或抢内存带宽 | 停掉其他负载,单任务复测 | 平台 |
| 换过驱动或内核之后才变慢 | 版本问题,不是硬件 | 回退到基线版本,把版本矩阵记进台账 | 自己 |
| 上面全排除 | 单卡本身掉速 | 跑 dcgmi diag -r 1,看是哪一张卡 |
厂商 |
回到第 1 篇那三种手:这一篇里最常见的处置其实是第三种------改预期。拓扑是买机器那天定下来的,运维手上没有开关;能做的是按它安排任务,把这个数字写进基线,下次有人说「这机器怎么这么慢」时,拿表出来说话。
六、最容易卡住的五处
坑一:拿 SXM 和 PCIe 机型对比
同样叫 A100、同样 80 GB,SXM 版有 NVLink、PCIe 版只有 PCIe。两台机器摆在一起,卡数、驱动、命令全一样,busbw 差近十倍。对比之前先看 nvidia-smi -q | grep -i "Product Architecture" 或直接看矩阵,别拿卡型号当结论。
坑二:矩阵里有 SYS,还按全连预期
看到 8 张卡就默认真能当一张用,是最常见的误判。先读矩阵,再设预期------有 SYS 的机器,8 卡 AllReduce 就该按「两个 4 卡小组」来估,而不是按全连估。
坑三:IOMMU / ACS 让 P2P 不可用
虚拟化开着、ACS 没关,GPU 之间不能直接访问显存,NCCL 只能退回 shared memory。表现是数字掉了一个数量级,日志里是 P2P is disabled 或 via shared memory。nvidia-smi topo -p2p r 能看到哪几对卡不支持。
坑四:只报 algbw 不报 busbw
algbw 是每张卡的有效吞吐,busbw 是链路上实际搬动的量。报数必须带口径,否则和 02 篇的链路带宽、03 篇的跨机数字都对不上,越对越糊涂。
坑五:拓扑对、数字还是低,没查绑核
进程跑到另一颗 CPU 的核上,数据要跨 UPI 才能到卡,模型再对也白搭。用 numactl --cpunodebind=0 --membind=0 把进程和卡绑在同一个 NUMA 节点上,再测一次。
七、怎么算做完了
验收标准
① 每台机器的
topo -m矩阵存档;② 单机 8 卡busbw有一个基线数字,并算出它占理论上限的百分比;③NCCL_DEBUG日志确认走的是P2P或NVLS,不是shared memory;④ 每台机器的拓扑类型记进台账(全连 / 分组 / 纯 PCIe)。
企业里这活归谁:整机验收由交付实施做,跨机那一段归网络工程师,NCCL 参数调优多数落在框架或平台侧。三方的数字要用同一个口径,否则验收会上各说各话。
下一篇 :三台机器各自能跑,怎么让它们变成一个资源池。k3s 三节点起步,装 GPU 设备插件,让 Pod 能要到卡------以及第一次遇到「卡明明空着,Pod 却一直 Pending」。
命令与判读方法整理自 NVIDIA NCCL 文档、nccl-tests 仓库、nvidia-smi 手册与 RDMA 运维社区帖。拓扑矩阵与数字为常见机型示例,实际以机器实测为准。如有错漏,欢迎指出。