← 返回全部思考

训练 Infra 01:算力、显存、带宽的三角,与一张集群带宽地图

从 H20 的机器平衡点讲起,解释为什么它是推理卡却在做训练,为什么 96GB 显存直接买通了 TP1 的并行设计,以及 NVLink/RoCE/NFS 三级带宽如何决定 EP8 与 DP40 的分工。

训练 Infra 01:算力、显存、带宽的三角

本节属于大模型训练 Infra 实战课。教材项目是 40 卡 H20 上的 Qwen3.6-35B-A3B 后训练,所有数字都是真实实测值。

训练 infra 的所有决策——并行怎么切、batch 怎么设、为什么 MFU 只有 8%——最终都能还原成同一个问题:数据在哪一层硬件之间流动,那一层的带宽够不够。

所以第 1 节我们把硬件栈从卡内一路捋到跨机,建立一张"带宽地图"。后面七节全部是在这张地图上做文章。

1. 一块 GPU 的三角:算力 / 显存 / 带宽

直觉

把 GPU 想成一个工厂:

  • 算力(FLOPs) = 车间里工人的干活速度
  • 显存容量(GB) = 仓库能囤多少原料
  • 显存带宽(TB/s) = 仓库到车间的传送带速度

决定这块卡适合什么活的,不是三者的绝对值,而是它们之间的比例。工人极快但传送带慢,工人就整天在等料;仓库巨大但工人不多,就适合囤大件慢慢干。

H20 是后者。

H20 到底是一块什么卡

H20 H100 SXM A100 80G SXM H800
bf16 稠密算力 约 148 TFLOPs 约 989 TFLOPs 约 312 TFLOPs 约 989 TFLOPs
显存 96 GB HBM3 80 GB 80 GB 80 GB
显存带宽 约 4.0 TB/s 约 3.35 TB/s 约 2.0 TB/s 约 3.35 TB/s
NVLink(双向) 900 GB/s 900 GB/s 600 GB/s 400 GB/s

H20 的出身一目了然:它是出口管制版的 H100——算力砍到 H100 的约 15%,但显存反而更大、带宽反而更高、NVLink 一刀未动。

这不是均衡设计,是"戴着镣铐设计"的产物。理解这一点,是理解后面所有取舍的前提。

公式:机器平衡点(machine balance)

判断一块卡偏科在哪,只需要一个数:

balance = 峰值算力 / 显存带宽      单位:FLOPs/byte

含义是:一段计算,如果它每从显存搬 1 个字节能做的浮点运算数(即"运算强度",arithmetic intensity)超过这个 balance,它就是算力受限(compute-bound);低于它,就是带宽受限(memory-bound)。这就是 Roofline 模型的核心。

代入三块卡:

H20  : 148e12 / 4.00e12 ≈ 37  FLOPs/byte
A100 : 312e12 / 2.00e12 ≈ 156 FLOPs/byte
H100 : 989e12 / 3.35e12 ≈ 295 FLOPs/byte

现在把两种典型负载摆进来:

训练中的大矩阵乘(比如 49K 序列打包成的 GEMM):运算强度轻松上几百甚至上千 FLOPs/byte,在三块卡上都远远越过各自的 balance → 全部是 compute-bound → 比拼的是算力,H20 只有 H100 的 15%。

推理 decode(batch 很小):每生成 1 个 token,基本要把激活参数完整读一遍。每读 2 字节(bf16)大约做 2 次运算,运算强度约 1 FLOPs/byte → 深度 memory-bound → 比拼的是带宽,H20(4.0 TB/s)反而比 H100(3.35 TB/s)还快。

一个公式就解释了 H20 的江湖定位:它是推理卡。

拿它做训练,就是在用它的短板干活。所以后面第 6 节测出 MFU 只有 8-10% 时不用太自责——那个数字里有多少是 H20 的锅、多少是 MoE 和长序列的锅,我们会一项一项拆开。

项目实例:96GB 显存如何"买通"了整个并行设计

教材项目的配置是 TP=1 / PP=1 / EP=8 / DP=40,序列 49K,micro batch=1,full recompute,实测峰值显存 85.6GB。

注意 85.6 这个数。

在 H100(80GB)上,这套配置直接 OOM。 你会被迫上 TP=2,或者做更激进的 offload。而 TP 会引入每层两次 all-reduce 的通信开销,拓扑复杂度、调试难度、故障面立刻全部上升。

也就是说:H20 用多出来的 16GB 显存,换掉了 TP 的通信成本和全部工程复杂度。

大显存弱算力的卡,正确用法就是"用显存换简单"——把并行度做低、把单卡装得满满的、接受算力上限。这套配置是教科书式地顺着这块卡的性格来的。

集群总账记一下,第 6 节算 MFU 要用:

40 卡 × 148 TFLOPs = 5.92 PFLOPs 峰值算力

2. 节点内互联:NVLink vs PCIe

直觉

显存墙之外的第二堵墙是通信墙。GPU 之间交换数据走哪条路,决定了哪种并行策略"付得起"。

  • NVLink 4 + NVSwitch:每卡 900 GB/s 双向,节点内 8 卡经 NVSwitch 全互联,任意两卡等距
  • PCIe Gen5 x16:约 64 GB/s 单向

差距约 14 倍。于是有一条经验法则:

每层都要通信的并行(TP 的 all-reduce、EP 的 all-to-all)必须关在 NVLink 域内;每步才通信一次的并行(DP 的梯度同步)才允许跨出去。

项目实例:EP=8 恰好等于一台机器

这是整套配置里最漂亮的一步棋:EP=8,恰好是单节点的 8 张卡。

Qwen3.6-35B-A3B 是 MoE。每个 MoE 层的 token 要按路由结果分发到不同专家(一次 all-to-all),算完再收回来(第二次 all-to-all)。这是每个 MoE 层都要发生两次的通信,频率极高。

EP=8 把专家切到本机 8 卡上,意味着:

  • 所有 all-to-all 流量走 900 GB/s 的 NVLink
  • 如果贪心设成 EP=16(跨 2 台机器),这些流量就要走 RoCE 网卡,带宽掉 20 到 40 倍;而 all-to-all 是所有集体通信里最怕慢链路的——一条链路慢,整组一起等它

"每 4 层 1 层全注意力,其余是 GDN 线性注意力"这个混合结构不改变上面的结论。但它意味着 MoE 的 FFN 部分在计算里占比更高,all-to-all 的频率照旧——EP 域的带宽依然是生命线。

3. 节点间网络:以太网 / InfiniBand / RoCE

三种"跨机说话"的方式

普通以太网 + TCP:数据经过 CPU 内核协议栈,拷贝多、延迟高、吃 CPU。只配跑管理流量、SSH、日志。

InfiniBand(IB):原生 RDMA——网卡直接读写对端内存,绕过双方 CPU;链路层用 credit 流控从根上避免丢包。训练集群的贵族方案。

RoCE v2:把 RDMA 跑在以太网上。便宜,但要求"无损以太网"(PFC/ECN 配置正确),配不好就丢包重传、性能雪崩。是 IB 的平民替身,也是本项目集群的方案。

再加一个关键技术:GPUDirect RDMA——网卡直接读写 GPU 显存,不用先拷到主机内存。NCCL 检测到 RoCE/IB 时会自动启用。

带宽量级:单口 100/200/400 Gbps 分别是 12.5/25/50 GB/s,多网卡可聚合。但即使聚合到 100 GB/s,也比 NVLink 低一个数量级。这就是"DP 放跨机、EP 放机内"的物理依据。

项目实例:跨机流量到底是什么

5 节点 × 8 卡,eth0 走管理面,RoCE 多网卡走数据面。跨节点的主要流量是 DP=40 的梯度同步(distributed optimizer 的 reduce-scatter + all-gather,第 3、4 节细讲)。

关键的量级感在于频率对比:

流量 频率 走哪条路
EP all-to-all 每个 MoE 层 × 每个 micro batch,2 次 NVLink 900 GB/s
DP 梯度同步 每个 global step 1 次(梯度累积 2) RoCE
checkpoint 落盘 每几百步 1 次 NFS

低频的放慢网络,高频的放快网络。 在 120 秒一步的节奏下,跨机梯度同步只占其中一小段——所以通信不是当前的主要瓶颈,算力才是。

常见坑:多网卡选口与脏环境变量

这是第六次排障的现场。多网卡环境下 NCCL 有两条独立的路,由两个变量分别控制:

  • 控制面(bootstrap / 握手)走 socket:NCCL_SOCKET_IFNAME 选网口。选错(比如选到 docker0 虚拟网卡,或选到一个不通的口),初始化直接挂,或者静默退化到慢速口
  • 数据面走 RDMA:NCCL_IB_HCA 选 RoCE/IB 设备

真正咬人的坑更隐蔽:节点全局 bashrc 里残留了不一致的 NCCL 调试变量。

NCCL 的很多环境变量会改变通信算法和协议的选择。5 台机器里只要有 1 台配置不同,集体通信双方的"舞步"就对不上——结果是死锁,而且报错信息几乎为零。

教训很硬:

  1. NCCL 环境变量必须由启动脚本统一注入,全集群逐字节一致
  2. 任何人不许往系统级 bashrc 里塞训练相关的环境变量
  3. 启动前应该主动 dump 一次全集群的相关环境变量做 diff

这也是第 7 节"容器化隔离的价值"的引子——容器化解决的正是这一类"人为不可控的宿主机状态"。

4. NFS 共享存储:训练中的第四个角色

直觉

NFS 是"全集群共享的慢仓库":容量大、带宽低(典型 1 到几 GB/s)、延迟高、并发写弱。它承担三件事:数据集、代码、checkpoint。

读的账:数据加载

每步 80 样本 × 平均 29K token = 2.31M token
tokenized 后每 token 几个字节
→ 每步从 NFS 读约 10MB 量级
→ 摊在 120 秒里可以忽略

结论:这套负载里数据加载不是瓶颈。长序列 + 重计算的 workload 天然如此——单步算得太久,IO 有充足的时间窗口。

反过来,短序列小模型高吞吐的场景就未必了,那时要上本地缓存和流式预取。

写的账:checkpoint

35B 参数,按 Adam 训练态算:

bf16 权重        : 35e9 × 2  字节 = 70  GB
fp32 主权重      : 35e9 × 4  字节 = 140 GB
Adam 两份动量    : 35e9 × 8  字节 = 280 GB
------------------------------------------------
全量 checkpoint  ≈ 490 GB,即 500 GB 量级

按 NFS 写带宽 1 GB/s 估算:

存一次 ≈ 490 秒 ≈ 8 分钟 ≈ 4 个训练步白跑

这一笔账解释了三件事:

  1. 为什么 save_steps 不敢设密
  2. 为什么 Megatron 用"每个 rank 写自己分片"的 distributed checkpoint,而不是 rank0 单点写全量
  3. 为什么高级方案要做异步 checkpoint——先落本地盘,再后台搬 NFS

第 7 节讲 checkpoint 策略时会回到这笔账。

常见坑

  • 40 个 rank 同时往 NFS 写分片 → NFS 抖动,写入耗时方差巨大,慢的那个 rank 拖住全组
  • 反过来 rank0 单点写全量 → 稳定但奇慢
  • NFS 客户端缓存一致性:A 节点刚写完的文件,B 节点立刻读可能读到旧内容。续训脚本里"存完马上校验/加载"的逻辑要特别小心
  • 把 Python 环境装在 NFS 上 → 几百个进程同时 import,启动卡几分钟。环境放本地盘或打进镜像

5. 收官:一张带宽地图

把本节所有数字排成一个层级,每降一级大约掉一个数量级:

层级 带宽 上面跑什么
HBM(卡内) 约 4 TB/s 一切计算的供料
NVLink(节点内) 900 GB/s EP 的 all-to-all(本项目);TP 的 all-reduce(别人)
PCIe 约 64 GB/s CPU 与 GPU 之间搬运、offload
RoCE(跨节点) 约 12.5-50 GB/s/口 DP 梯度同步
NFS 约 1 GB/s 数据集、checkpoint

并行策略设计的本质,就一句话:让高频通信发生在高带宽层级。

回看这套配置:EP8(每层通信)关在 NVLink,DP40(每步通信)放到 RoCE,checkpoint(每几百步)扔给 NFS——每一种流量都待在了它付得起的那一层。

后面所有章节,都是这张地图的展开。

思考题

1. 假设把这套训练搬到 1 台 8×H100(80GB)上跑同样的 49K 序列 SFT:显存和算力哪个先成为约束?你会怎么改并行配置?

提示:85.6GB 峰值 > 80GB 容量,你必须省出至少 6GB。有 TP、更狠的 recompute、offload 三条路,各自的代价是什么?

2. 用 1.1 节的方法论证"H20 训练弱、推理强":

  • (a) 训练时大 GEMM 的运算强度远高于 37 FLOPs/byte,说明比的是什么?
  • (b) 推理 decode 时,每生成 1 个 token 至少要读一遍激活参数(3B × 2 字节 = 6GB),做约 2 × 3B = 6 GFLOPs。按 H20 的 4 TB/s 带宽,理论 decode 上限是多少 token/s?换成 H100(3.35 TB/s)呢?谁快?

下一节进入显存账:把 85.6GB 这个峰值逐项拆开算给你看,并解释那次让 39 张卡静默死锁 5 小时的优化器惰性初始化陷阱。