Netpath
学习路径22 / 49 · 高性能网络看全程 →
实验预计 40 分钟

动手:NCCL 与 busbw 判读

AllReduce 的 busbw 是 GPU 集群的网络体检报告。这节讲怎么跑、怎么读、怎么调。

学完这节你能做到

  • 跑通多节点 all_reduce_perf 并确认正确性检查通过
  • 用 2(n-1)/n 校正因子把 busbw 与硬件峰值对比
  • 按机型配对 NCCL_IB_HCA、NCCL_SOCKET_IFNAME 等关键变量

建议先学

跳过这几节会看不懂本节的部分推导

busbw 是 GPU 集群的体检报告

perftest 验证的是"某一段是对的":两张卡之间点对点能跑满线速。 但它测不出多机多卡同时通信时的表现。

NCCL 的 AllReduce 测试补上这一环。它一个数字就能覆盖:网卡、PCIe、NVLink、链路速率、 rail 布线、拥塞控制、参数配置——任何一处错都会反映在 busbw 上。

所以它既是调优工具,也是交付验收的最终指标。

先算理论峰值

不知道目标值,测出来的数字没有意义。公式很简单:

理论峰值 busbw = 每节点计算网卡数 × 单口带宽 ÷ 8
配置计算理论峰值
8 × 400 Gb(NDR)8 × 400 ÷ 8400 GB/s
8 × 200 Gb(HDR)8 × 200 ÷ 8200 GB/s
4 × 200 Gb4 × 200 ÷ 8100 GB/s

判定标准(来自实践口径):

busbw / 理论峰值评级
≥ 90%优秀
80–90%可接受
低于 80%异常,必须查

两个任务,顺序不能反

任务一:单次测试任务二:压力测试
参数-b 8 -e 8G -f 2 -g 1-b 8G -e 8G -i 0 -g 1
数据量8 B 到 8 GB 翻倍扫描固定 8 GB 循环
行为扫完自动退出持续跑,手动停
目的正确性 + 性能曲线满载长稳

先做任务一,通过了再做任务二。 正确性没过就调性能是白费功夫。

-i 0 让数据量不递增,配合 -b/-e 相等,程序就会一直循环输出同一个尺寸的结果。

关键环境变量

配错任何一个,结果都会失真。分两类看。

所有机型一致的

变量值作用
NCCL_ALGORing固定算法,确保多次测试可比
NCCL_IB_DISABLE0启用 IB/RoCE;设 1 会回退 TCP,带宽暴跌
NCCL_IB_QPS_PER_CONNECTION8每连接 8 个 QP,提升带宽利用率
NCCL_DEBUGWARN(调试时 INFO)INFO 日志很大,压测时会触发轮转

按机型/网络差异的

变量典型值选错的后果
NCCL_IB_HCAmlx5 或显式列全走错卡或少用卡,带宽按比例掉
NCCL_SOCKET_IFNAMEbond0 / bond1 / eth0节点间 TCP 控制通道不通,连不上
NCCL_IB_TC106(RoCE)不设则 RoCE 流量没有优先级标记,拥塞时被丢
NCCL_CROSS_NIC1(多网卡)不启用跨卡聚合,带宽利用率低
UCX_NET_DEVICESbond1 / eth0UCX 选错网卡,hcoll 初始化失败
!NCCL_IB_HCA 是最容易出错的一个

它决定 NCCL 用哪几张网卡。三种写法:

NCCL_IB_HCA=mlx5                    # 前缀匹配,所有 mlx5* 设备
NCCL_IB_HCA=mlx5_0,mlx5_1,...       # 显式列举(要列全!)
NCCL_IB_HCA=^=mlx5_4                # 排除某个设备

从别的机型抄配置是最常见的事故来源:4 卡机型的配置搬到 8 卡机器上, 就只用了一半网卡,busbw 正好掉一半。 AllReduce 闯关 那一关练的就是这个。

设备名用 ibdev2netdev 核对,别猜。

调试时才开的

NCCL_DEBUG=INFO
NCCL_DEBUG_SUBSYS=INIT,NET,GRAPH     # 只看初始化、网络选择、拓扑

集合通信原语

AllReduce 是最常用的(数据并行的梯度同步就是它),但要理解 busbw 得先知道它做了什么:

原语做什么用在哪
AllReduce所有卡的数据求和,结果发回所有卡数据并行的梯度同步
AllGather每张卡的数据拼起来发给所有卡张量并行、ZeRO
ReduceScatter求和后按分片发给各卡ZeRO、张量并行
AllToAll每张卡给每张卡发不同数据MoE 专家并行,对网络最苛刻

Ring AllReduce 的实现是 ReduceScatter + AllGather 两阶段, 每张卡都要收发 2(n-1)/n 倍的数据——这正是下面校正因子的来源。

busbw 与 algbw:为什么要两个指标

测试输出有两列带宽,很多人只看后面那个而不知道区别:

#      size    count    type   redop   time   algbw   busbw #wrong
    8589934592 2147483648 float sum  84744.2  101.36  190.05      0
指标含义特点
algbw数据量 ÷ 耗时随 GPU 数变化,不能直接和硬件比
busbwalgbw × 校正因子可以直接和硬件峰值比

AllReduce 的校正因子是:

busbw = algbw × 2(n − 1)/n        n = GPU 总数

16 卡时:2 × 15 / 16 = 1.875,所以 101.36 × 1.875 ≈ 190.05。

i校正因子的意义:让不同规模可比

n 越大,因子越接近 2。这样设计的目的是消除 GPU 数量对指标的影响:

  • 16 卡集群 busbw 380 GB/s
  • 128 卡集群 busbw 375 GB/s

这两个数字可以直接比较,也都能直接和"8 × 400 Gb ÷ 8 = 400 GB/s"对照。 如果只看 algbw,规模一变数字就变,没法判断好坏。

所以验收看 busbw,别看 algbw。

跑一次完整测试

以 Kubeflow Trainer 提交为例(配置来自实践仓库):

kubectl apply -f nccl-tests-b200.yaml

# 看完整日志(含 NCCL 初始化,--tail=-1 别省)
kubectl logs -l trainer.kubeflow.org/replicated-job-name=launcher -f --tail=-1

完成标准(三条都要过)

# Out of bounds values : 0 OK        ← 正确性
# Wrong values : 0 OK                ← 正确性
峰值 busbw ≥ 理论峰值 × 90%           ← 性能

扩展到 N 个节点要改两处

# numNodes 与 mpirun 的 -np 必须同时改,-np = 节点数 × 每节点 GPU 数
sed -e 's/numNodes: 2/numNodes: 4/' \
    -e 's/-np 16/-np 32/' \
    nccl-tests-b200.yaml > nccl-tests-4node.yaml
diff nccl-tests-b200.yaml nccl-tests-4node.yaml     # 改完先看 diff

只改一处是高频错误:numNodes 改了但 -np 没改, 结果只有一部分 GPU 参与,busbw 看起来"莫名偏低"。

转成压测

sed 's|all_reduce_perf -b 8 -e 16G -f 2 -g 1|all_reduce_perf -b 8G -e 8G -i 0 -g 1|' \
    nccl-tests-b200.yaml > nccl-tests-stress.yaml
kubectl apply -f nccl-tests-stress.yaml
# 跑够时长后手动停
kubectl delete -f nccl-tests-stress.yaml

判读日志:三行定生死

kubectl logs <launcher-pod> | grep -E 'NET/|Using network|GPU Direct'
NCCL INFO NET/IB : Using [0]mlx5_0:1/RoCE [1]mlx5_1:1/RoCE ... [7]mlx5_7:1/RoCE
NCCL INFO NET/IB : GPU Direct RDMA Enabled for HCA 0 'mlx5_0'
NCCL INFO Using network IB
看什么期望不对说明什么
NET/IB 还是 NET/SocketNET/IBNET/Socket = 回退 TCP,带宽差一个量级
列出的 HCA 数量等于每节点网卡数少了 = NCCL_IB_HCA 漏了卡
GPU Direct RDMA Enabled有这一行没有 = 显存要经主存中转

这三行是排查 busbw 偏低的第一站,比调参数快得多。

busbw 偏低的排查顺序

busbw 不达标
   │
   ├─ 只剩几分之一(低于 30%)
   │     → 日志找 NET/Socket:回退 TCP 了
   │       查 NCCL_IB_DISABLE、NCCL_IB_HCA 设备名、Pod 内 ibv_devinfo 是否看得到设备
   │
   ├─ 约一半
   │     → ① NCCL_IB_HCA 少列了卡(日志里数一下)
   │       ② 端口速率降级(ibstat | grep Rate)
   │       ③ rail 布线接错(ibnetdiscover 核对)
   │       ④ numNodes 改了但 -np 没改
   │
   ├─ 约 60%
   │     → PCIe 代际/lane 不够(lspci LnkSta,400G 要 Gen5 x16)
   │
   ├─ 80–90% 且抖动
   │     → RoCE 无损配置:PFC 计数是否暴涨、NCCL_IB_TC 是否设置
   │
   └─ 单点异常(某几张卡慢)
         → NVLink 掉链(nvidia-smi topo -m 看 NV 数是否一致)
           或 BER 恶化(perfquery -a)
✓把它做成基线而不是一次性测试

最有价值的用法不是交付时测一次,而是存一份基线,之后定期复测对比:

# 每次测试后记录关键字段
date +%F,$(kubectl logs <launcher> | awk '/Avg bus bandwidth/{print $NF}')

理由是这一层的故障大多是静默退化:NVLink 掉一条链、光模块 BER 慢慢恶化、 某次维护后网卡插到了错误槽位。它们都不报错,只让 busbw 掉几个百分点。

有基线才能发现"上周 385、这周 340"。没基线就只能等业务抱怨训练变慢。

检查点

Checkpoint单选

8 卡 × 400 Gb 的节点,16 卡测试算出 algbw 101 GB/s、busbw 190 GB/s。这套集群健康吗?

Checkpoint单选

日志里出现 NET/Socket 而不是 NET/IB,意味着什么?

Checkpoint单选

为什么建议保存 busbw 基线并定期复测?

这节课的落点

  • 理论峰值 = 网卡数 × 单口带宽 ÷ 8;判定线:≥90% 优秀、80–90% 可接受、低于 80% 必查
  • 两个任务顺序不能反:单次扫描测正确性与曲线,通过后再做固定尺寸压测
  • 固定 NCCL_ALGO=Ring 保证可比;NCCL_IB_DISABLE=0 否则回退 TCP
  • NCCL_IB_HCA 是最容易出错的一个,从别的机型抄配置会少用卡
  • 验收看 busbw 不看 algbw;busbw = algbw × 2(n−1)/n,因子的作用是消除规模影响
  • 扩节点要同时改 numNodes 与 -np,只改一处会让部分 GPU 不参与
  • 日志三行定生死:NET/IB vs NET/Socket、列出的 HCA 数量、GPU Direct RDMA Enabled
  • 偏低比例即线索:几分之一 = 回退 TCP、一半 = 少卡/降速/接错 rail、60% = PCIe 不够
  • 存基线定期复测,这一层的故障几乎都是静默退化

延伸资料

  • k8s-in-actionai/nccl-tests/quickstart.md
  • k8s-in-actionai/nccl-tests/config-reference.md