大模型分布式并行方式详解
系统梳理 DP(含 ZeRO-1/2/3、FSDP)、TP、PP、SP、CP、EP 等所有主流并行方式:切什么维度、每张 GPU 本地维护什么、大家共享什么、通信内容与通信原语、通信发生的时机与规律。
🧭 总览:并行的本质是"切一个大问题"
训练/推理一个大模型,本质上要在一批数据上,对一个巨大的计算图反复做前向、反向、参数更新。当单卡放不下(显存不够)或算不快(算力不够)时,就要把这个"大问题"沿着某个维度切开,分给多张 GPU。不同的并行方式,区别就在于"沿哪个维度切"。
一个训练任务的张量大致有这么几个维度,每种并行对应切其中之一:
| 并行方式 | 切分的维度 | 直觉一句话 | 主要解决 |
|---|---|---|---|
| DP数据并行 | Batch(样本)维度 | 每卡一份完整模型,各算各的数据,最后同步梯度 | 算得更快(吞吐) |
| ZeRO/FSDP | Batch 维度切数据 + 把"模型状态"分片存 | DP 的省显存版:参数/梯度/优化器状态不再每卡都存整份 | 省显存 + 吞吐 |
| TP张量并行 | 单个权重矩阵的 hidden 维度 | 把一层 Linear/Attention 的矩阵横着/竖着切,多卡合力算一层 | 放下大层 + 降单层延迟 |
| PP流水线并行 | 模型的层(深度)维度 | 把 L 层切成几段,像工厂流水线一样接力 | 放下深模型 |
| SP序列并行 | 序列长度维度(LayerNorm/Dropout 区) | TP 的搭档:把 TP 里冗余的激活也沿序列切开省显存 | 省激活显存 |
| CP上下文并行 | 序列长度维度(Attention 全程) | 超长上下文时,把 seq 切开,注意力用 Ring 交换 K/V | 超长序列 |
| EP专家并行 | MoE 的专家(expert)维度 | 把不同专家放到不同卡,token 按路由 All-to-All 分发 | MoE 扩容 |
两大类:省算力 vs 省显存
🧮 前置知识:训练时显存都花在哪
要理解 ZeRO/FSDP 为什么这么切,必须先知道训练一个模型,显存被哪几块吃掉。设模型有 Ψ 个参数,用混合精度(FP16/BF16 计算 + FP32 主权重)+ Adam 优化器:
| 显存组成 | 内容 | 大小(以 Ψ 个参数计) | 是否随 batch/seq 增长 |
|---|---|---|---|
| 参数 Parameters | FP16 权重(前向/反向用) | 2Ψ | 否 |
| 梯度 Gradients | FP16 梯度 | 2Ψ | 否 |
| 优化器状态 Optimizer States | FP32 主权重副本 | 4Ψ | 否 |
| FP32 momentum(一阶动量) | 4Ψ | 否 | |
| FP32 variance(二阶动量) | 4Ψ | 否 | |
| 激活 Activations | 前向每层输出,反向要用 | ∝ batch × seq × layers | 是(可能很大) |
ZeRO 的核心洞察就是:DP 里每张卡都存了一份完全相同的 16Ψ,这是巨大的冗余。把它们分片存,就能省下大量显存。 而「激活」则是 TP/SP/CP/PP 关注的另一块显存。
📡 集合通信原语(所有并行的"字母表")
GPU 之间怎么"交换数据",归根到底就是几种标准的集合通信(Collective Communication)操作,由 NCCL/RCCL 等库实现。看懂这几个原语,后面每种并行的"通信内容"你就能自己推出来。
| 原语 | 做什么 | 通信量(每卡,N 卡,数据量 M) | 典型用在 |
|---|---|---|---|
| AllReduce | 把 N 张卡上各自的张量逐元素求和(或其它 reduce),结果每张卡都拿到一份完整的和。等价于 ReduceScatter + AllGather。 | ≈ 2M | DP 同步梯度;TP 前向/反向合并部分和 |
| ReduceScatter | 把张量求和,但结果切成 N 份,每张卡只拿到其中 1 份(第 i 张卡拿第 i 段的和)。 | ≈ M | ZeRO/FSDP 归约梯度;SP |
| AllGather | 每张卡各持有 1 份分片,操作后每张卡都拿到拼接起来的完整张量。 | ≈ M | ZeRO/FSDP 临时凑齐参数;SP |
| All-to-All | "转置式"交换:每张卡把自己的数据切成 N 份分别发给 N 张卡,同时从每张卡各收 1 份。像发牌。 | ≈ M | MoE 专家并行(EP)分发/回收 token |
| Broadcast | 一张卡把数据复制发送给所有卡。 | ≈ M | 参数初始化广播、TP 输入分发 |
| P2P (Send/Recv) | 点对点:一张卡把数据发给指定的另一张卡。不是集合操作。 | M(单向) | PP 跨 stage 传激活/梯度;CP 的 Ring 传 K/V |
🎬 动画:四种核心原语在做什么
下面四个动画自动循环播放,用 4 张 GPU(蓝/绿/橙/紫代表 4 段数据)演示数据如何流动。
通信量的两个直觉
- AllReduce / AllGather / ReduceScatter 的单卡通信量与 GPU 数 N 几乎无关(Ring 算法下约为 2M、M、M),只与被通信的数据量 M 有关。这是它们能扩展的关键。
- 通信在哪条链路上很重要:机内 NVLink(数百 GB/s~TB/s)远快于机间 InfiniBand/以太网(几十~几百 GB/s)。所以通信频繁的并行(TP)尽量放机内,通信少的(DP/PP)可以跨机。这决定了混合并行的"排布"。
DP · 数据并行 Data Parallelism
最基础的并行。每张卡放一份完整的模型,各自处理不同的数据小批次,反向后用一次 AllReduce 同步梯度,保证所有卡的模型始终一致。
① 切分维度
切 Batch(样本)维度。全局 batch 拆成 N 份(micro-batch),第 i 张卡拿第 i 份数据。模型本身完全不切。
② 每张 GPU 本地维护什么
🔵 蓝色 = 每张卡都存一份"完整且相同"的副本 → 这就是 DP 的显存冗余:16Ψ × N,浪费严重。只有激活是各卡不同的。
- 本卡数据分片的激活值(不同)
- 参数:每步更新后必须完全一致(靠梯度同步保证)
- 梯度:需跨卡求平均
- 反向得到的梯度(大小 = 2Ψ)
③④⑤ 通信内容、原语与时机
(各算各的数据) → 各卡独立反向
得到本地梯度 → AllReduce 梯度
(求和/平均) → 各卡用相同的
平均梯度更新参数
- 通信时机:只在每个 step 的反向传播完成后、优化器更新前发生一次(现代实现里会把反向和 AllReduce 重叠:某层梯度一算完就异步开始通信,即 gradient bucketing / overlap)。
- 通信原语:AllReduce(对梯度求和,再除以 N 得平均),每卡通信量 ≈ 2 × 2Ψ = 4Ψ 字节。
- 通信模式/规律:整个训练里,DP 组内是"计算—通信"交替的规律脉冲;通信量与 batch/seq 无关,只与模型大小 Ψ 有关;每步 1 次(可与反向重叠隐藏)。
⑥ 优缺点
- 实现最简单,扩展性好
- 通信少(每步一次 AllReduce,可与反向重叠)
- 提升训练吞吐几乎线性
- 显存零节省:每卡都存完整的 16Ψ,装不下大模型
- 模型必须能塞进单卡
ZeRO-1 / 2 / 3 · 零冗余优化器 Zero Redundancy Optimizer
ZeRO(DeepSpeed)是 DP 的"省显存进化版"。核心思想:数据并行照旧(各卡不同数据),但把那份冗余的 16Ψ 模型状态沿参数维度切成 N 份,每张卡只存 1/N。谁需要完整的谁临时 AllGather 拿过来,用完就丢。 逻辑上等价于 DP,数学结果完全一致,只是把"空间换成了通信"。
三个渐进阶段:切得越多,越省显存,通信越多
| 阶段 | 分片的内容 | 每卡模型状态显存 | 相比 DP 省 | 额外通信 |
|---|---|---|---|---|
| ZeRO-1 Pos | 只切优化器状态(12Ψ) | 4Ψ + 12Ψ/N | ~4× (N大时) | 无(通信量同 DP) |
| ZeRO-2 Pos+g | 切优化器状态 + 梯度 | 2Ψ + 14Ψ/N | ~8× | 无(通信量同 DP) |
| ZeRO-3 Pos+g+p | 切优化器状态 + 梯度 + 参数 | 16Ψ/N | ~N× (线性!) | +50%(多一次参数 AllGather) |
下标含义:os = optimizer states,g = gradients,p = parameters。ZeRO-3 是"全都切",显存随卡数线性下降——这也是 FSDP 的等价物。
ZeRO-1:只切优化器状态
🟢 绿色 = 分片存储(每卡只存 1/N)。参数和梯度仍是完整副本。
- 本地维护:完整参数、完整梯度、仅 1/N 的优化器状态(以及对应那 1/N 参数的 FP32 主副本)。
- 怎么跑:前向/反向和 DP 完全一样(参数完整)。反向后 AllReduce 得到完整平均梯度 → 但每张卡只更新自己负责的那 1/N 参数(因为它只有那段的优化器状态)→ 更新完各卡只有 1/N 的新参数,再 AllGather 把新参数拼回完整。
- 通信内容与原语:梯度 AllReduce(≈2Ψ)+ 更新后参数 AllGather(≈Ψ)。实践中把 AllReduce 换成 ReduceScatter+AllGather,总通信量与 DP 持平(≈2M)。
ZeRO-2:再切梯度
- 关键变化:既然每张卡只更新 1/N 参数,那它其实只需要那 1/N 的梯度,其余梯度算完立刻可以丢掉。于是反向时不用 AllReduce(人人都拿完整梯度),改用 ReduceScatter:反向一边算梯度,一边把梯度归约并散射,让第 i 卡只收到并保留第 i 段的梯度和。
- 本地维护:完整参数、1/N 梯度、1/N 优化器状态。
- 通信内容与原语:反向中 ReduceScatter 梯度(≈Ψ)+ 更新后 AllGather 参数(≈Ψ)= 总 ≈2M,仍与 DP 持平,却比 ZeRO-1 更省显存。
ZeRO-3:连参数也切(=FSDP)
🟢 三样东西全部分片,每卡稳定只占 16Ψ/N —— 显存随 GPU 数线性下降,这是"零冗余"的完全体。
ZeRO-3 的运行机制(重点:参数是"临时凑齐、用完就扔")
因为参数也被切了,任何一层要做前向/反向计算时,本地并没有该层的完整权重。于是:
本地只有 1/N 权重 → AllGather
凑齐第 L 层完整权重 → 算完第 L 层前向
立即释放完整权重 → 下一层重复……
- 前向:逐层(或逐 bucket)AllGather 参数 → 算 → 丢弃。任意时刻只有"当前层"的完整参数在显存里。
- 反向:同样需要该层完整参数,再 AllGather 一次(前向丢了得重拿);算出梯度后用 ReduceScatter 把梯度归约散射到"负责该段的卡"。
- 更新:各卡用本地 1/N 梯度 + 1/N 优化器状态更新自己的 1/N 参数。无需额外通信。
- 前向:参数 AllGather
- 反向:参数 AllGather + 梯度 ReduceScatter
- 贯穿整个前向和反向,每层/每 bucket 一次(而非每步一次)
- 可与计算重叠:预取下一层参数
- ≈ 3M(前向 AllGather + 反向 AllGather + ReduceScatter)
- 比 DP 的 2M 多约 50%
FSDP · 全分片数据并行 Fully Sharded Data Parallel
FSDP 是 PyTorch 官方实现的"ZeRO-3 等价物"。思想与 ZeRO-3 几乎相同:把参数、梯度、优化器状态全部沿数据并行组分片,计算时临时 AllGather 凑齐、算完释放。 差别主要在工程实现与分片粒度上,不在核心原理上。
torch.distributed.fsdp),分片单位是用户定义的 FSDP Unit(通常按 Transformer Block 包裹),而 ZeRO-3 是按参数扁平化桶(bucket)自动切。理解 FSDP 的"Unit + Flat-Parameter"机制,是用好它的关键。FSDP2(per-parameter 分片)进一步改进了这一点。
① 切分维度
与 ZeRO-3 相同:数据并行切 batch;同时把每个 FSDP Unit 内的参数/梯度/优化器状态沿 DP 组切成 N 份。
② 每张 GPU 本地维护什么
③④⑤ 运行流程(一个 FSDP Unit 的生命周期)
(只留分片) → 反向再 AllGather 一次参数 → 算梯度 → ReduceScatter 梯度
- 逻辑上一份完整模型,物理上分片
- 更新后各卡持有各自 1/N 的最新参数
- 前向:参数 AllGather
- 反向:参数 AllGather + 梯度 ReduceScatter
- 每进入/离开一个 FSDP Unit 就通信一次
- 相邻 Unit 的通信与计算重叠(prefetch)
| 维度 | ZeRO-3 (DeepSpeed) | FSDP (PyTorch) |
|---|---|---|
| 核心原理 | 完全相同:参数/梯度/优化器状态全分片,临时 AllGather | |
| 分片单位 | 参数扁平化后按 bucket 自动切 | 用户定义的 FSDP Unit(常按 Transformer Block);FSDP2 支持 per-parameter |
| 通信原语 | AllGather + ReduceScatter | AllGather + ReduceScatter(相同) |
| 生态 | DeepSpeed,配置驱动 | PyTorch 原生,代码包裹模块 |
| Offload | ZeRO-Offload / Infinity(CPU/NVMe) | CPU offload |
| 分片范围(sharding group) | 通常全局 DP 组 | 可配 HYBRID_SHARD:机内分片 + 机间复制 |
TP · 张量并行 Tensor Parallelism
TP(Megatron-LM)把单个层内的权重矩阵切开,分给多张卡。多张卡合力完成同一层的计算——它们处理同一份数据,各算矩阵的一部分,再通过通信把部分结果合并。与 DP 的"各算各的数据"截然不同。
① 切分维度
切 权重矩阵的 hidden 维度。核心技巧是把 Transformer 里连续的两个 Linear(MLP 的 up/down,或 Attention 的 QKV/O)分别用"列切"和"行切"配对,使得中间只需要一次通信。
Megatron 的两种切法
Y_i = X·A_i,得到输出的不同列。前向无需通信(输出天然是拼起来的)。用于 MLP 第一层、QKV 投影。Z_i = Y_i·B_i 得到部分和,需要 AllReduce 求和得到完整输出。用于 MLP 第二层、Attention 输出投影。MLP 块的经典配对(column→row,中间不通信)
(每卡完整) → 列并行 Linear+GeLU
各卡得 Y_i (输出的一部分列) → 行并行 Linear
各卡得部分和 Z_i → AllReduce
合并得完整输出 Z
妙处:列并行的输出(按列切)正好是行并行需要的输入(按行切),所以中间那步不用通信,只在最后 AllReduce 一次。整个 MLP 块前向只需 1 次 AllReduce。
② 每张 GPU 本地维护什么
🟠 橙色 = 每卡只存权重矩阵的一部分(沿 hidden 切)。注意:TP 组内各卡处理的是同一份数据,但注意力头也被分到不同卡(每卡算不同的 heads)。
- 权重矩阵的 1/N 切片(列或行)
- 对应切片的梯度与优化器状态
- 注意力的一部分 heads
- 输入/输出激活:块的边界处需要是完整的、各卡一致的
- LayerNorm 等在 TP 中是冗余复制的
- 激活值(部分和),不是梯度也不是参数!
- 大小 ∝ batch × seq × hidden
④⑤ 通信内容、原语与时机
- 通信的是激活,不是权重。这是 TP 与 DP/ZeRO 最本质的区别:DP/ZeRO 通信梯度/参数(∝Ψ,与数据无关),TP 通信激活(∝ batch×seq×hidden,与数据强相关)。
- 前向:每个 Transformer 层里,Attention 块 1 次 AllReduce + MLP 块 1 次 AllReduce,合计 2 次 AllReduce/层。
- 反向:对称地也需要 2 次 AllReduce/层(对输入梯度求和)。
- 通信时机与规律:通信极其频繁且在计算的关键路径上(每层前向反向各 2 次,无法像 DP 那样攒到最后)。因此 TP 几乎必须放在单机内、走 NVLink,跨机做 TP 会严重掉速。TP 数一般 ≤ 单机 GPU 数(如 8)。
⑥ 优缺点
- 能放下单卡装不下的超大层(如巨大的 FFN、词表)
- 降低单层计算延迟(多卡分摊)
- 参数/优化器/梯度也天然被切(省显存)
- 通信频繁且在关键路径,强依赖 NVLink,难以跨机扩展
- 激活在块边界仍是完整复制的 → 催生了 SP
- 实现侵入模型代码
PP · 流水线并行 Pipeline Parallelism
PP 沿模型的层(深度)方向切:把 L 层分成几个连续的 stage,每个 stage 放一张(组)卡上。数据像在工厂流水线上一样,一个 stage 算完把中间激活传给下一个 stage。
① 切分维度
切 模型的层数(深度)。例如 32 层分成 4 个 stage,每 stage 8 层。Stage 之间是串行依赖的(后一段要等前一段的输出)。
② 每张 GPU 本地维护什么
🟣 紫色 = 每卡只持有连续若干层的完整参数(该层的权重是完整的,不像 TP 那样切碎)。这是 PP 与 TP 的关键区别:PP 切"哪些层归谁",TP 切"同一层的矩阵"。
- 本 stage 那几层的完整参数、梯度、优化器状态
- 在途 micro-batch 的激活(要留到反向)
- stage 边界的激活(前向)与激活梯度(反向)
- 前向:把本 stage 输出激活传给下一 stage
- 反向:把输入梯度传回上一 stage
④⑤ 通信原语与时机:P2P 点对点
- 通信原语:不是集合通信,而是 P2P Send/Recv——只在相邻 stage 之间点对点传一个激活张量(前向)或激活梯度(反向)。
- 通信量:每个 micro-batch 在每个 stage 边界传一次,大小 ∝ batch × seq × hidden。相比 TP 的每层 AllReduce,PP 通信量小得多、次数少得多,因此 PP 可以跨机器(走机间网络也能接受)。
核心难题:流水线气泡(Bubble)与 micro-batch
朴素做法(一次喂整个 batch)会让流水线严重空转:Stage 3 必须等 Stage 0→1→2 都算完才能开工,反向时反过来。解决办法是把 batch 切成很多 micro-batch 灌进流水线,让各 stage 尽量都有活干。
GPipe 调度(先全部前向,再全部反向)
1F1B(One-Forward-One-Backward,PipeDream)
更优的调度:进入稳态后,每个 stage 交替做一次前向、一次反向。好处是能尽早释放激活显存(一个 micro-batch 反向做完就丢掉它的激活),显存占用大幅降低。Interleaved 1F1B(Megatron)进一步把每个 stage 再拆成多个不连续的层块,交错调度,把气泡进一步压小。
气泡比例 ≈ (stage 数 − 1) / micro-batch 数。所以要用远多于 stage 数的 micro-batch 来摊薄空转;1F1B 调度还能让反向尽早做、及时释放激活。
(p − 1) / m,p = stage 数,m = micro-batch 数。所以 micro-batch 数 m 要远大于 stage 数 p 才能摊薄气泡。这是 PP 调参的核心。
⑥ 优缺点
- 通信少(只在 stage 边界 P2P),能跨机扩展
- 每卡只放几层,显存友好
- 与 TP/DP 正交,易组合
- 流水线气泡浪费算力,需要足够多 micro-batch
- 负载均衡难(各 stage 层数/计算要均匀)
- 调度实现复杂(1F1B、interleave)
SP · 序列并行 Sequence Parallelism
SP 是 TP 的搭档(Megatron-SP)。它解决 TP 遗留的一个问题:在 TP 里,LayerNorm、Dropout、残差这些"逐元素"操作没法按 hidden 切,于是在 TP 组的每张卡上都冗余保存了完整的激活。SP 把这些区域的激活沿序列长度维度切开,消除这块激活冗余。
(B) 早期/广义的 SP:把整个序列切到不同卡上独立算,注意力处再想办法通信——这条路线后来发展成了 CP(上下文并行)。所以你会看到 SP 和 CP 概念上有重叠。
① 切分维度
在 TP 已经切了 hidden 维的基础上,SP 把 Transformer 中"非 TP 区域"(LayerNorm、Dropout、残差)的激活沿 sequence 维度切成 N 份,每卡只存 1/N 的序列。
② 每张 GPU 本地维护什么(TP+SP 联合视角)
🌸 SP 区:激活按序列切;🟠 TP 区:激活按 hidden 切。同一批激活在"进入/离开 TP 区"时要在这两种切分方式之间转换——这就是 SP 的通信来源。
③④⑤ 妙处:把 TP 的 AllReduce 拆成 AllGather + ReduceScatter,通信量不变
在纯 TP 里,进出 TP 区各要一次 AllReduce。SP 把序列切分和 hidden 切分衔接起来后,这两次操作变成:
激活按 seq 切 → AllGather
(沿 seq 凑齐进入 TP 区) → TP 区
激活按 hidden 切 → ReduceScatter
(回到 SP 区,按 seq 切)
- 通信原语变了但总量不变:回忆
AllReduce = AllGather + ReduceScatter。SP 只是把原来 TP 的 1 次 AllReduce 拆成 1 次 AllGather + 1 次 ReduceScatter,通信总量与纯 TP 完全相同,却额外省下了 LN/Dropout 区的激活显存(从完整变成 1/N)。 - 通信时机:与 TP 一样在每层前向/反向、进出 TP 区的边界处,因此同样必须在机内 NVLink,且 SP 组通常与 TP 组重合(same ranks)。
- 通信内容:依然是激活(不是参数/梯度)。
⑥ 优缺点
- 几乎免费地省下 LN/Dropout 区激活显存(通信量不增加)
- 与 TP 无缝配合,是长序列训练标配
- 只能和 TP 一起用,不独立
- 同样受限于机内高带宽
CP · 上下文并行 Context Parallelism
CP 专门为超长上下文(几十万甚至上百万 token)而生。它把序列长度维度切开,让每张卡只负责一段 token 的全部计算(含注意力)。难点在于注意力是"全局"的——每个 token 要看到所有 token 的 K/V,所以 CP 的核心是用 Ring 通信在卡之间轮转 K/V。
① 切分维度
切 序列长度 seq。序列 S 切成 N 段,第 i 张卡持有第 i 段 token 的 Q/K/V 和激活。参数则在每张卡上是完整(或与 TP/DP 组合)。
② 每张 GPU 本地维护什么
🔵 CP 区:每卡只持有自己那段序列的 Q/K/V 与激活;参数完整。激活显存随 CP 数线性下降 → 这才能撑起超长序列。
③④⑤ 核心机制:Ring Attention(环形轮转 K/V)
要算注意力 softmax(QKᵀ)V,本卡的 Q 需要和所有 token 的 K/V 做内积。CP 不把所有 K/V 收集到一张卡(那会爆显存),而是让 K/V 块在环上一站一站传递,每收到一块就和本地 Q 算一次局部注意力,用 online-softmax 累加:
环上下一张卡,同时收上一张的 K/V → 用收到的远端 K/V 再算一块,累加进 softmax → 重复 N-1 次,遍历全序列
seq 0
seq 1
seq 2
seq 3
online-softmax 累加
通信原语是最近邻 P2P(环形),通信量与计算量都随序列线性,且可与注意力计算重叠——所以能撑起超长上下文,也能跨机。
- 通信原语:环形 P2P Send/Recv(Ring Attention);DeepSpeed-Ulysses 则改用 All-to-All 在"按序列切"和"按 head 切"之间转换。
- 通信内容:是 K/V 张量块(∝ 每段 seq × hidden),不是参数也不是梯度。反向对称地轮转梯度。
- 通信时机与规律:在每一层的注意力内部发生,一层里要环转 N-1 步。好处是通信可与注意力计算重叠(算当前块时预取下一块 K/V),且通信量与计算量都随序列线性,扩展性好。CP 组可以跨机(Ring 是最近邻 P2P,对拓扑友好)。
⑥ 优缺点
- 把激活显存与注意力显存随序列线性切开,支持超长上下文
- Ring P2P 通信可与计算重叠,扩展性好
- 与 TP/PP/DP 正交组合
- 实现复杂(online softmax、因果掩码下的负载均衡)
- 短序列时收益不大、反而多通信
- 参数未被切(需配合 ZeRO/TP 省参数显存)
EP · 专家并行 Expert Parallelism
EP 专为 MoE(Mixture of Experts,混合专家) 模型设计。MoE 的 FFN 层有很多个"专家"(expert,各自是一个 FFN),每个 token 只被路由到少数几个专家(如 top-2)。EP 把不同的专家放到不同的 GPU 上,token 按路由结果用 All-to-All 送到对应专家所在的卡计算,再送回来。
① 切分维度
切 专家(expert)维度。假设有 64 个专家、8 张卡做 EP,则每卡放 8 个专家。注意力等非 MoE 部分照常用 DP/TP/PP,只有 MoE 的 FFN 部分走 EP。
② 每张 GPU 本地维护什么
🔴 EP 区:每卡只存一部分专家的权重(专家总参数被切)。非专家部分(注意力、路由门控)仍是复制或另行并行。
- 本卡负责的那批专家的完整权重、梯度、优化器状态
- 共享层(注意力等)参数
- 共享层参数在 EP 组内需一致(走 DP 式同步)
- token 需被送到"正确的专家"
- token 的激活向量(分发去 + 收回来)
③④⑤ 核心机制:两次 All-to-All(Dispatch → Compute → Combine)
每张卡先本地算注意力和门控(router),得到"本卡这些 token 各自要去哪个专家"。由于目标专家可能在任何一张卡上,需要一次"重新分发":
每个 token 去哪个专家 → All-to-All Dispatch
把 token 送到目标专家所在卡 → 各卡用本地专家
计算收到的 token → All-to-All Combine
把结果送回 token 原来的卡
- 通信原语:两次 All-to-All(前向 dispatch + combine;反向再对称两次)。All-to-All 正是"每张卡把自己的 token 按目的地拆开发给所有卡"的天然操作。
- 通信内容:token 激活向量,通信量 ∝ 被路由的 token 数 × hidden × top-k。与数据强相关,且依赖路由分布。
- 通信时机与规律:在每个 MoE 层发生两次 All-to-All。All-to-All 是全局的、对网络带宽压力大,通常希望 EP 组落在高带宽域(机内或高速互联的多机)。
⑥ 优缺点
- 让总参数量暴涨(更多专家)而单 token 计算量几乎不变(稀疏激活)
- 专家权重天然分片,省显存
- All-to-All 通信重,对网络敏感
- 负载均衡难,需要额外机制
- 只适用于 MoE 结构
🧩 混合并行:3D / 4D / 5D 并行怎么组合
真实的大模型训练几乎从不用单一并行,而是把它们组合成多维网格(每张 GPU 同时属于多个并行组)。这些维度是正交的:可以把 GPU 想象成一个多维数组,每个维度对应一种并行。
组合的黄金法则:按"通信频率"匹配"物理带宽"
| 并行 | 通信频率 | 通信量 | 放在哪一层网络 |
|---|---|---|---|
| TP / SP | 极高(每层多次) | 大(激活) | 机内 NVLink(同一节点 8 卡内) |
| EP | 高(每 MoE 层 All-to-All) | 中(token) | 机内或高速互联多机 |
| CP | 中(注意力内 Ring,可重叠) | 中(K/V) | 机内或近邻多机 |
| PP | 低(stage 边界 P2P) | 小(激活) | 跨机(机间网络) |
| DP / ZeRO / FSDP | 低(每步一次,可重叠) | 大(梯度/参数)但不在关键路径 | 最外层,可跨多机 |
一张 GPU 在混合并行里的"多重身份"
假设 TP=2, PP=2, DP=2(共 8 卡)。GPU #5 可能同时是:
- 某个 TP 组的 rank 1 —— 和另一张卡合存一层的权重矩阵,每层 AllReduce 激活
- 某个 PP stage 1 —— 持有后半段的层,与前一 stage P2P 传激活
- 某个 DP 组的 rank 1 —— 和另一份副本处理不同数据,每步 AllReduce/ReduceScatter 梯度
每种"身份"对应一个独立的通信组(NCCL communicator),互不干扰地在各自维度上通信。这就是分布式训练框架(Megatron-LM、DeepSpeed)要维护的"进程组拓扑"。
📊 横向对比总表
把前面所有并行方式的核心问题浓缩到一张表里,方便速查:
| 并行 | 切什么维度 | 每卡本地维护 | 通信内容 | 通信原语 | 通信时机 | 主要目的 |
|---|---|---|---|---|---|---|
| DP | Batch(数据) | 完整模型 + 完整状态(冗余) | 梯度 | AllReduce | 每步反向后 1 次 | 吞吐 |
| ZeRO-1/2 | Batch + 优化器状态(/梯度)分片 | 完整参数 + 1/N 状态(/梯度) | 梯度 + 更新后参数 | ReduceScatter+AllGather | 每步(同 DP 总量) | 省显存+吞吐 |
| ZeRO-3/FSDP | Batch + 参数/梯度/状态全分片 | 各 1/N(临时凑齐当前层参数) | 参数(前向+反向) + 梯度 | AllGather+ReduceScatter | 每层/每 Unit | 显存线性下降 |
| TP | 权重矩阵 hidden 维 | 权重的 1/N 切片(同份数据) | 激活(部分和) | AllReduce | 每层前向/反向各 2 次 | 放下大层/降延迟 |
| PP | 模型层(深度) | 连续若干层的完整参数 | 边界激活 / 激活梯度 | P2P | 每 micro-batch 过 stage 边界 | 放下深模型 |
| SP | 序列维(LN/Dropout 区) | + 1/N 的 LN/Dropout 激活 | 激活 | AllGather+ReduceScatter | 进出 TP 区(同 TP 频率) | 省激活显存 |
| CP | 序列维(全网络含注意力) | 本段序列的 Q/K/V + 激活 | K/V 块(反向传梯度) | 环形 P2P / All-to-All | 每层注意力内 N-1 步 | 超长上下文 |
| EP | MoE 专家维 | 一部分专家的完整权重 | token 激活(分发/回收) | All-to-All ×2 | 每个 MoE 层 | MoE 稀疏扩容 |
通信的"三种东西"一句话总结
⚡ 推理场景的特别说明
前面很多内容以训练为主。推理(inference / serving)没有反向传播,也没有梯度和优化器状态,因此并行的侧重点不同。这里单独说明。
推理与训练的关键差异
- 梯度、优化器状态(无反向)
- DP 的梯度 AllReduce(推理里"DP"= 多副本负载均衡,各副本独立服务不同请求,几乎不通信)
- ZeRO 的优化器状态分片(无优化器)
- KV Cache 显存(随并发/序列长度暴涨)
- 延迟(TTFT / TPOT)而非纯吞吐
- Prefill 与 Decode 两阶段特性不同
各并行在推理中的角色
| 并行 | 推理中的作用 | 通信特点 |
|---|---|---|
| TP | 最常用。把大模型的每层切开放到多卡,降低单次前向延迟,并让 KV Cache 也按 head 分摊。 | 每层 AllReduce 激活,走 NVLink;KV Cache 随 TP 切分 |
| PP | 放下超大模型(单机放不下时跨机)。但对延迟不友好(stage 串行),更适合吞吐导向的批处理。 | stage 边界 P2P 传激活 |
| EP | MoE 模型推理必备(如 DeepSeek/Mixtral)。专家分卡,token All-to-All。 | 每 MoE 层两次 All-to-All |
| CP / SP | 长上下文推理(尤其 prefill 阶段)把长序列切开,降低单卡激活/KV 压力。 | 注意力内 Ring 传 K/V |
| DP | 推理里通常指多实例副本:N 份完整模型各自服务请求,用负载均衡器分发,副本间基本不通信。 | 几乎无(仅 MoE 的 attention DP + EP 组合时有) |
batch × seq × layers × hidden × 2,在长上下文/高并发下可能比模型权重还大。TP 会把 KV Cache 按注意力头切分到各卡;CP/SP 会按序列切分。这是推理并行相比训练最不同的关注点。(此外还有 PagedAttention、连续批处理、Prefill/Decode 分离部署等推理专属优化,超出"并行方式"范畴,这里不展开。)
🎯 选型速查:我该用哪种并行?
- 模型能塞进单卡,只想训得更快 → DP,再不够上 ZeRO-1/2 省点显存。
- 模型塞不进单卡(几十亿~百亿) → FSDP / ZeRO-3(最易用),或机内 TP。
- 超大稠密模型(百亿~千亿) → TP(机内) + PP(跨机) + DP/ZeRO(最外) 三件套,加 SP 省激活。
- 超长上下文(数十万 token) → 在上面基础上加 CP。
- MoE 模型 → 在上面基础上给 FFN 部分加 EP。
- 纯推理 → TP(降延迟) + 多副本 DP(扩吞吐),MoE 加 EP,长序列加 CP。