CUDA系统拆解-18-NCCL、多GPU与通信隐藏:规模扩展怎么不被通信拖垮

本文是「CUDA系统拆解」系列第 18 篇。
系列导读:CUDA系统拆解-00-导读:从编程模型到 AI 推理系统的学习路线
上一篇:CUDA系统拆解-17-vLLM、TensorRT-LLM 与 Continuous Batching:CUDA 为什么最终连到推理系统

1. 这篇解决什么问题

这一篇讲的是:当系统从单 GPU 走向多 GPU 之后,性能问题为什么会突然变成“通信问题”。

核心要回答 5 件事:

  • 多 GPU 新增的核心矛盾到底是什么。
  • NCCL 到底处在什么位置,为什么它几乎是 NVIDIA 多 GPU 系统的默认基础设施。
  • BroadcastAllReduceAllGatherReduceScatter 这些 collective 为什么重要。
  • 为什么 ring、拓扑感知和链路选择会直接影响扩展效率。
  • 为什么 overlappipeline、通信隐藏常常决定大模型系统最终能跑多快。

如果这篇只记住一句话,那就是:

单卡优化主要在回答“数据怎么在一张卡里少搬”,多卡优化主要在回答“数据怎么在多张卡之间尽量少搬、尽量晚搬、尽量边搬边算”。

2. 先记住的核心结论

  • 多 GPU 引入的新核心矛盾通常不是“算力不够”,而是“跨卡通信太贵”。
  • NCCL 是 NVIDIA 的高性能 GPU collective communication 库,重点不是只提供 API,而是把拓扑感知、通信算法和 CUDA 执行模型结合起来。
  • BroadcastAllReduceAllGatherReduceScatter 是多 GPU 系统里最常见的 collective,分别对应不同的数据分发、聚合和收集需求。
  • AllReduce 是最经典的多 GPU 同步原语之一,因为它代表了“大家先规约,再都拿到最终结果”这类高频需求。
  • ring 常见是因为它对大消息传输带宽利用率很好;但并不存在永远最优的单一算法,拓扑和消息规模会影响选择。
  • overlap 的本质是让通信时间和计算时间重叠,尽量把等待藏进已有计算里,而不是让通信成本真的消失。
  • pipeline 的本质是分阶段并行;bubble 是流水线没被填满带来的空闲。
  • 在大模型训练和推理里,很多最终性能差距都来自通信组织质量,而不是单个 kernel 的峰值。

3. 正文讲解

3.1 多 GPU 的本质矛盾是什么

一张 GPU 上,性能问题主要集中在:

  • 算子组织
  • 访存层级
  • launch 开销
  • 同步和调度

到了多 GPU,问题突然多出一层:

每张卡本地算得很快,但卡和卡之间交换数据很慢。

这就是多 GPU 最核心的新矛盾。

从代价层级看,可以粗略理解成:

  • registers / shared memory 最快
  • 本地 HBM 也很快
  • 同机其他 GPU 更慢
  • 跨机 GPU 更慢

所以多 GPU 优化和单 GPU 优化在逻辑上其实是一脉相承的:

  • 单 GPU:尽量少访问慢层级
  • 多 GPU:尽量少做跨卡交换

3.2 NCCL 到底是什么

NCCL 是 NVIDIA 提供的高性能 GPU collective communication 库。它要解决的不是“怎么写一个 kernel”,而是“多张 GPU 怎么高效交换数据”。

它最重要的价值不只是接口,而是三件事:

  • 提供常见 collective communication 原语
  • 感知硬件拓扑并选择更合适的通信路径
  • 和 CUDA stream、异步执行模型配合,方便做 overlap

所以理解 NCCL 时,不要把它只看成“多卡 API 集合”。更准确地说,它是 GPU 世界里的通信执行基础设施。

3.3 什么是 collective communication

所谓 collective,可以先理解成:

一组 GPU 按某种固定规则一起交换和重组数据。

最常见的几类是:

Broadcast

  • 一个 GPU 把数据发给所有 GPU
  • 常见于参数分发、初始化、共享配置同步

AllReduce

  • 每个 GPU 各有一份数据
  • 先按元素规约,比如 sum
  • 再把最终结果发回所有 GPU
  • 这是最经典的 collective 之一

AllGather

  • 每个 GPU 提供自己的一块数据
  • 所有 GPU 最终都拿到拼接后的完整结果
  • 常见于张量并行后的结果收集

ReduceScatter

  • 先做规约
  • 再把规约后的结果分块散给不同 GPU
  • 很适合那些不需要所有 GPU 都拿完整结果的场景

这些 collective 之所以重要,是因为多 GPU 系统几乎总要在“本地算一部分”和“全局拼结果”之间来回切换。

3.4 为什么 AllReduce 这么重要

如果只记一个 collective,那通常先记 AllReduce

原因很简单:它代表了多 GPU 协作里最经典的一类需求。

它同时做两件事:

  • 聚合
  • 分发

所以它经常很贵,也经常是扩展瓶颈。
训练里它典型对应梯度同步;推理里在某些张量并行和中间结果合并路径上,也会遇到类似问题。

面试里如果问“为什么多卡不线性加速”,一个高频原因就是:
本地计算虽然分摊了,但每一层或每一步都可能新增 collective 成本。

3.5 为什么 ring 这么常见

ring 是最常见的 collective 实现直觉之一,尤其适合大消息场景。

直观上可以理解成:

  • GPU 组成一个环
  • 每轮每张卡只和相邻卡交换一块数据
  • 数据一边转、一边规约或收集

它的优势主要是:

  • 链路利用比较均衡
  • 更容易把所有链路都忙起来
  • 对大消息时的带宽利用通常不错

但不要把它神化成唯一正确答案。
真实系统里还会有:

  • tree
  • hierarchical
  • 拓扑感知的混合策略

选择和这些因素有关:

  • 消息大小
  • GPU 数量
  • NVLink / PCIe / IB 拓扑
  • 单机还是跨机

所以 NCCL 的强点之一,就在于它不是死板地用一种算法,而是会结合拓扑和场景做更合理的选择。

3.6 为什么拓扑感知非常关键

多 GPU 性能不只取决于“用了几张卡”,还取决于“这些卡之间到底怎么连”。

例如:

  • 有的 GPU 之间有 NVLink
  • 有的只能走 PCIe
  • 跨机要走 IB 或其他网络
  • 某些链路可能共享交换资源

如果通信路径选得不好,就会出现:

  • 热点链路
  • 带宽利用不均
  • 某些 GPU 长时间等待

所以 NCCL 的价值很大一部分在于:
它在做 topology-aware 的通信组织,而不是只在做“把字节发出去”。

3.7 overlap、pipeline、通信隐藏是什么意思

这几个词经常一起出现,但不是一回事。

overlap

  • 指通信和计算重叠
  • 理想目标是让总时间更接近 max(计算时间, 通信时间),而不是两者简单相加

pipeline

  • 指把系统拆成多个阶段,让不同阶段并行推进
  • 常见于 pipeline parallel,也常见于更一般的数据流分阶段组织

communication hiding

  • 指尽量把通信等待藏进已有的计算时间里
  • 它依赖 overlap,但不等于“通信成本不存在”

这三者的重要性在于:
在多 GPU 系统里,最怕的不是“有通信”,而是“通信裸露在关键路径上,所有卡都在等”。

3.8 pipeline bubble 是什么

一旦谈 pipeline,就绕不开 bubble

bubble 可以理解成:

  • 流水线没有被填满时产生的空闲
  • 或者不同 stage 不平衡导致的等待

典型来源包括:

  • stage 切分不均衡
  • micro-batch 太少
  • 某些阶段明显更慢

所以 pipeline 的关键不只是“分阶段”,还包括:

  • 阶段怎么切
  • 每阶段负载是否均衡
  • 是否有足够多的 micro-batch 来填满流水线

3.9 为什么多 GPU 常常不线性加速

这是最常见的系统问题之一。

多卡扩展不线性的典型原因包括:

  • 通信占比上升
  • 通信无法被完全隐藏
  • 切分过细,导致本地计算太碎
  • launch 和同步开销比例上升
  • 拓扑不理想
  • 负载不均衡

所以多 GPU 扩展效率的本质问题是:

卡虽然变多了,但“等待”的部分也变多了。

3.10 和并行策略的关系

通信模式不是独立存在的,它通常由并行切分方式决定。

常见直觉是:

  • data parallel 更容易出现梯度同步类通信
  • tensor parallel 经常需要 AllGatherReduceScatterAllReduce
  • pipeline parallel 更强调相邻 stage 之间的传递和流水线组织
  • expert parallel / MoE 会更敏感于 token 分发和跨设备交换

这意味着真正分析一个多 GPU 系统时,不应该先问“用了什么 collective”,而应该先问:

  • 模型怎么切
  • 数据怎么切
  • 每一步的通信到底发生在哪里

4. 和 AI 推理的关系

对大模型推理来说,这一篇非常关键,因为推理尤其容易暴露通信问题。

原因之一是 decode 阶段往往本地计算时间短:

  • 每步新增计算量小
  • kernel 更碎
  • launch 更频繁
  • 于是通信更难被隐藏

所以在大模型推理里,很多工程动作最终都在围绕同一个目标:

  • 减少每步必须做的跨卡同步
  • 尽量让通信和计算重叠
  • 让切分、调度、KV locality 和通信路径尽量一致

这也是为什么 AI infra / 推理岗位会非常看重你对 NCCLcollectiveoverlappipeline bubble 的理解。它们直接决定多卡系统能不能真正跑出吞吐。

5. 常见误区

  • NCCL 不只是“多卡 API 库”,它的真正价值在于 topology-aware 通信和与 CUDA 执行模型的集成。
  • overlap 不是让通信成本消失,而是尽量把通信等待藏进已有计算里。
  • ring 不是永远最优,只是它在很多大消息场景下带宽利用很好。
  • 多 GPU 不线性加速不一定是 kernel 太慢,很多时候是通信、同步或负载不均衡在拖后腿。
  • pipeline 不等于自动高效;如果 stage 切分差或 bubble 大,流水线本身也会浪费很多时间。

6. 复习自测

  • 为什么说多 GPU 新增的核心矛盾通常是通信成本,而不是单纯算力不足?
  • NCCL 解决的核心问题是什么?为什么不能只把它理解成一组 API?
  • BroadcastAllReduceAllGatherReduceScatter 各自更适合哪类数据流场景?
  • 为什么 AllReduce 经常成为多 GPU 扩展瓶颈?
  • ring 为什么常见?它的优势和局限分别是什么?
  • 为什么拓扑感知会直接影响多 GPU 性能?
  • overlappipelinecommunication hidingbubble 分别在讲什么?
  • 为什么 decode 阶段的多 GPU 推理往往比 prefill 更容易暴露通信问题?

系列导航