Chapter 09

序列并行 SP / 上下文并行 CP

📌 源码引用基于 megatron/core/tensor_parallel/mappings.py 、 megatron/core/transformer/transformer_block.py · commit 432d76b2b8bf 。

9.1 SP 与 CP 听起来都像"切序列",但完全不是一回事

名字切谁解决什么flag
SP(Sequence Parallel)沿 seq 维切 norm / dropout / loss 的激活降 TP 模式下"非切片"激活内存--sequence-parallel
CP(Context Parallel) 沿 seq 维切 attention 计算 训长上下文(32K / 128K / 1M) --context-parallel-size N

SP 是 TP 的"补丁",CP 是独立维度。两者可以同时开,但用途完全不同。

9.2 SP:TP 的"补丁"

问题

第 7 章讲过 TP 切 attention / FFN 的 weight,但 LayerNorm 之前的激活、dropout、loss 这些位置仍然是完整复制的。设 hidden $H$、seq $S$、batch $B$:

$$ M_\text{norm act} = B \cdot S \cdot H \cdot 2\,\text{bytes} \quad (\text{每卡完整}) $$

Llama-3 70B 70 层、$B{=}4$、$S{=}8192$、$H{=}8192$ → 每层 norm 输入约 0.5 GB,70 层就 35 GB,加上反向梯度 70 GB,压根装不下

SP 的招

把这些"夹在 TP 区域之间"的激活也按 seq 维切到 $T$ 张卡,需要时再 gather 回来:

flowchart LR A["输入 X (full)"] --> Scatter["scatter on seq"] Scatter --> LN["LayerNorm
(每卡 1/T)"] LN --> AG["all-gather on seq"] AG --> Col["ColumnParallelLinear
(TP 内)"] Col --> Act[GeLU] Act --> Row["RowParallelLinear
(reduce-scatter on seq)"] Row --> RS["每卡 1/T seq"] RS --> Drop["Dropout / Residual
(每卡 1/T)"] Drop --> Next[下一层]

关键变换:原来 TP 区域结尾的 all-reduce 变成 reduce-scatter;原来 TP 区域开头的"复制"变成 all-gather通信总量没变,但激活内存降到 $1/T$。

SP 显存收益

$$ M_\text{act, full TP} = O(B \cdot S \cdot H) \;\longrightarrow\; M_\text{act, TP+SP} = O(B \cdot S \cdot H / T) $$

对长序列 + 大 TP 的训练,SP 几乎是必开。

9.3 SP 怎么用

# SP 必须跟 TP > 1 一起开
--tensor-model-parallel-size 4 \
--sequence-parallel

不开 TP 的情况下 --sequence-parallel 没意义(毫无切分对象)。

SP 与 dropout

SP 区域内的 dropout 在 seq 维度被切,每卡只看自己那段,dropout mask 互相独立,这是正确的(不像 TP 区域里同步 RNG 那样)。

9.4 CP:让长上下文成为可能

动机

attention 内存增长是 $O(S^2)$

$$ M_\text{attn} = B \cdot N_\text{heads} \cdot S^2 \cdot \text{dtype bytes} $$

$S{=}32$K 时 attention 矩阵 $\approx 32^2 = 1024$ 倍于 $S{=}1$K 的情况。FlashAttention 把内存降到 $O(S)$,但仍然单卡装不下千 K 序列

CP 的招

一条序列沿时间维切到 $C$ 张卡,每卡处理 $S/C$ 个 token。问题是 attention 是"每个 token 看所有历史 token",跨卡怎么办?

答案:ring attention —— 让序列在卡之间循环传递,每张卡轮流接收别人的 KV 做局部 attention,最后把所有 partial output 聚合起来。

sequenceDiagram participant R0 as Rank 0 (token 0-1K) participant R1 as Rank 1 (token 1-2K) participant R2 as Rank 2 (token 2-3K) participant R3 as Rank 3 (token 3-4K) Note over R0,R3: 第 1 轮: 本地 Q × 本地 K/V R0->>R1: 传 K/V (0-1K) R1->>R2: 传 K/V (1-2K) R2->>R3: 传 K/V (2-3K) R3->>R0: 传 K/V (3-4K) Note over R0,R3: 第 2 轮: 用收到的 KV 做局部 attention R0->>R1: 传新一轮 K/V Note over R0,R3: 重复 C-1 次 Note over R0,R3: 最后用 logsumexp 合并所有 partial

关键点

9.5 CP 怎么用

# 4 卡 CP,sequence length 32K
--seq-length 32768 \
--max-position-embeddings 32768 \
--context-parallel-size 4 \
--rotary-base 1000000              # 长上下文常配长 RoPE base
# CP 通常与 TP / SP 一起开
--tensor-model-parallel-size 4 \
--sequence-parallel

9.6 SP 与 CP 的关系

维度SPCP
目的 降 norm / dropout 激活 降 attention 内存
依赖 TP > 1 独立维度
切谁 norm/dropout 的 activation attention 的 Q/K/V/output
通信 替换 all-reduce 为 rs/ag ring KV 传输
对吞吐影响 ≤ 5% 慢 2-15% 慢(看 ring 通信开销)
典型场景 所有 TP 训练 seq ≥ 16K

9.7 全维度并行总和

Megatron 在最大化并行后,单 step world size:

$$ W = \mathrm{TP} \times \mathrm{PP} \times \mathrm{DP} \times \mathrm{CP} \times \mathrm{EP}_\text{eff} $$

其中 $\mathrm{EP}_\text{eff}$ 是 MoE 的有效专家并行度(与 TP 复用)。Llama-3 405B 训练 PP=16 × TP=8 × CP=8 × DP=8 = 8192 卡这种规模。

9.8 长上下文配方:SP + CP + Flash-Attn

序列长度推荐组合
4K - 8K TP=4 + SP + flash-attn-2
16K - 32K TP=8 + SP + flash-attn-2 + CP=2
64K - 128K TP=8 + SP + flash-attn-3 + CP=4
256K - 1M TP=8 + SP + flash-attn-3 + CP=8/16 + selective recompute

9.9 关键源码

文件角色
megatron/core/tensor_parallel/mappings.py SP 的 reduce_scatter / all_gather 原语
megatron/core/transformer/transformer_block.py SP 在 transformer block 中的具体接入位置
megatron/core/pipeline_parallel/hybrid_cp_schedule.pyCP + PP 混合调度
megatron/core/extensions/transformer_engine/ flash-attn / TE 集成;CP 在这里实现 ring

9.10 常见错误

现象原因修复
--sequence-parallel 不识别 / 没生效 没开 TP > 1 SP 必须配 TP
开 SP 后 dropout 数值不一致 RNG context 错 get_cuda_rng_tracker,详见 ch07 7.9
seq 不能被 CP 整除 --seq-length--context-parallel-size
CP 通信极慢 跨节点 + 大 CP 把 CP 留在单节点;或换 NVLink
长上下文 OOM 没开 CP / 没开 selective recompute叠 CP + recompute + flash-attn

9.11 Mamba SSM + Context Parallel

2025-Q3 加入,专门解决 Mamba 这种 SSM 长序列下激活也会 OOM 的问题(递归式状态而非 attention,但激活仍随 seq 线性增长):

文件用途
megatron/core/ssm/mamba.py Mamba SSM 主实现
megatron/core/ssm/mamba_context_parallel.py SSM 跨 CP 切分(注意 state 跨片段传递)
examples/mamba/ 纯 Mamba 与 Mamba+Transformer 混合的 recipe

启用:--use-mamba --mamba-num-layers 24 --context-parallel-size 4注意:Mamba 与 Transformer 层混合的"hybrid 模型"在 pretrain_hybrid.py

9.12 Heterogeneous Models(异构层)

2025-Q4 加入(arguments.py:3194 _add_heterogeneous_args):

9.13 Packed Sequences(变长打包)

SFT 阶段常见的"短序列浪费 padding"问题。megatron/core/datasets/packed_seq_params.py + arguments.py:3244 _add_experimental_args 里的相关 flag:

--use-packed-sequences \
--packed-seq-cu-seqlens-style flash_attn   # 与 flash-attn 的 cu_seqlens 对齐

原理:把短样本拼到一个 seq 里,用 cu_seqlens 告诉 attention "这一段到哪结束"。flash-attn-2/3 原生支持。SFT 提速 2-3 倍

9.14 这章你需要带走什么