TP 把矩阵乘切到多卡,却把 LayerNorm、Dropout 的激活整条序列复制在每张卡上。SP 把这段非 TP 区域沿 sequence 切开,并把 all-gather / reduce-scatter 塞进 TP 原来的通信边界;CP 更进一步,连 attention 的输入和激活都按序列切,每张卡只算自己的 Q,KV 则沿环传递。 两者都能省显存,但一个不增加带宽,另一个每层都要换 KV。
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 比例缩小。
先看清 TP 的激活账单:哪一项随 TP 缩小,哪一项不动。
张量并行(TP)把 attention 和 MLP 里的矩阵乘按 hidden 维切开,块内的激活也随 hidden 维切开。但 QKV 投影、MLP 第一层的输入,以及 LayerNorm 和 Dropout 这些逐 token 操作,并不在 TP 的切分范围里。它们要么在 TP 区域之外,要么需要完整 hidden 维,于是每张 TP rank 都保留一份完整序列的激活。
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 处理的是 TP 留下的“复制区域”:LayerNorm、Dropout、残差等非 TP 激活。它不改变 attention 的计算方式,也不减少 attention 的 s² 项。CP 处理的是 attention 本身:把输入和所有激活沿 sequence 切开,让每张卡只算一部分 Q,再通过 KV 交换看到全序列。Megatron-Core 的 CP 文档把这条界线写得很直接:CP 分割网络输入和所有激活,而此前的 SP 只分割 Dropout 和 LayerNorm 的激活(context_parallel.md:19)。
把几条路放在同一张表里,先分清「省激活」「省通信」「扩上下文」。
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² 激活。
Full activation recomputation 丢掉中间激活,反向从 checkpoint 重放。SP 论文的 22B 单层实测:baseline 的 fwd+bwd 是 19.6 ms,full recompute 变成 27.2 ms,开销 39%(Table 4)。
卡在:重算的是整层,哪怕其中很多激活又小又便宜。SP 的价值之一是省出显存后,可以少做重算。
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 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-gather | 4 次 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 块 KV | attention 激活 + 上下文 | Algorithm 1 |
| CP(Megatron-Core) | 网络输入和所有激活按 sequence | KV all-gather;反向 KV 梯度 reduce-scatter | P2P 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)。
SP 的 g/ḡ 与 TP 的 f/f̄ 对齐;CP 的 KV 沿环走,Q 不动。
论文把 MLP 的 LayerNorm → 线性 → GeLU → 线性 → Dropout 拆成下面这样。上标 s 表示按 sequence 切,h 表示按 hidden 切,c/r 表示按列/行切权重:
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)。
在默认的静态配置里,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 把网络输入和所有激活沿 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)。
N/CP 个 token;QKV 投影在本地做。CP−1 步,直到本地 Q 看过全部 KV。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)。
如果每张卡只拿连续的一段,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 换到的是每卡显存和每卡计算,不是总计算量。
把公式代进 22B 的 SP 配置和一组 CP 的 KV 配置,看看数字落在哪。
把 Eq.2 与 Eq.4 相减:
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 无 SP | sbh(10 + 24/t + 5as/(ht)) | 998,244,352 | 1,996,488,704 B = 1.996 GB | Eq.2 |
| TP + SP | (sbh/t)(34 + 5as/h) | 778,043,392 | 1,556,086,784 B = 1.556 GB | Eq.4 |
| SP 省下 | 10sbh(1 − 1/t) | 220,200,960 | 440,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 节展开。
模型固定 22B 的 h=6144、a=64、b=1,按 bf16 每元素 2 字节。只算 Eq.2/Eq.4 的每层激活,不含参数、梯度、优化器状态、通信和 allocator 开销。
组 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。12,582,912×(10+24/8+13.3333) = 12,582,912×26.3333 = 331,350,016 元素;×2 = 662,700,032 B = 0.663 GB。(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。25,165,824×(10+3+26.6667) = 25,165,824×39.6667 = 998,244,352 元素;×2 = 1,996,488,704 B = 1.996 GB。(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 的额外驻留。
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 的通信 | 次数 | 总字节 |
|---|---|---|---|
| TP | all-reduce | 4 | 基准 |
| TP + SP | all-gather + reduce-scatter | 4 + 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)。异步只能重叠,不能消除字节数。
N/CP 的 Q 和 KVCP 下每张卡的序列长度是 N/CP;非 attention 激活近似按 CP 缩小。attention 的 KV 在 forward 只存本地块,backward 再 gather(context_parallel.md:19),所以常驻 KV 也从 N 降到 N/CP。但 attention 的临时 KV 交换量不是 N/CP,而是接近完整 KV——下一段算。
CP−1 个 KV 块用 Ring 的逐块轮转算:每张卡有 N/CP 个 token 的 KV 块,环上其余 CP−1 张卡各有一块,所以每张卡每层要接收 CP−1 块 K 和 V。单块字节数是 2·(N/CP)·hkv·bytes,因此:
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 |
|---|---|---|---|---|---|
| 2 | 16384 | 64 MiB | 64 MiB | 2.0 GiB | 50.0% |
| 4 | 8192 | 32 MiB | 96 MiB | 3.0 GiB | 75.0% |
| 8 | 4096 | 16 MiB | 112 MiB | 3.5 GiB | 87.5% |
| 16 | 2048 | 8 MiB | 120 MiB | 3.8 GiB | 93.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 特别重要的原因。
固定 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)。
组 1:CP=4,N=32768,GQA hkv=1024,bf16,32 层。
32768/4 = 8192。2×8192×1024×2 = 33,554,432 B = 32 MiB。(4−1)×32 = 96 MiB。32×96 = 3072 MiB = 3.0 GiB。(4−1)/4×100 = 75.0%(完整 KV 为 2×32768×1024×2 = 128 MiB)。组 2:CP=8,N=32768,其余相同。
32768/8 = 4096。2×4096×1024×2 = 16,777,216 B = 16 MiB。(8−1)×16 = 112 MiB。32×112 = 3584 MiB = 3.5 GiB。(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 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 NVLink | 312 | 80 | 300 | 1.0 | 6.2 |
| A100 InfiniBand | 312 | 80 | 12.5 | 24.5 | 149.5 |
| TPU v3 | 123 | 16 | 112 | 1.1 | 6.6 |
| TPU v4 | 275 | 32 | 268 | 1.0 | 6.2 |
| TPU v5e | 196 | 16 | 186 | 1.1 | 6.3 |
这张表给了一个很具体的边界:在 A100 NVLink 上,block 只要约 1K token 就能重叠;换到 A100 InfiniBand,最小 block 变成 24.5K token,序列至少要 149.5K。同样是 CP=8、每卡 4096 token,在 NVLink 上通信可以藏住,在 IB 上就藏不住——这时要么加 block,要么换 Ulysses 这类通信量随 P 缩小的方案。
把数据流拆成 SP 和 CP 两条线,再看它们在同一个 transformer layer 里怎么叠。
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-scatter | QKV 投影本地做;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 太小或带宽不够,通信仍会露在关键路径上。
论文实测的条件见表与正文:模型规模、并行配置、序列长度、是否混合精度。
先看单层时间。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 recompute | 7.7 | 11.9 | 19.6 | — |
| Sequence Parallelism | 7.2 | 11.8 | 19.0 | −3% |
| Baseline with recompute | 7.7 | 19.5 | 27.2 | 39% |
| Selective Recompute | 7.7 | 13.2 | 20.9 | 7% |
| Selective + Sequence | 7.2 | 13.1 | 20.3 | 4% |
端到端结果更能说明 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 数 | TP | PP | Full recompute (s) | Present work (s) | Increase | MFU | HFU |
|---|---|---|---|---|---|---|---|---|
| 22B | 8 | 8 | 1 | 1.42 | 1.10 | 29.0% | 41.5% | 43.7% |
| 175B | 64 | 8 | 8 | 18.13 | 13.75 | 31.8% | 51.4% | 52.8% |
| 530B | 280 | 8 | 35 | 49.05 | 37.83 | 29.7% | 56.0% | 57.0% |
| 1T | 512 | 8 | 64 | 94.42 | 71.49 | 32.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 里那部分复制激活。
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。
| Seqlen | GPUs | Time (ms) | TFLOPs / GPU | 说明 |
|---|---|---|---|---|
| 131072 | 64 | 32432.13 | 165.53 | 固定序列,强 scaling |
| 131072 | 128 | 17052.51 | 157.41 | 固定序列,强 scaling |
| 131072 | 256 | 9886.70 | 136.09 | 固定序列,强 scaling |
| 65536 | 64 | 9676.76 | 161.36 | 序列与 GPU 同比例 |
| 262144 | 256 | 33486.50 | 147.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)。
这几条边界不改变结论,但会改变你该选 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%。绝对字节仍在涨,只是占比变小。
| s | SP 省下比例(相对 Eq.2) | 读法 |
|---|---|---|
| 1024 | 44.5% | 非 attention 激活占比高,SP 很划算 |
| 2048 | 33.2% | 论文 Table 3 的序列长度 |
| 4096 | 22.1% | 本页 Table 2 的参考配置 |
| 8192 | 13.2% | attention 项开始主导 |
| 16384 | 7.3% | SP 仍在省,但已不是主要矛盾 |
这也解释了为什么长上下文场景需要 CP:SP 只去掉复制激活,attention 的 s² 项还在。CP 把每卡 Q 和激活降到 1/CP,但每卡 KV 通信趋近完整 KV;所以 CP 适合“单卡放不下、且互联带宽足够”的长序列,不适合短序列或低带宽集群。
layout.py:83-105);代价是序列长度必须能被 2×CP 整除(utils.py:80-98)。cu_seqlens 元数据;layout.py 提供 contiguous↔zigzag 的转换计划(contiguous_to_zigzag、zigzag_to_contiguous,:582-628)。文档说 CP 支持单向/双向 mask,但 dynamic CP 要求 per-token loss 和 varlen packed data,且不支持 deterministic mode / CUDA graphs / router z-loss(context_parallel.md:38-70)。context_parallel.md:70)。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 有独立的 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 走。
1/t 的直觉(§6.2)。源码只把反向 all-gather 与 reduce-scatter 设为 async_op=True(layers.py:838-840、871-873;forward all-gather 在 layers.py:792 同步),重叠上限受带宽和依赖关系约束。cp_stream:TE 实现单独开 stream(transformer_engine.py:2409-2415),能把 KV 通信放到计算旁边;但如果 KV 体积太大(MHA 而非 GQA),带宽仍是瓶颈。s² 计算量,也不改变主体 GEMM 的 FLOPs;它只是把非 TP 的复制激活按 sequence 切开。1/CP;模型状态(含优化器状态)的内存不受 CP 影响(Ulysses 需要配合 ZeRO-3 才能同时扩模型,§3.3)。(1−1/CP)·2N·hkv,CP 越大越接近完整 KV;这是 Ring/CP 与 Ulysses 的 4Nh/P 最根本的差别。推断 选择顺序可以按成本模型来:TP 已开且 LayerNorm/Dropout 激活显著时,先开 SP(几乎不改代码、不增加带宽);单卡 attention 激活或计算放不下时,再上 CP;head 数能被并行度整除、且 all-to-all 互联好时,Ulysses 的 O(N/P) 通信更有优势;需要“设备数×上下文”且能接受 O(N) KV 轮转时,用 Ring/CP。依据是 §4 的通信/显存公式与 §6 的实测条件,不是论文的直接建议。