# 第 10 章 万卡对答案：通信与软件栈

> 本章问题：集合通信、NVLink、RDMA、CUDA、NCCL、Kubernetes 各干什么？
> 来源：https://llm.weiborao.link/#ch10

**上一章留下的问题：**拆开之后，GPU 之间要不停地“对答案”。上万块卡怎样对答案，才不至于把大部分时间都花在等待上？

一万块 GPU 拼在一起，并不自动等于一块“一万倍快”的 GPU。中间隔着一个问题：**它们怎样高效地交流**。本章先讲交流的方式（集合通信），再讲交流的通道（NVLink 与 RDMA），最后讲指挥这一切的软件（CUDA、NCCL、Kubernetes），并把第 6 章那次推理在整座工厂里重走一遍。

### 10.1 对答案：计算、交换、汇总

以数据并行为例：每块卡用自己那份数据算出一份梯度（“旋钮该往哪拧”），但真正要用的是所有卡梯度的**平均值**。于是每一步训练都是同一个循环：各自计算 → 互相交换 → 汇总平均 → 一起更新 → 下一步。

> 【图示】训练中“计算—交换—汇总”的循环：四块 GPU 各自计算，然后一起交换梯度。GPU 3 慢了一点，其他三块只能空等，整步时间由最慢的那块决定，这就是木桶效应。

**图 10-1** 同步训练的木桶效应。只要有一块卡、一条链路慢了一点，其他所有卡都得空等。规模越大，“总有一个慢的”的概率越高。

问：一万块卡里，只有一块慢了 10%，影响大吗？

答：很大。同步训练里，每一步都要等最后一块卡交卷。那一块的计算慢 10%，整个集群每一步的计算阶段也跟着慢 10%。

问：如果是网络里某条链路偶尔堵一下呢？

答：一样。哪怕平均很快，只要偶尔有一次慢，那一步所有卡都在等。所以 AI 网络最在意的是**尾部延迟**，也就是“最慢的那几次有多慢”。

### 10.2 AllReduce：圆桌传纸条

“每块卡都拿到所有卡数据的总和”，这个操作叫 **AllReduce**（全归约），是 AI 训练里最常用的集合通信操作。最笨的做法是：所有卡把数据发给一个“组长”，组长加总后再发回去。组长的网络会被挤爆。

聪明的做法是**环形 AllReduce**：所有卡围成一圈，每块卡只和左右邻居说话。

> 【图示】环形 AllReduce：四块 GPU 围成一圈，每块的数据分成四份。前三步“归约”，每步每块卡把一份传给下一块并累加，三步后每块卡各自拥有一份完整的总和；后三步“广播”，把完整的那份沿环传下去。六步后，每块卡都有了全部四份的总和。

1. **起点：**每块卡把自己的数据分成 4 份，每份都只有自己的贡献（1）。
2. **归约 1：**每块卡把其中一份传给下一块，对方加到自己那份上。
3. **归约 2：**继续沿环传、继续累加。
4. **归约 3：**每块卡各自有一份累加满了 4 块卡的数据。
5. **广播 1：**把完整的那份沿环传给下一块。
6. **广播 2：**继续传。
7. **广播 3：**每块卡的 4 份都是完整总和，AllReduce 完成。

**图 10-2** 4 块卡的环形 AllReduce，6 步完成。每一步，每块卡同时在发和收，所有链路都在干活，没有谁闲着，也没有谁被挤爆。网页版可以逐步播放。

类比：

环形 AllReduce 像**圆桌传纸条**：要算全桌人的零花钱总数，不必都交给班长，而是每人把纸条传给右手边的人，对方加上自己的数再往下传。转完一圈，每人手里都有了总数。

环形算法有一个漂亮的性质：每块卡要发送的数据量约为 2 × (N − 1) / N 倍的原始数据，N 越大越接近 2 倍，**几乎不随卡数增加**。8 块卡时是 1.75 倍。

**停一下：既然每块卡发的数据量几乎不变，环形 AllReduce 是不是可以无限扩展？**

**参考答案：**

不能。数据量不变，但**步数**随卡数线性增加：N 块卡要走 2(N − 1) 步，每一步都有固定的延迟。几千块卡时，光是“一步一步传”的等待就很可观。所以实际的通信库还会用树形等算法，或者先在服务器内部汇总、再跨服务器汇总，按规模和消息大小自动选择。

还有一种更激进的思路：**让交换机来做加法**。既然 AllReduce 的本质是“把大家的数加起来”，何必让数据在 GPU 之间来回传？NVIDIA 在 InfiniBand 交换机里实现的 **SHARP**（可扩展分层聚合与归约协议）就是这样做的：数据在经过交换机时就被就地聚合，越往上走，网络里需要传的数据越少，集合通信的时间随之缩短。这类做法统称**网内计算**。

除了 AllReduce，常用的集合通信还有 AllGather（每人把自己那份分享给所有人）、ReduceScatter（汇总后每人只拿一部分）、Broadcast（一人发给所有人）和 AlltoAll（每人给每人发一份不同的东西，混合专家模型很依赖它）。

### 10.3 两级高速公路：NVLink 与 RDMA

对答案要走“路”。AI 集群里有两级路，速度差很多。

> 【图示】两级高速公路：服务器内部，8 块 GPU 通过 NVLink 和 NVSwitch 互联，H100 每块卡 900 GB/s；服务器之间，每块 GPU 配一块网卡，经交换机用 RDMA 通信，400 Gb/s 约等于单向 50 GB/s；NVLink 900 GB/s 是双向口径，同口径比较约快 9 倍。

**图 10-3** 服务器内部走 NVLink，服务器之间走网卡和交换机。H100 的 NVLink 每卡 900 GB/s（收发合计的双向口径，单向约 450 GB/s），一块 400 Gb/s 网卡单向约 50 GB/s；按同一口径比较，机内比机间快约 9 倍，接近一个数量级。所以并行策略会把通信最频繁的张量并行放在服务器内部。

**读带宽数字之前，先认清两种单位**　网络行业用**比特**：Gb/s、Tb/s，网卡、交换机端口都这样标（400G 就是 400 Gb/s）。GPU、显存、存储行业多用**字节**：GB/s、TB/s。1 字节 = 8 比特，所以 400 Gb/s ≈ 50 GB/s。另一个陷阱是方向：网卡的 400 Gb/s 指每个方向各 400 Gb/s；NVIDIA 给 NVLink 标的 900 GB/s 是收发合计的双向数字。比较两个带宽之前，先把单位和方向换成同一口径。本书后面凡是 Gb/s，都是比特；GB/s 都是字节。

**服务器内部：NVLink。**在普通电脑里，两块显卡交换数据要经过主板上的 PCIe 通道，甚至绕道 CPU 内存。NVIDIA 的 NVLink 让 GPU 之间直接相连，配合 NVSwitch 芯片，一台服务器里的 8 块卡可以两两高速通信。Blackwell 一代把每卡带宽提到 1.8 TB/s（双向）；GB200 NVL72 更进一步，把整整一个机柜的 72 块 GPU 连成一个 NVLink 域，总带宽 130 TB/s，让它们像一块巨大的 GPU。

**服务器之间：RDMA。**跨服务器就得走网络。传统网络通信要经过操作系统层层处理：数据从显存拷到内存，再交给网络协议栈打包，对方再一层层拆开。**RDMA**（远程直接内存访问）让一台机器的网卡直接读写另一台机器的内存，不需要两端的 CPU 和操作系统插手；配合 **GPUDirect RDMA**，网卡甚至可以直接读写 GPU 显存。

类比：

传统网络通信像**寄快递**：打包、送到快递站、分拣、运输、再分拣、派送、拆包，每一站都要人经手。RDMA 像在两个车间之间架了一条**直达传送带**：东西从这边的货架直接滑到那边的货架上，中间没有人碰。

RDMA 有两种主流的“传送带”：一种是 **InfiniBand**，专为高性能计算设计的网络；另一种是 **RoCEv2**，把 RDMA 搬到以太网上。它们的区别，以及以太网为此要做的改造，是下一章的主题。

### 10.4 软件栈：谁在指挥

硬件连好了，还要有软件告诉每一块卡做什么、什么时候和谁说话、哪个任务用哪些卡。

> 【图示】AI 工厂的软件栈，从上到下：AI 应用与框架（PyTorch、推理引擎）；通信库 NCCL；CUDA 与驱动；编排与调度（Kubernetes、Slurm、Run:ai）横跨所有层；最底层是 GPU、NVLink、网卡、交换机和存储。右侧是工厂类比。

**图 10-4** AI 工厂的软件栈与工厂类比。CUDA 是工人的语言，NCCL 是物流调度，Kubernetes 等编排系统是厂长和人事部。

#### CUDA：工人的语言

GPU 上的上万个计算单元听不懂普通程序。NVIDIA 在 2006 年推出 **CUDA**，2007 年 6 月发布 1.0 工具包，让程序员可以用 C/C++ 这类语言，把一个计算拆成成千上万个线程，分配给 GPU 去执行。今天的 PyTorch 等框架在底层调用的，就是用 CUDA 写成、为矩阵乘法等操作高度优化的程序（叫“内核”）。没有它，GPU 只是一堆不会干活的硅片。

#### NCCL：物流调度

**NCCL**（NVIDIA 集合通信库）负责 10.1 和 10.2 讲的那些“对答案”操作。它的官方定义是“拓扑感知的 GPU 间通信原语”：先看清楚机器怎么连的——哪两块卡之间有 NVLink、哪块卡离哪块网卡最近——再为每次通信挑最快的路线和算法（环形、树形等）。同一台服务器内走 NVLink，跨服务器走网卡上的 RDMA。上层框架只要说一句“AllReduce”，剩下的都交给它。

#### Kubernetes 等编排系统：厂长与人事部

一座有几千台服务器的工厂，不可能靠人去逐台分配任务。**Kubernetes**（2014 年由 Google 开源）把所有机器变成一个资源池：接收任务，寻找有空闲 GPU 的机器，把任务放上去，机器坏了就把任务挪走。配合 NVIDIA 的 GPU Operator，它能自动安装和管理 GPU 驱动等组件。超大规模训练里也常用传统超算的调度器 **Slurm**（NVIDIA 在 2025 年 12 月收购了它的开发商 SchedMD）。NVIDIA 2024 年宣布收购的 **Run:ai** 则在 Kubernetes 上做更精细的 GPU 调度，并在 2025 年开源了其调度器 KAI Scheduler。

表 10-1 训练与推理都要编排，但侧重点不同

|  | 训练 | 推理 |
| --- | --- | --- |
| 核心诉求 | 一次性拿到大批 GPU，长时间不中断 | 随流量自动伸缩，资源不浪费 |
| 典型能力 | 成组调度（要么全部到位，要么不开始）、按网络拓扑放置、故障后从检查点恢复 | 自动扩缩容（高峰多开副本、深夜回收）、负载均衡、把一块 GPU 切给多个小模型共享 |
| 常见工具 | Slurm、Kubernetes + 批调度器 | Kubernetes + 推理服务框架、Run:ai 等 |

##### NCCL 是

- 一个软件库：决定“怎么传”、走哪条路、用哪种算法
- 上层框架和底层网络之间的翻译与调度者

##### NCCL 不是

- 网络硬件：链路慢了、堵了，它只能绕，不能变出带宽
- CUDA：CUDA 管“怎么算”，NCCL 管“怎么传”

### 10.5 再走一遍：从回车到屏幕

现在可以把第 6 章那次推理，放到整座工厂里重走一遍。以一个需要 8 块 GPU 才放得下的大模型为例：

> 【图示】从按下回车到看到第一个字：请求经网络到达数据中心，负载均衡把它交给 Kubernetes 管理的推理服务；调度器分配好 GPU；模型权重早已切分加载在 8 块 GPU 的显存里；CUDA 在每块卡上执行矩阵乘法；每层算完，NCCL 通过 NVLink 汇总结果；最后一层输出下一个 Token，经网络流回你的屏幕，然后循环。

1. **请求到达：**你的问题经互联网进入数据中心的前端网络。
2. **Kubernetes：**负载均衡把请求交给一个正在运行的推理服务实例。
3. **调度：**这个实例早已被分配到一台 8 卡服务器上。
4. **权重就位：**模型按张量并行切成 8 份，常驻在 8 块卡的显存里。
5. **CUDA：**每块卡执行自己那 1/8 的矩阵乘法。
6. **NCCL：**每一层算完，8 块卡经 NVLink 交换部分结果，凑成完整的结果再进入下一层。
7. **输出：**最后一层给出下一个 Token，流式发回你的屏幕；然后回到第 5 步，生成下一个。

**图 10-5** 屏幕上每蹦出一个字，第 5、6 步就要在一百多层网络里来回几百次。所谓“打字机效果”，背后是 CUDA 在算、NCCL 在传、Kubernetes 在守。网页版可以逐步播放。

CUDA 管*算*，NCCL 管*传*，  
编排系统管“谁在哪儿干活”。

##### 本章带走

万卡协作的关键是“对答案”：集合通信让每块卡拿到汇总结果，速度由最慢的卡和最堵的链路决定。

- 环形 AllReduce：每块卡只和邻居说话，发送量约为数据的 2(N−1)/N 倍，几乎不随卡数增长。
- 两级路：服务器内 NVLink（H100 每卡 900 GB/s，双向），服务器间 RDMA 网络（400 Gb/s ≈ 单向 50 GB/s），同口径相差约 9 倍。
- 软件栈：CUDA 管计算，NCCL 管通信，Kubernetes / Slurm / Run:ai 管调度；训练要“整批不中断”，推理要“弹性伸缩”。

**于是，下一个问题**：一旦“对答案”跨出机箱，数据就要走上网络。为什么传统的以太网扛不住 AI 训练的流量？AI 网络要怎样改造？
