GPU 集合通信与互联拓扑
通过 AllReduce、AllGather 与 ReduceScatter 理解梯度同步、参数分片,以及跨节点通信为何影响扩展效率。
学习目标
- 区分求和并分发、聚合分片和求和后分片。
- 用 rank 输入输出解释 AllReduce 的含义。
- 分析消息大小、拓扑和同步顺序对训练的影响。
学习前需要掌握
背景与问题
四卡单机训练扩展良好,换成两台机器后却变慢,可能是数据交换进入了更慢的网络路径。知道“使用 NCCL”仍不足以判断通信是否合理,还要知道每步交换什么、交换多大以及经过哪里。
概念定义
集合通信是同一进程组中多个 rank 协同执行的数据操作。AllReduce 对各 rank 的同形张量进行求和等归约并把完整结果给所有 rank;AllGather 把每个 rank 的分片汇集成完整张量;ReduceScatter 先归约,再让每个 rank 保留结果的一部分。
NCCL 是常用于 NVIDIA GPU 的集合通信库。通信语义、算法实现与物理互联是三个层次;同一 AllReduce 可采用不同算法,并不固定对应某一种拓扑。
直观理解
rank 0 的梯度是 [1, 3],rank 1 是 [5, 7]。求和 AllReduce 后两者都得到 [6, 10];若需要平均梯度,再除以 2 得到 [3, 5]。必须确认上层框架是否已做平均,避免再次除法。
核心原理
通信时间可粗略拆成启动延迟和数据传输时间。小消息更容易受延迟影响,大消息更容易受有效带宽影响;梯度分桶可以减少小消息数量,但过大的桶可能推迟通信开始,减少与计算重叠。
在理想 ring AllReduce 中, 个 rank 每个持有 byte,单 rank 发送的数据量约为 。这个流量估算不是延迟公式;它忽略拓扑、协议、链路竞争与实现选择,也不表示真实训练总通信量只有这么多。
TP 往往发生频繁的层内通信,通常优先放在高速互联域;跨节点 DP 也必须测量真实网络。设备编号相邻不保证物理路径相同,应核对 GPU、网卡和 NUMA 关系。
排查与实践步骤
- 用小张量验证各 rank 的输入、归约类型和输出符合预期。
- 核对驱动、通信库、进程组、网络接口与 rank 到设备映射。
- 分别测小消息和大消息,逐步从单节点扩大到多节点。
- 对比计算时间、通信时间和等待最慢 rank 的时间。
- 遇到挂起时检查所有 rank 是否以相同顺序参与 collective,以及是否有某个 rank 提前异常退出。
代码实现
下面模拟求和及平均语义,只在 CPU 上使用 Python 列表,不会调用 NCCL。
example.pyrank_gradients = [[1, 3], [5, 7]]
summed = [sum(column) for column in zip(*rank_gradients)]
averaged = [value / len(rank_gradients) for value in summed]
print(f"sum={summed}")
print(f"mean={averaged}")输出 sum=[6, 10] 与 mean=[3.0, 5.0]。
实际应用
- 理解 DDP 的梯度同步和分片训练中的参数聚合。
- 区分网络慢、负载不均和 collective 调用顺序不一致造成的等待。
常见错误
- 只在 rank 0 调用 collective,其他 rank 没有参与。
- 用理论网卡速率当作端到端有效吞吐,不考虑协议与拓扑。
- 在正常训练中频繁插入全局 barrier,破坏原本的计算通信重叠。
继续阅读
NCCL Collective Operations 给出了各类操作的输入输出语义。
推荐学习资料
NCCL Collective Operations
通过输入输出示意理解 AllReduce、AllGather、ReduceScatter 与各 rank 的通信语义。
NVIDIA · NVIDIA Docs