4、单机多卡测试:使用 NCCL Tests 测试单机内 GPU 间 P2P 带宽(NVLink/NVSwitch),理解拓扑对带宽的影响
好,咱们进入实战环节。
单机多卡测试,说白了就是看看你机器里那几张 GPU 互相通信到底有多快。我见过不少团队,模型跑不起来就怪代码,结果一查,是 GPU 之间带宽没跑满。嗯,这节咱们就用 NCCL Tests 这把尺子,把带宽量明白。
4.1 为什么单机内 P2P 带宽这么重要?
你想想看,大模型训练时,数据并行、张量并行、流水线并行……哪个不得频繁地在 GPU 之间搬数据?
- 数据并行:每张卡算完梯度,得 AllReduce 一下。这步慢了,整卡都在等。
- 张量并行:每层计算拆到多张卡上,前向反向都得来回传激活值和梯度。
- 流水线并行:虽然通信量小点,但 stage 之间也得传数据。
所以,P2P 带宽就是多卡训练的命脉。带宽不够,算力再强也白搭。
核心观点:单机内 GPU 间 P2P 带宽,直接决定了多卡扩展效率的上限。
4.2 NCCL Tests 是什么?
NCCL Tests 是 NVIDIA 官方提供的一套微基准测试工具。它专门用来测试 NCCL(NVIDIA Collective Communications Library)的各种集合通信操作性能。
我个人习惯用它来验证三件事:
- 带宽是否达标:比如 A100 配 NVLink 3.0,理论单向 600 GB/s,实际能跑多少?
- 拓扑是否合理:同一台机器里,卡 0 和卡 1 快,卡 0 和卡 7 慢,为什么?
- 驱动/NCCL 版本是否正常:有时候升级了驱动,带宽反而掉了,一测便知。
4.3 编译与运行 NCCL Tests
先把它编译出来。过程很简单:
# 下载源码
git clone https://github.com/NVIDIA/nccl-tests.git
cd nccl-tests
# 编译(假设你已经装好了 CUDA 和 NCCL)
make
# 编译完成后,会生成一堆可执行文件,比如 all_reduce_perf, sendrecv_perf 等
小提示:如果你用的是容器,记得把 NCCL 库挂载进去。我遇到过有人编译好了,跑的时候报错找不到 libnccl.so,其实就是环境变量没配好。
编译完,咱们先跑个最简单的 点对点(P2P)带宽测试:
# 测试 GPU 0 到 GPU 1 的双向带宽
./sendrecv_perf -b 8 -e 128M -f 2 -g 0 -g 1
参数说明:
-b 8:起始消息大小 8 字节-e 128M:结束消息大小 128 MB-f 2:步进因子,每次翻倍-g 0 -g 1:指定 GPU 0 和 GPU 1
输出大概长这样:
# out-of-place in-place
# size count type redop root time algbw busbw
# (B) (elements) (us) (GB/s) (GB/s)
8 2 float none -1 12.34 0.00 0.00
16 4 float none -1 12.45 0.00 0.00
...
134217728 33554432 float none -1 210.45 637.89 637.89
注意看最后一行,busbw 就是实际的双向带宽。对于 A100 配 NVLink 3.0,单条链路双向 600 GB/s,这里跑到 637 GB/s 说明正常(因为还有 NVSwitch 的加成)。
4.4 理解拓扑对带宽的影响
为什么我要强调拓扑?
我举个例子。在 DGX A100 里,8 张 GPU 通过 6 个 NVSwitch 全互联。理论上任意两张卡之间带宽都一样。但如果你用的是普通服务器,比如 4 张卡插在 PCIe 上,那卡 0 和卡 1 可能走同一个 PCIe Root Complex,带宽高;卡 0 和卡 3 可能要走 QPI 跨 CPU,带宽直接砍半。
咱们用 nvidia-smi topo -m 看看拓扑:
nvidia-smi topo -m
输出示例:
GPU0 GPU1 GPU2 GPU3 GPU4 GPU5 GPU6 GPU7 mlx5_0
GPU0 X NV1 NV1 NV2 NV2 SYS SYS SYS NODE
GPU1 NV1 X NV1 NV2 NV2 SYS SYS SYS NODE
GPU2 NV1 NV1 X NV2 NV2 SYS SYS SYS NODE
GPU3 NV2 NV2 NV2 X NV1 SYS SYS SYS NODE
GPU4 NV2 NV2 NV2 NV1 X NV1 SYS SYS NODE
GPU5 SYS SYS SYS SYS NV1 X NV1 NV2 NODE
GPU6 SYS SYS SYS SYS NV1 NV1 X NV2 NODE
GPU7 SYS SYS SYS SYS NV2 NV2 NV2 X NODE
这里 NV1、NV2 表示通过 NVLink 连接,SYS 表示要通过系统总线(PCIe/QPI)。你看 GPU0 和 GPU1 是 NV1,带宽高;GPU0 和 GPU5 是 SYS,带宽低。
避坑指南:我曾经遇到过一台机器,跑 AllReduce 总是比预期慢 30%。查了半天,发现是两张卡虽然物理上插在同一个 CPU 的 PCIe 槽上,但 BIOS 里把 PCIe 拆分成了 x8 模式。带宽直接减半。所以,测完拓扑后,别忘了用 nvidia-smi -q -d PCI 确认一下链路宽度。
4.5 全互联带宽测试:AllReduce
点对点测完了,咱们再测一下集合通信。最常用的就是 AllReduce:
# 测试 8 卡 AllReduce 带宽
mpirun -np 8 ./all_reduce_perf -b 8 -e 128M -f 2 -g 8
这里 -g 8 表示使用 8 张 GPU。输出会显示不同消息大小下的带宽。重点关注大消息(比如 128MB)的带宽,因为大模型训练时梯度同步都是大块数据。
我一般会对比两个值:
- 理论峰值:比如 8 卡 A100,NVLink 全互联,理论 AllReduce 带宽约 600 GB/s * (8-1)/8 ≈ 525 GB/s(算法开销)。
- 实测值:如果实测只有 400 GB/s,那就要排查了——是 NCCL 算法没选对?还是拓扑有瓶颈?
4.6 用 SVG 图理解拓扑与带宽的关系
下面这张图,是我自己总结的「单机多卡带宽测试决策流程」。你照着走一遍,基本不会漏掉关键点。
4.7 常见问题与排查思路
跑完测试,如果发现带宽不对,别慌。我按优先级列一下排查步骤:
- 检查 NCCL 版本:
nccl-tests --version。太老的版本可能有 bug。 - 检查环境变量:
NCCL_DEBUG=INFO打开调试日志,看它选了哪个算法(Ring 还是 Tree)。Ring 适合小消息,Tree 适合大消息。 - 检查 GPU 频率:
nvidia-smi -q -d CLOCK。如果 GPU 降频了,带宽也会掉。 - 检查 NUMA 绑定:用
numactl --cpunodebind=0 --membind=0把进程绑到和 GPU 同一个 NUMA 节点上。跨 NUMA 访问会损失带宽。
一句话总结:单机多卡测试,先看拓扑,再测 P2P,最后跑 AllReduce。每一步都对照理论值,偏差超过 10% 就要深挖。
好了,这节的内容就到这。你按这个流程走一遍,基本能把单机内的 GPU 带宽摸得清清楚楚。