SP / CP
Infra · 2022–2024 · SP / CP

Sequence Parallel 与 Context Parallel:
把序列切开,但别把通信切开

TP 把矩阵乘切到多卡,却把 LayerNorm、Dropout 的激活整条序列复制在每张卡上。SP 把这段非 TP 区域沿 sequence 切开,并把 all-gather / reduce-scatter 塞进 TP 原来的通信边界;CP 更进一步,连 attention 的输入和激活都按序列切,每张卡只算自己的 Q,KV 则沿环传递。 两者都能省显存,但一个不增加带宽,另一个每层都要换 KV。

Korthikanti et al. · Liu et al. · Jacobs et al. · NVIDIA / UC Berkeley / Microsoft SP 原文 ↗ Ring 原文 ↗ Ulysses 原文 ↗ Megatron-Core @70f7a26 ↗
TL;DR · 30 秒抓住核心

TP 的盲区:LayerNorm、Dropout、残差这些 token-wise 操作不需要跨卡,但 Megatron-LM 的 TP 仍让每张卡保留完整序列的这部分激活。论文 Eq.2 里 10sbh 就是不随 TP 缩小的那一项。

SP 的做法:把非 TP 区域按 sequence 切成 t 份;在 column-parallel linear 前 all-gather 成完整序列,在 row-parallel linear 后用 reduce-scatter 切回。论文把这两个操作记作 g / ḡ,并让它们与 TP 的 f / f̄ 融合。TP 原有的 4 次 all-reduce 变成 4 次 AG + 4 次 RS;ring all-reduce 本来就是 RS + AG,所以总带宽不变。

CP 的做法:输入和所有激活沿 sequence 切;attention 需要全序列 KV,于是每张卡用自己的 Q 块对轮转过来的 KV 块做 attention。Ring Attention 用逐块 KV 轮转把通信藏进计算;Megatron-Core 用 TE 的 P2P ring、zigzag 布局和 GQA 来降低或均衡开销。

边界:SP 仍依附 TP,不新增并行维度;CP 是独立轴,总卡数 TP×CP×PP×DP。SP 省的是非 attention 激活,CP 省的是 attention 的显存和每卡计算,但 CP 的 KV 通信随序列长度增长,不会按 CP 比例缩小。

5×
SP + selective recompute 的激活显存降幅
相对 TP baseline,不是 SP 单独
54.2%
530B GPT-3-style(MT-NLG 530B),Present work = SP + selective recompute
2240 张 A100、8-way DP;比 full recompute 的 42.1% 快 29%
256×
Ring Attention 在 TPUv4-512 上的上下文扩展
可训练超过 30M tokens
01

TP 只切矩阵乘,LayerNorm/Dropout 的激活仍按整条序列复制

先看清 TP 的激活账单:哪一项随 TP 缩小,哪一项不动。

张量并行(TP)把 attention 和 MLP 里的矩阵乘按 hidden 维切开,块内的激活也随 hidden 维切开。但 QKV 投影、MLP 第一层的输入,以及 LayerNorm 和 Dropout 这些逐 token 操作,并不在 TP 的切分范围里。它们要么在 TP 区域之外,要么需要完整 hidden 维,于是每张 TP rank 都保留一份完整序列的激活。

MTP = s · b · h · ( 10 + 24/t + 5·a·s/(h·t) )
论文 Eq.2:t 是 TP size,a 是 attention heads,s 是序列长度。中间那一项 10 不除以 t,就是 LayerNorm/Dropout 等复制区域的激活

代入 22B 模型的配置(h=6144、a=64、s=4096、b=1、t=8、bf16),每层被复制的非 TP 激活是 503.3 MB。SP 把这块也按 t 切开,于是每层省下 10sbh(1−1/t),t=8 时是 440.4 MB。注意,剩下的 10sbh/t 仍存在,只是不再是每卡一份完整拷贝。

另一半压力来自 attention 自己。Eq.2 的 5·a·s/(h·t) 项乘上 sbh 后是 5abs²/t,随序列长度平方增长。Ring Attention 论文给了一个量级:1 亿 token、batch 1、hidden 1024,内存就超过 1000 GB,而当时的 GPU/TPU HBM 普遍小于 100 GB(§2)。这就是 CP 要解决的问题:不是权重放不下,而是 attention 的序列维激活放不下。

两个瓶颈

SP 和 CP 不是同一件事的两个名字

SP 处理的是 TP 留下的“复制区域”:LayerNorm、Dropout、残差等非 TP 激活。它不改变 attention 的计算方式,也不减少 attention 的 s² 项。CP 处理的是 attention 本身:把输入和所有激活沿 sequence 切开,让每张卡只算一部分 Q,再通过 KV 交换看到全序列。Megatron-Core 的 CP 文档把这条界线写得很直接:CP 分割网络输入和所有激活,而此前的 SP 只分割 Dropout 和 LayerNorm 的激活(context_parallel.md:19)。

02

TP、重算、Ulysses/Ring:各自绕开一段,但解决的不是同一个瓶颈

把几条路放在同一张表里,先分清「省激活」「省通信」「扩上下文」。

TP

切矩阵乘,不切逐 token 区域

TP 把 attention 的 QKV 投影、MLP 的两层线性按 hidden 维切开,块内激活随 hidden 维缩小。代价是每次前向+反向有 4 次 all-reduce(论文 §4.2.2)。LayerNorm/Dropout 仍是完整序列,Eq.2 的 10sbh 不会随 t 缩小。

卡在:继续加 TP 会让 Linear 的计算短到藏不住通信延迟(context_parallel.md:32);而且它不解决 attention 的 s² 激活。

recompute

用算力换显存,但反向要多跑一遍

Full activation recomputation 丢掉中间激活,反向从 checkpoint 重放。SP 论文的 22B 单层实测:baseline 的 fwd+bwd 是 19.6 ms,full recompute 变成 27.2 ms,开销 39%(Table 4)。

卡在:重算的是整层,哪怕其中很多激活又小又便宜。SP 的价值之一是省出显存后,可以少做重算。

Ulysses

先按序列切,再 all-to-all 换成 head 并行

DeepSpeed-Ulysses 把输入沿 sequence 切开,attention 前用 all-to-all 让每张卡拿到完整序列、但只属于自己的一部分 head,attention 后再 all-to-all 换回序列切分。每层通信量约 4Nh/P,N 和 P 同比例增长时保持不变(§3.2)。

卡在:依赖 all-to-all 和 head 数能被并行度整除 推断(由论文“每卡拿非重叠 head 子集”推得);论文的对比基线是 Megatron-LM 的 SP,不是 CP。

Ring

Q 不动,KV 块沿环转

Ring Attention 把序列切成 Nh 块,每个 host 持一块 Q;KV 块沿环依次传给下一个 host,每步用当前 KV 块更新局部 attention。计算和传输重叠的条件是 block 计算时间 ≥ 传输时间(Algorithm 1、§3.1)。

卡在:每个 host 最终要看过全部 KV,单卡 KV 通信量随 N 增长,不随设备数缩小;block 太小时通信藏不住。

Table 1(本页归纳):这几条路线的切分对象与通信方式。SP 行指 Megatron-LM 2022 的 sequence parallelism;CP 行指 Megatron-Core 的 context parallel 实现,两者是不同来源。

方案切什么attention 怎么拿全序列每层主要通信主要省什么出处
TP矩阵乘的 hidden 维;块内激活不需要,每卡已有完整序列4 次 all-reduce(fwd+bwd)参数、块内激活§4.2.1
SP(2022)LN/Dropout 等非 TP 区域按 sequence进入 TP 线性层前 all-gather4 次 AG + 4 次 RS复制的非 TP 激活§4.2.2
Ulysses所有模块按 sequence;attention 换成 head 并行all-to-all 后每卡拿部分 head 的完整序列2 次 all-to-all,合计 4Nh/P激活 + 长上下文§3.2
Ring所有模块按 sequence;Q 本地KV 块沿环传递,每卡看全部 KV每层轮转 CP−1 块 KVattention 激活 + 上下文Algorithm 1
CP(Megatron-Core)网络输入和所有激活按 sequenceKV all-gather;反向 KV 梯度 reduce-scatterP2P ring 上的 KV AG / RS激活 + 每卡计算context_parallel.md:19

一个容易混淆的点:Ulysses 论文里的 “sequence parallelism” 指的是整个模型沿 sequence 切,attention 用 all-to-all 换成 head 并行;Megatron-LM 2022 的 SP 是TP 内部的激活优化,只切 LayerNorm/Dropout 这些非 TP 区域。两者都叫 SP,但切分范围和通信原语都不同。Megatron-Core 的 CP 文档特意把这条写出来,作为 CP 与 “prior SP” 的区别(context_parallel.md:19)。

03

SP 在 TP 的缝里切序列,CP 让每张卡只算自己的 Q

SP 的 g/ḡ 与 TP 的 f/f̄ 对齐;CP 的 KV 沿环走,Q 不动。

SP:all-gather 进、reduce-scatter 出

论文把 MLP 的 LayerNorm → 线性 → GeLU → 线性 → Dropout 拆成下面这样。上标 s 表示按 sequence 切,h 表示按 hidden 切,c/r 表示按列/行切权重:

[Y1s, Y2s] = LayerNorm([X1s, X2s])
Y = g(Y1s, Y2s)   ←  forward: all-gather,backward: reduce-scatter
[Z1h, Z2h] = [GeLU(Y·A1c), GeLU(Y·A2c)]
W1 = Z1h·B1r,   W2 = Z2h·B2r
[W1s, W2s] = ḡ(W1, W2)   ←  forward: reduce-scatter,backward: all-gather
[V1s, V2s] = [Dropout(W1s), Dropout(W2s)]
论文 §4.2.2 式 (3),以 2-way TP 为例。LayerNorm 和 Dropout 在 sequence 切分后的本地块上做;g/ḡ 是 TP 的 f/f̄ 与序列转换的融合

为什么要把 all-gather 放在第一个线性层前?论文的拆分让 column-parallel 线性仍看到完整序列 Y,这样 TP 的列并行 GEMM 形状不变;第二个线性层是 row-parallel,输出是各卡的 partial sum,正好用 reduce-scatter 同时完成“跨 TP 求和”和“按 sequence 切回”两件事。论文明确说,这样做是为了把 g/ḡ 与 f/f̄ 合并,避免引入额外的独立通信(§4.2.2)。

反向时两个操作互换:g 的 backward 是 reduce-scatter,ḡ 的 backward 是 all-gather。还有一个细节:Y 在 forward 只存本地的 Yis,backward 需要完整 Y 时再做一次 all-gather;论文把这次通信与 dY 的计算重叠,以压低延迟(§4.2.2)。

源码

Megatron-Core 里 SP 没有独立 process group

在默认的静态配置里,gather_from_sequence_parallel_region 和 reduce_scatter_to_sequence_parallel_region 拿的就是 tensor model parallel group(megatron/core/tensor_parallel/mappings.py:528-549)。线性层在 forward 里 all-gather 输入、在 backward 里 reduce-scatter 输入梯度,group 都是 tp_group(layers.py:787-793、864-873)。开启 reduce_scatter_embeddings 时,embedding 输出可以直接 reduce-scatter 成 sequence-sharded(layers.py:506-519)。TP size 为 1 时,sequence_parallel 会被直接关掉(layers.py:1311-1317)。

另一个实现细节:开启 SP 后,transformer block 会 fork CUDA RNG tracker(transformer_block.py:645-648),保证 Dropout 的随机 mask 在 TP rank 之间可复现。

CP:Q 留在本地,KV 沿环传递

CP 把网络输入和所有激活沿 sequence 切成 CP 份。Linear、LayerNorm 这些逐 token 模块不需要跨 rank,直接算本地块即可;只有 attention 需要全序列 KV。Megatron-Core 的 CP 文档把流程写得很清楚:attention 前 all-gather KV,反向对 KV 的激活梯度做 reduce-scatter;每张卡 forward 只存本地 KV 块,backward 再 gather 一次(context_parallel.md:19)。

  1. 切输入:每张 CP rank 拿到 N/CP 个 token;QKV 投影在本地做。
  2. 转 KV:每张卡把自己的 KV 块传给环上的下一个 rank,同时接收上一个 rank 的 KV 块。Q 块不动。
  3. 局部 attention:用本地 Q 对当前 KV 块做 attention,在线 softmax 合并各块的统计量;重复 CP−1 步,直到本地 Q 看过全部 KV。
  4. 输出:attention 输出留在本地 Q 块上,后续 MLP/LayerNorm 继续本地计算。

Ring Attention 的 Algorithm 1 就是这个循环:每个 host 负责一个 query block,KV block 在 host 之间轮转;只要单块计算时间大于 KV 传输时间,通信就被完全重叠(§3.1、Algorithm 1)。Megatron-Core 的实现不自己写 attention kernel,而是把 CP 参数交给 Transformer Engine 的 TEDotProductAttention:cp_group、cp_global_ranks、cp_stream,默认 cp_comm_type="p2p"(megatron/core/extensions/transformer_engine.py:2405-2429)。普通 DotProductAttention 明确不支持 CP,会直接 assert(dot_product_attention.py:57-59)。

CP group 在 parallel_state.py:1270-1297 里按 token rank generator 的 cp 轴建立;如果启用 hierarchical CP,还会按 hierarchical_context_parallel_sizes 再建子组(:1284-1297)。a2a+p2p 这种层次化通信要求 TE ≥ 1.12(transformer_engine.py:2419-2427)。

zigzag

causal mask 下,rank r 拿的是前后两段,不是连续一段

如果每张卡只拿连续的一段,causal attention 下后面的 rank 要算更多 KV 块,负载会倾斜。Megatron-Core 的 CPLayout 支持 contiguous 和 zigzag;zigzag 下 rank r 拿 front 段和 back 段(context_parallel/layout.py:83-105),让 causal 工作量在 CP rank 之间更均衡。代价是序列长度必须能被 2×CP 整除(context_parallel/utils.py:80-98),否则代码会抛 ValueError(context_parallel/utils.py:80-98),需要先 pad。文档还说,CP 用现成的 flash/cuDNN kernel,并避免为下三角 mask 做额外的逐块计算(context_parallel.md:21)。

KV 的体积可以靠 GQA/MQA 降:Megatron 文档指出,KV 交换量取决于 KV head 数,而不是完整 hidden 维(context_parallel.md:19)。本页后面的算例里,MHA 的 hkv=8192 和 GQA 的 hkv=1024 相差 8×。

最后强调:CP 不减少 attention 的总 FLOPs。每张卡用 N/CP 个 Q 对 N 个 KV 做 attention,单卡计算量约为全量的 1/CP;所有 CP rank 加起来仍是 N² 量级。CP 换到的是每卡显存和每卡计算,不是总计算量。

04

SP 不增加带宽,CP 每层换 KV:省下的显存和付出的字节都能算

把公式代进 22B 的 SP 配置和一组 CP 的 KV 配置,看看数字落在哪。

SP 的显存账:只省复制项

把 Eq.2 与 Eq.4 相减:

MTP − MTP+SP = 10·s·b·h·(1 − 1/t)
省下的正好是 Eq.2 里不随 t 缩小的 10sbh 项减去 SP 后仍保留的 10sbh/t;attention 项 5abs²/t 两边相同,不受 SP 影响

Table 2(本页计算,公式来自 §4.2.1 Eq.2 与 §4.2.2 Eq.4):参考配置 s=4096、b=1、h=6144、a=64、t=8,按 bf16 每元素 2 字节换算。论文公式以“元素数”计,这里乘 2 得到字节。

项公式元素数bf16 字节说明
TP 无 SPsbh(10 + 24/t + 5as/(ht))998,244,3521,996,488,704 B = 1.996 GBEq.2
TP + SP(sbh/t)(34 + 5as/h)778,043,3921,556,086,784 B = 1.556 GBEq.4
SP 省下10sbh(1 − 1/t)220,200,960440,401,920 B = 440.4 MB占 Eq.2 的 22.1%

这张表也解释了为什么 SP 单独并没有 5×:s=4096 时 attention 项已经很大,省下的 440.4 MB 只占 1.996 GB 的 22.1%。论文的 5× 是 SP + selective recompute 合起来、相对 TP baseline 的结果(Fig.7、Abstract);SP 单独解决的是 LayerNorm/Dropout 这部分复制激活。序列越长,attention 项占比越高,SP 的相对节省越小——这个趋势在 07 节展开。

自己动手:拖 t 和 s,看 SP 前后的每层激活

模型固定 22B 的 h=6144、a=64、b=1,按 bf16 每元素 2 字节。只算 Eq.2/Eq.4 的每层激活,不含参数、梯度、优化器状态、通信和 allocator 开销。

无 SP 每层
1.996 GB
有 SP 每层
1.556 GB
SP 省下
440.4 MB
相对 Eq.2
22.1%
当前读数
t=8,s=4096:Eq.2=1.996 GB,Eq.4=1.556 GB,省下 440.4 MB(相对 Eq.2 的 22.1%)。
手算:两组参数,SP 前后每层激活 与 JS 输出逐位一致

组 1:t=8,s=2048,b=1,h=6144,a=64,bf16。

  • sbh = 2048×1×6144 = 12,582,912。
  • 5as/(ht) = 5×64×2048/(6144×8) = 655,360/49,152 = 13.3333。
  • Eq.2:12,582,912×(10+24/8+13.3333) = 12,582,912×26.3333 = 331,350,016 元素;×2 = 662,700,032 B = 0.663 GB。
  • Eq.4:(12,582,912/8)×(34+5×64×2048/6144) = 1,572,864×140.6667 = 221,249,536 元素;×2 = 442,499,072 B = 0.442 GB。
  • 省下:331,350,016−221,249,536 = 110,100,480 元素;×2 = 220,200,960 B = 220.2 MB;110,100,480/331,350,016×100 = 33.2%。

组 2:t=8,s=4096,其余相同。

  • sbh = 4096×6144 = 25,165,824;5as/(ht) = 1,310,720/49,152 = 26.6667。
  • Eq.2:25,165,824×(10+3+26.6667) = 25,165,824×39.6667 = 998,244,352 元素;×2 = 1,996,488,704 B = 1.996 GB。
  • Eq.4:(25,165,824/8)×(34+5×64×4096/6144) = 3,145,728×247.3333 = 778,043,392 元素;×2 = 1,556,086,784 B = 1.556 GB。
  • 省下:998,244,352−778,043,392 = 220,200,960 元素;×2 = 440,401,920 B = 440.4 MB;220,200,960/998,244,352×100 = 22.1%。

简化与未覆盖:只算论文 Eq.2/Eq.4 的每层激活,假设各层相同、b=1、bf16;没有计参数/梯度/优化器状态、TP 的 all-reduce 通信、SP 的 AG/RS 时间、显存碎片和 allocator 开销。Eq.2/Eq.4 本身是论文的近似公式,不含 attention 的临时 workspace 和 activation recompute 的额外驻留。

SP 的通信账:4 次 all-reduce 对 4 次 AG + 4 次 RS

Table 3(本页归纳,来源 §4.2.2):一次 forward+backward 的通信原语计数。论文的原话是 TP 需要 4 次 all-reduce,TP+SP 需要 4 次 all-gather + 4 次 reduce-scatter;ring all-reduce 由 reduce-scatter 和 all-gather 组成,所以总带宽相同。

方案forward + backward 的通信次数总字节
TPall-reduce4基准
TP + SPall-gather + reduce-scatter4 + 4与 TP 相同(ring all-reduce = RS + AG)

“带宽相同”不等于“时间相同”。论文在 22B 单层实测里指出,AG+RS 合起来的执行比单次 all-reduce 慢,因而 SP 的实测前向加速被削掉一部分(§6.2)。源码里只有反向 all-gather(wgrad 重取输入)与反向 reduce-scatter 开了 async_op=True(layers.py:838-840、871-873);forward all-gather 是同步的(layers.py:792)。异步只能重叠,不能消除字节数。

CP 的显存账:每卡只留 N/CP 的 Q 和 KV

CP 下每张卡的序列长度是 N/CP;非 attention 激活近似按 CP 缩小。attention 的 KV 在 forward 只存本地块,backward 再 gather(context_parallel.md:19),所以常驻 KV 也从 N 降到 N/CP。但 attention 的临时 KV 交换量不是 N/CP,而是接近完整 KV——下一段算。

CP 的通信账:每卡收 CP−1 个 KV 块

用 Ring 的逐块轮转算:每张卡有 N/CP 个 token 的 KV 块,环上其余 CP−1 张卡各有一块,所以每张卡每层要接收 CP−1 块 K 和 V。单块字节数是 2·(N/CP)·hkv·bytes,因此:

BKV,rank = (CP − 1) · 2 · (N/CP) · hkv · bytes
= (1 − 1/CP) · 2 · N · hkv · bytes
K 和 V 各一份;hkv = KV heads × head_dim。这是每层、每个 rank 的接收量,不含 TP 的 AG/RS,也不含 QKV 投影本身的通信

Table 4(本页计算,独立 CP 算例):N=32768,MHA 取 hkv=8192(64 heads × 128)、GQA 取 hkv=1024(8 个 KV head × 128),head_dim 128,bf16,32 层;这套配置不沿用同页 22B 的 h=6144/head_dim 96,只用于 KV 通信量对照。统计口径:仅 forward,每个 rank 对每块 K/V 只计一次接收;环上每块还要逐跳转发 CP−1 次,链路总字节另计;反向会再 gather 一次 KV(约翻倍),且不含 KV 梯度 reduce-scatter。最后一列是“每卡收到的 KV”占“完整序列 KV”的比例,即 1−1/CP。

CP每卡 tokens单块 KV每层收到32 层累计占全量 KV
21638464 MiB64 MiB2.0 GiB50.0%
4819232 MiB96 MiB3.0 GiB75.0%
8409616 MiB112 MiB3.5 GiB87.5%
1620488 MiB120 MiB3.8 GiB93.8%

注意这个反直觉的结果:CP 从 2 加到 16,每卡 Q 和激活从 16384 降到 2048,但每层收到的 KV 从 64 MiB 涨到 120 MiB,越来越接近完整的 128 MiB。CP 缩小的是每卡计算和常驻激活,不是每卡 KV 通信。如果把 MHA 的 hkv 换成 8192,CP=8 时每层每卡要收 896 MiB,32 层是 28 GiB,正好是 GQA 的 8 倍(8×)。这就是 GQA/MQA 对 CP 特别重要的原因。

自己动手:拖 CP 和 N,切换 MHA / GQA

固定 N 可被 2×CP 整除、bf16、32 层;只算每层 attention 的 KV 轮转接收量,不含 TP 的 AG/RS、QKV 投影、attention 计算和反向。MHA 取 hkv=8192(64 head × 128),GQA 取 hkv=1024(8 KV head × 128)。

每卡 tokens
4096
每层收到 KV
112.0 MiB
32 层累计
3.5 GiB
占全量 KV
87.5%
当前读数
CP=8,N=32768:每卡 4096 token,单块 KV 16.0 MiB,每层收到 112.0 MiB,32 层 3.5 GiB,占全量 KV 的 87.5%。
手算:CP=4 与 CP=8 的每卡序列和 KV 字节 与 JS 输出逐位一致

组 1:CP=4,N=32768,GQA hkv=1024,bf16,32 层。

  • 每卡 tokens:32768/4 = 8192。
  • 单块 KV:2×8192×1024×2 = 33,554,432 B = 32 MiB。
  • 每层收到:(4−1)×32 = 96 MiB。
  • 32 层:32×96 = 3072 MiB = 3.0 GiB。
  • 占全量 KV:(4−1)/4×100 = 75.0%(完整 KV 为 2×32768×1024×2 = 128 MiB)。

组 2:CP=8,N=32768,其余相同。

  • 每卡 tokens:32768/8 = 4096。
  • 单块 KV:2×4096×1024×2 = 16,777,216 B = 16 MiB。
  • 每层收到:(8−1)×16 = 112 MiB。
  • 32 层:32×112 = 3584 MiB = 3.5 GiB。
  • 占全量 KV:(8−1)/8×100 = 87.5%。

简化与未覆盖:只算 forward 的 KV 接收量,且每个 rank 对每块 K/V 只计一次接收(环上每块逐跳转发 CP−1 次,链路总字节另计);反向还要再 gather 一次 KV(context_parallel.md:19),实际字节约翻倍;也没有计 KV 梯度 reduce-scatter、TP 的 AG/RS、QKV 投影、RoPE 重排、attention 计算、packed sequence 的 padding、显存碎片和通信延迟;没有计 MHA/GQA 的 head 维不整除等实现约束。这里的“全量 KV”指完整序列的 K+V,不含 Q。

作为对照,Ulysses 的每层每链路通信量是 4Nh/P,其中 P 是序列并行度(§3.2);它随 P 缩小,而 Ring/CP 的每卡 KV 接收量随 CP 趋近 2Nhkv,不随 CP 缩小。两者的通信复杂度不同,不能只看“都叫序列并行”。

Ring 的重叠条件:block 必须够大

Ring Attention 把“计算 ≥ 传输”写成 4dc²/F ≥ 4cd/B,化简得 c ≥ F/B:block 大小至少等于设备的 FLOPS/带宽比(§3.1)。Table 5 转录论文 Table 2 的 5 行硬件参数;如果每卡分到的 token 数小于最小 block,KV 传输就藏不进计算。

Table 5(转录 Ring Attention Table 2,5 行全录):最小 block c=F/B,最小序列 s=6c。FLOPS 是峰值,带宽是主机间单向带宽;表中 K 表示 ×10³。

硬件FLOPS (TF)HBM (GB)带宽 (GB/s)最小 block (K tokens)最小序列 (K tokens)
A100 NVLink312803001.06.2
A100 InfiniBand3128012.524.5149.5
TPU v3123161121.16.6
TPU v4275322681.06.2
TPU v5e196161861.16.3

这张表给了一个很具体的边界:在 A100 NVLink 上,block 只要约 1K token 就能重叠;换到 A100 InfiniBand,最小 block 变成 24.5K token,序列至少要 149.5K。同样是 CP=8、每卡 4096 token,在 NVLink 上通信可以藏住,在 IB 上就藏不住——这时要么加 block,要么换 Ulysses 这类通信量随 P 缩小的方案。

05

前向 AG/RS 夹住 TP 线性层,CP 在 attention 前后换 KV

把数据流拆成 SP 和 CP 两条线,再看它们在同一个 transformer layer 里怎么叠。

CP=4:Q 块不动,KV 块沿环走 CP−1 步 rank 0 rank 1 rank 2 rank 3 Q0 + KV0 Q1 + KV1 Q2 + KV2 Q3 + KV3 本地 Q 块 本地 Q 块 本地 Q 块 本地 Q 块 KV0 KV1 KV2 KV3 → KV0:每步把当前 KV 块传给下一个 rank Q 块始终留在原 rank;CP−1 步后,每个 Q 块都看过全部 KV。 attention 输出留在本地 Q 块上;KV 只作为临时输入,不随输出回传。 每张卡常驻:本地 Q + 本地 KV + 一块接收缓冲。图中省略 TP 组与 packed sequence。 依据:Ring Attention Algorithm 1;Megatron context_parallel.md:19(本页绘制)
图 1(本页绘制):CP=4 的 ring KV 传递。每个 rank 用本地 Q 块对轮转过来的 KV 块做 attention;传输与计算重叠的前提是 block 足够大(Table 5)。图中没有画 TP 的 QKV 投影和 RoPE 重排,也没有画反向的 KV 梯度 reduce-scatter;这些步骤在 Table 6 里列出。

Table 6(本页归纳):同一个 transformer layer 里 SP 与 CP 的数据流。SP 列来自 §4.2.2 与 Megatron-Core 源码;CP 列来自 context_parallel.md:19-21 与 TE 封装。

阶段SP(叠加在 TP 上)CP(独立轴)
层输入开启 reduce_scatter_embeddings 时,embedding 输出 reduce-scatter 成 sequence-sharded(layers.py:506-519)按 sequence 切成 CP 份;每卡 N/CP token(megatron/core/context_parallel/utils.py:56-104;入口分派 megatron/core/utils.py:2830-2884)
LayerNorm / Dropout本地做,只看 s/t 个 token本地做,只看 N/CP 个 token
QKV / MLP 线性all-gather → TP 列并行 GEMM → 行并行 GEMM → reduce-scatterQKV 投影本地做;MLP 逐 token,本地做
Attention在 TP 内做;KV 随 head 切分,不需要跨 TP 换 KV本地 Q 对轮转 KV 做 attention;KV 从 CP 环上收全序列
反向g 变 reduce-scatter,ḡ 变 all-gather;额外 all-gather 存下的 Yis,并与 dY 计算重叠KV 重新 gather;KV 激活梯度 reduce-scatter;cp_stream 与计算重叠
输出仍是 sequence-sharded,进入下一个 LayerNorm仍是 sequence-sharded,进入下一层

组合方式是乘法:总卡数 TP × CP × PP × DP(context_parallel.md:38)。SP 不是独立轴,它复用 TP group;CP 是独立轴,KV 通信发生在“其他 TP group 的对应 rank”之间(context_parallel.md:19)。PP 只切层,不改变单层内的 SP/CP 数据流;DP 复制模型、切 batch。实际训练里常见的是 TP8 + CP2/4/8 + PP + DP 的组合 推断(依据:Megatron-Core CP 文档只说明 CP 可与 TP/PP/DP 组合),具体比例取决于模型大小、序列长度和互联带宽。

一个实现上的重叠点:SP 只在反向的 all-gather 与 reduce-scatter 上开了异步(layers.py:838-840、871-873),forward all-gather 是同步的(layers.py:792);CP 的 TE 实现则单独开了 cp_stream(transformer_engine.py:2409-2415),目的都是把通信放到计算旁边。但异步不等于免费:如果 block 太小或带宽不够,通信仍会露在关键路径上。

06

SP + selective recompute 让 530B 在 2240 卡上跑到 54.2% MFU,Ring 把上下文拉到 30M+

论文实测的条件见表与正文:模型规模、并行配置、序列长度、是否混合精度。

SP:显存省下来后,少做重算才是大头

先看单层时间。SP 论文在 22B 模型的一层上做了对照(Table 4):

Table 7(转录 SP 论文 Table 4):22B 模型单层,TP8,forward+backward。Overhead 是相对 “Baseline no recompute” 的 19.6 ms。SP 单独只把 combined 从 19.6 ms 降到 19.0 ms(论文写 −3%),真正的大头是 selective recompute 把 27.2 ms 的 full recompute 压到 20.9 ms。

配置Forward (ms)Backward (ms)Combined (ms)Overhead
Baseline no recompute7.711.919.6—
Sequence Parallelism7.211.819.0−3%
Baseline with recompute7.719.527.239%
Selective Recompute7.713.220.97%
Selective + Sequence7.213.120.34%

端到端结果更能说明 SP 的定位:它本身不直接提速多少,但省出的显存让模型可以少做重算。Table 8 是论文 Table 5 的四个配置,TP 都是 8,序列长度 2048,没有使用 DP;Present work 指 SP + selective recompute。

Table 8(转录 SP 论文 Table 5 与 Table 3):模型规模、GPU 数、并行配置与迭代时间。22B/175B/530B/1T 的 PP 分别是 1/8/35/64;序列长度 2048,vocab 51200。最后的 “Increase” 是论文列,口径是相对 full recompute 的吞吐提升;MFU 和 HFU 是同一个 Present work 配置下的两个利用率,不是前后对比。

模型GPU 数TPPPFull recompute (s)Present work (s)IncreaseMFUHFU
22B8811.421.1029.0%41.5%43.7%
175B648818.1313.7531.8%51.4%52.8%
530B28083549.0537.8329.7%56.0%57.0%
1T51286494.4271.4932.1%56.3%57.0%

54.2% MFU 的完整口径是 Present work(SP + selective recompute):530B GPT-3-style(即 MT-NLG 530B,不是 175B 的 GPT-3)扩到 8-way DP、2240 张 A100,迭代时间从 37.83 s 变成 39.15 s,MFU 从 56.0% 降到 54.2%;论文称这比 full recompute 的 42.1% 快 29%(§0 Abstract、§6.3)。Table 8 的 530B MFU 是不用 DP 的 280 卡结果(56.0%),HFU 是 57.0%;两个数不要和 54.2% 混用。

显存侧,论文 Fig.7 的结论是:SP 单独和 selective recompute 单独都把激活压到接近一半;两者合用约 5×,降到 TP baseline 的 20% 以下;full recompute 是 10%。这里的 5× 是合用的降幅,不是 SP 单独的降幅——SP 单独只去掉 Eq.2 里那部分复制激活。

CP / Ring:最多(up to)随设备数线性扩

Ring Attention 的实验条件先钉住:LLaMA 架构,3B/7B/13B/30B,用 FSDP 分片模型并配合 full gradient checkpointing,所有结果用全精度而不是混合精度,且不加 TP(§4、§5.1)。在这个条件下实测:32 张 A100 上,7B 模型做到超过 1M tokens,是此前最佳结果的 32 倍;TPUv4-512 上扩展 256 倍、可训练超过 30M tokens(§5.1)——256× 是这组配置的实测值,不是 512×。把论文 Table 3 横着看,倍数还取决于设备和模型:TPUv4-1024 上 3B/7B 是 512×,13B/30B 是 256×;设备也并不全给序列,FSDP 会先占掉一部分设备做模型分片,能用于扩序列的只是剩余设备(Appendix A),所以论文写的是 “up to device count times”,不能按 512× 直接外推。

§1 引言给的口径更大:比此前内存高效方案的最佳结果长 500 倍以上、可训练超过 100M tokens;这是引言里的主张,不是 Table 3 某个格子的实测值。v4 摘要只说 “up to device count times” 和 “millions of tokens”。线性扩的前提是每卡 block 够大:Table 5 的 c ≥ F/B 是通信重叠门槛,s ≥ 6c 是六块显存的容量门槛;c 小于 F/B 时 KV 传输会露在计算外。

Ulysses 的实测更偏“吞吐随 GPU 数 scaling”。Table 9 转录它的 Table 2/3:GPT-7B、dense attention、global batch 8,序列长度和 GPU 数同比例增长时,每 GPU TFLOPs 从 161.36 降到 147.40,迭代时间从 9676.76 ms 涨到 33486.5 ms——工作量随 N² 涨,吞吐只轻微下降。

Table 9(转录 DeepSpeed-Ulysses Table 2/3;Table 3 的 131072/128 行与 Table 2 第 2 行数值相同,合并列出):GPT-7B、dense attention、global batch 8。前三行是固定序列 131072、增加 GPU 的强 scaling;后两行是序列与 GPU 同比例增加的弱 scaling。

SeqlenGPUsTime (ms)TFLOPs / GPU说明
1310726432432.13165.53固定序列,强 scaling
13107212817052.51157.41固定序列,强 scaling
1310722569886.70136.09固定序列,强 scaling
65536649676.76161.36序列与 GPU 同比例
26214425633486.50147.40序列与 GPU 同比例

Ulysses v2 摘要的原词是 “2.5x faster with 4x longer sequence length”(4× 序列、快 2.5×,不是“最高 2.5×”);§1 贡献列表另有 “throughput improvements of up to 2.5x” 的表述。§4 的对比基线是 Megatron-LM 的 sequence parallelism。它的通信量 4Nh/P 比 Megatron SP 的 4Nh 小 P 倍(§3.2),所以序列越长、P 越大,Ulysses 的通信优势越明显;代价是 all-to-all 和 head 数整除约束。

Megatron-Core 的 CP 文档用 Figure 2 给出 175B GPT 在 TP+CP 组合下相对 full recomputation(TP8CP1)的加速曲线,本页未转录该图的读数;文档的定性结论是 TP+CP 能超过 full recomputation,因为去掉了大部分重算开销(context_parallel.md:32-34)。

07

小序列、因果 mask、TP 耦合和通信重叠,决定这些数字能不能复用

这几条边界不改变结论,但会改变你该选 SP 还是 CP。

序列越短,SP 的相对节省越大;序列越长,CP 才越有意义

由 Eq.2/Eq.4,SP 省下的绝对字节是 10sbh(1−1/t),随 s 线性增长;但分母里的 attention 项 5abs²/t 按 s² 增长,所以相对节省随 s 增大而下降。固定 h=6144、a=64、t=8:

Table 10(本页由 Eq.2/Eq.4 计算):SP 相对 Eq.2 的每层激活节省比例。s=1024 时非 attention 项占比高,省 44.5%;s=16384 时 attention 项主导,只剩 7.3%。绝对字节仍在涨,只是占比变小。

sSP 省下比例(相对 Eq.2)读法
102444.5%非 attention 激活占比高,SP 很划算
204833.2%论文 Table 3 的序列长度
409622.1%本页 Table 2 的参考配置
819213.2%attention 项开始主导
163847.3%SP 仍在省,但已不是主要矛盾

这也解释了为什么长上下文场景需要 CP:SP 只去掉复制激活,attention 的 s² 项还在。CP 把每卡 Q 和激活降到 1/CP,但每卡 KV 通信趋近完整 KV;所以 CP 适合“单卡放不下、且互联带宽足够”的长序列,不适合短序列或低带宽集群。

因果 mask 和 packed sequence 会改变布局

SP 与 TP 是耦合的,CP 与 TP 是正交的

SP × TP

SP 没有自己的 group,TP=1 时自动关闭

SP 的 AG/RS 用的就是 TP group(mappings.py:528-549);TP size 为 1 时 sequence_parallel 被禁用(layers.py:1311-1317)。所以 SP 不能脱离 TP 单独开,也不能独立扩序列——它的并行度和 TP 绑死。

TP 太大还会让 Linear 的计算短到藏不住通信(context_parallel.md:32);这时继续加 TP 不如加 CP。

CP × TP

CP 是独立轴,KV 只在对应 TP group 之间换

CP 有独立的 process group(parallel_state.py:1270-1297),总卡数是 TP×CP×PP×DP(context_parallel.md:38)。KV 通信发生在“其他 TP group 的对应 rank”之间(context_parallel.md:19),不会跨 TP 的 hidden 分片。

所以 CP 可以和 TP/PP/DP 自由组合;SP 则是 TP 的内部优化,组合时只跟着 TP 走。

通信重叠不是自动的

它们各自不解决什么

推断 选择顺序可以按成本模型来:TP 已开且 LayerNorm/Dropout 激活显著时,先开 SP(几乎不改代码、不增加带宽);单卡 attention 激活或计算放不下时,再上 CP;head 数能被并行度整除、且 all-to-all 互联好时,Ulysses 的 O(N/P) 通信更有优势;需要“设备数×上下文”且能接受 O(N) KV 轮转时,用 Ring/CP。依据是 §4 的通信/显存公式与 §6 的实测条件,不是论文的直接建议。