Ulysses vs Ring Attention:序列并行深度对比

背景

训练超长序列 LLM 时,单卡显存放不下完整的 KV cache,需要对序列维度做并行(Sequence Parallelism)。目前主流有两种方案:

  • Ulysses(DeepSpeed-Ulysses):两次 All-to-All,按 head 切分
  • Ring Attention:环形传递 KV blocks,分块计算

两者目标相同,但设计假设完全不同。本文从原理、通信量、代码实现到架构选择动机,做完整对比。


一、核心思想对比

Ulysses Attention

1
2
3
Step1: [N/P, h, d] ──All-to-All──→ [N, h/P, d]
Step2: 本地做标准 Attention(每张卡拿到全序列、部分 head)
Step3: 输出 ──All-to-All──→ 回到 [N/P, h, d]

关键:两次 All-to-All 把”序列切”转成”head 切”,每张卡对全序列做部分 head 的 attention。

Ring Attention

1
2
3
4
Round 0: attn(Q_i, K_i, V_i)           ← 本地 attention
Round 1: 收到 K_{i-1}, V_{i-1} → attn(Q_i, K_{i-1}, V_{i-1})
...
Round P-1: 收到所有 KV → online softmax 合并结果

关键:KV 沿环形传递,每张卡只存自己的 KV chunk,计算和通信完全重叠。


二、通信量数学推导

Ulysses:All-to-All 的 $(P-1)/P^2$

Ulysses 输入是 [N/P, h, d](序列已被切,head 完整),输出是 [N, h/P, d](序列完整,head 被切)。

每 GPU 发送 P-1 个 chunk,每个 chunk 大小:

$$
\frac{N}{P} \times \frac{h}{P} \times d = \frac{N \cdot h \cdot d}{P^2}
$$

总发送量:

$$
\text{send per GPU} = (P-1) \times \frac{N \cdot h \cdot d}{P^2} = N \cdot h \cdot d \cdot \frac{P-1}{P^2}
$$

两个 1/P 的来源:

  • 第一个 1/P:序列维度已被切(N/P
  • 第二个 1/P:head 维度再切一次(h/P

Ring:P2P 的 $(P-1)/P$

Ring 只传 KV(Q 不动),每轮传 (N/P) × d_kv

$$
\text{send per GPU} = (P-1) \times \frac{N}{P} \times d_{kv} = N \cdot d_{kv} \cdot \frac{P-1}{P}
$$

只有一个 1/P(序列切分),没有 head 切分。

对比(P=8, h=64, d=128, d_kv=576)

方法 通信量/GPU 比值
Ulysses (MHA) $N × 64 × 128 × 7/64 = N × 896$ 1.8×
Ring (MHA KV) $N × 128×2 × 7/8 = N × 224$
Ring (MLA) $N × 576 × 7/8 = N × 504$

注意:MHA 的 KV 是 h × d × 2 = 16384/token,MLA 的 c_kv 只有 576/token,这是后续分析的关键。


三、MLA 对 Ulysses 的致命问题

MLA 的 KV cache 结构

DeepSeek-V3/V4 使用 MLA(Multi-head Latent Attention),KV cache 不是 multi-head 的:

1
2
3
c_kv [seq, 512]     ← 压缩的共享 latent
k_rope [seq, 64] ← RoPE 位置编码部分
合计:576 dim/token(vs MHA 的 16384 dim/token)

**只有 1 个”头”**,无法按 head 切分。

DeepSpeed Ulysses 代码验证

1
2
3
4
# deepspeed/sequence/layer.py 核心逻辑
q = _SeqAllToAll.apply(group, query, scatter_idx=2, gather_idx=0)
k = _SeqAllToAll.apply(group, key, scatter_idx=2, gather_idx=0) # K 和 Q 对称处理
v = _SeqAllToAll.apply(group, value, scatter_idx=2, gather_idx=0)

Q、K、V 走完全相同的 All-to-All 路径,没有”KV 走 All-Gather”的分支。当 num_kv_heads=1 时:

1
2
1 KV head / 4 GPUs → [1, 0, 0, 0]
GPU 1-3:没有 KV head → 无法计算 ❌

Ulysses SP 上限 = num_kv_heads

模型 num_kv_heads Ulysses SP 上限 实际可用性
DiT (视觉) 32 (MHA) 32
LLaMA-3 8 (GQA) 8 ⚠️ 受限
DeepSeek-V3/V4 1 (MLA) 1 ❌ 不可用

四、MLA 场景:Ring vs Ulysses 精确对比

通信量(P=8)

Ring Attention(MLA)

  • 只传 c_kv:每轮 (N/8) × 576,共 7 轮
  • 总计:$N × 504$ / GPU

Ulysses(MLA,Q All-to-All + KV All-Gather)

  • Q All-to-All:$7 × (N/8) × 64 × 512 = N × 28,672$
  • KV All-Gather:$7 × (N/8) × 576 = N × 504$
  • Output reverse:$N × 28,672$
  • 总计:$N × 57,848$ / GPU

Ring 比 Ulysses 省 115×。

根本原因:MLA 的非对称性——Q 巨大(32768 dim/token)、KV 极小(576 dim/token)。Ring 只移动小的 KV,Ulysses 被迫移动巨大的 Q。

缩放性对比

P MHA Ulysses MLA Ring MLA 比 MHA 省
4 $N × 6,144$ $N × 432$ 14.2×
8 $N × 3,584$ $N × 504$ 7.1×
16 $N × 1,792$ $N × 540$ 3.3×
64 $N × 428$ $N × 567$ 0.75×(MHA 反超)

交叉点:$P = 4hd/d_{kv} ≈ 57$,但 Ulysses SP 上限 = 64,所以实践中 MLA Ring 几乎总是更优


五、为什么 Ulysses 在视觉模型流行,文本 LLM 不用?

四个结构性原因

1. KV head 数量趋势

1
2
3
4
2020: MHA (GPT-3) num_kv_heads = 96  → Ulysses 随便用
2022: MQA (PaLM) num_kv_heads = 1 → Ulysses 废了
2023: GQA (LLaMA-2) num_kv_heads = 8 → Ulysses 受限
2024: MLA (DeepSeek-V3) num_kv_heads = 1 → Ulysses 废了

文本 LLM 全面转向 GQA/MLA 压缩 KV heads,Ulysses 的前提条件被釜底抽薪。

2. 文本 LLM 的 TP 已经做了同样的事

1
2
Tensor Parallelism: 按 head 切权重 → 本地算 attention → All-Reduce
Ulysses: 按 head 切数据 → 本地算 attention → All-to-All

本质重叠,TP 已经切了 head 之后,Ulysses 没有额外收益。

3. 推理阶段 Decode 占 80% 时间

1
2
Prefill: ~20% 时间(可以序列并行)
Decode: ~80% 时间(Q=1 token,序列并行无用)

Ulysses 对 Decode 完全无用。视觉扩散模型没有 Decode 阶段,全程受益。

4. 视频生成 token 数极高

1
Sora 级别: (64×64) × 120 frames = 491,520 tokens

必须序列并行,且 DiT 用标准 MHA(32 heads),Ulysses 完美适配。

一句话总结

Ulysses 在视觉模型流行 = MHA(head 够多)+ 无 decode + 没被 TP 覆盖 + 序列极长。文本 LLM 四条全占不到。


六、DeepSeek 的实际选择

训练:全程 Ring/CP

DeepSeek-V3 技术报告显示,长上下文扩展(32K→128K)用的是 Ring Attention(Context Parallel),不是 Ulysses:

  • MLA 只有 1 个 KV latent → Ulysses 物理上不可用
  • KV 只有 576 dim → Ring 通信量本来就小
  • Ring 的通信-计算 overlap → 长序列时通信几乎免费

推理:SGLang 的 CP 配置

1
2
3
--enable-nsa-prefill-context-parallel
--attn-cp-size 8
--nsa-prefill-cp-mode round-robin-split

选择 Ring 风格 CP 的原因:

  • MLA 的 KV 只有 1 个共享 latent,切不了 head
  • KV 极小(576 dim),Ring 通信可接受
  • round-robin 切分 tokens 均衡负载

七、LoRA 压缩能让 Ulysses 复活吗?

思路:在压缩态做通信

MLA 的 Q 路径:hidden(7168) → q_lora(1536) → expand → Q[128, 576]

能否在 q_lora 维度(1536)做 All-Gather,而非展开后的 Q(73728)?

通信量更新(LoRA 压缩版)

通信 数据 P=8 总量
q_lora All-Gather $N × 1536 × 7/8$ $N × 1,344$
c_kv All-Gather $N × 576 × 7/8$ $N × 504$
o_lora Reduce-Scatter $N × 1024 × 7/8$ $N × 896$
总计 $N × 2,744$

vs 原始 Ulysses(展开态):$N × 57,848$ → 压缩 21×

但仍然比 Ring 大 5.4×

1
2
Ring:              N × 504
Ulysses (LoRA): N × 2,744 ← Q 和 O 的压缩态还是要传

根本原因:Ring 的 Q 和 O 根本不过网络,留在本地计算。Ulysses 无论怎么压缩,都要把 Q/O(或其压缩态)在网络上搬一次,这是架构级差距,压缩只能缩小、不能逆转。


八、全景决策树

1
2
3
4
5
6
7
8
9
10
num_kv_heads >= SP_size?

├─ YES (DiT, ViT, 视觉 MHA)
│ └─ Ulysses ✅(All-to-All,通信量 1/P²)

└─ NO (GQA=8, MLA=1, 文本 LLM)
├─ 训练:Ring/CP ✅
└─ 推理:
├─ Prefill:Ring/CP
└─ Decode:partial attn + All-Reduce

总结

Ulysses 的核心优势是 1/P² 通信缩放,但前提是 num_kv_heads ≥ SP。MLA 把 KV 压缩到 1 个 latent,直接废掉这个前提。Ring 只传 KV(576 dim),Q 留本地,反而成了 MLA 的最优搭档。

这不是巧合——MLA 和 Ring CP 是刻意协同设计,不是将就。


相关阅读:DeepSeek-V3 技术报告、DeepSpeed-Ulysses 论文(arXiv:2309.14509)、Ring Attention 论文(arXiv:2310.01889)