Skip to content

优化 FlashMLA 64 头融合注意力内核的 Prefill 与 FP8 Decode 性能 - #227

Open
cb521 wants to merge 6 commits into
deepseek-ai:mainfrom
cb521:perf/v41-fused-h64
Open

cb521 wants to merge 6 commits into
deepseek-ai:mainfrom
cb521:perf/v41-fused-h64

Conversation

@cb521

@cb521 cb521 commented Sep 24, 2026

Copy link
Copy Markdown

(1)这个 kernel 将 Q RMSNorm(可选,本次关闭)→ Q RoPE → 稀疏注意力(QK、Softmax、PV)→ 输出逆 RoPE → FP8 量化 融合在一次 kernel 执行中。

(2)**Prefill 和 Decode 共用一份模板实现,通过编译期分支生成各自的内核。**本次优化 H=64、关闭 QNorm 的路径:Prefill 使用 BF16 KV,Decode 使用全 FP8 KV;优化入口限定 B200(148 SM)。

(3)使用 DeepSeek 官方 tests/test_fused_norm_rope_attn_rope_cast.py 的性能用例和计时方法,对比 main(ba89a34)与本分支(2e388e7)。配置为 H=64、D=512、TopK=640,QNorm 关闭。

阶段 query 数量 KV/历史长度 优化前 → 后(μs) 加速幅度
Prefill 4096 8192 336.52–337.26 → 320.08–320.66 5.00–5.11%
Prefill 4096 32768 339.30–339.36 → 323.48–323.71 4.80–4.86%
Decode 140 × 4 8192 91.39–91.60 → 85.00–85.03 7.46–7.56%
Decode 140 × 4 32768 91.82–91.95 → 85.02–85.26 7.83–8.09%
Decode 320 × 3 8192 152.69–152.74 → 139.28–139.40 9.53–9.67%
Decode 320 × 3 32768 153.45–153.51 → 139.49–139.61 9.91–10.03%

Prefill 的 query 数为总 token 数;Decode 为请求数 × 每请求 token 数。区间来自两组随机输入,每组各做 5 轮交错测量。加速幅度按每轮配对耗时计算:(优化前耗时 ÷ 优化后耗时 − 1)× 100%。全部输出与 main 逐字节一致,并通过官方参考检查。

(4)六个优化点分为三类:隐藏延迟、减少重复工作、减轻调度竞争。

类别 优化点 适用阶段
隐藏延迟 warp 11 预取下一批 KV,与当前批 FP8→BF16 转换重叠 Decode
隐藏延迟 提前读出 TMEM 最后两段,让下一条 query 的 PV 与当前输出收尾重叠 Prefill、Decode
隐藏延迟 提前读取 mask,与 QK 计算重叠 Prefill、Decode
减少重复工作 mask 全有效时,跳过逐元素判断 Prefill、Decode
减少重复工作 共用 PV 完成通知,省掉一次重复通知 Prefill
减轻调度竞争 MMA 发令从 warp 8 移到 warp 11,旨在减轻与 Softmax warp 12 的发令竞争 Prefill

最后一项的目的是分散 MMA 和 Softmax 的调度压力,仅用于实测有收益的配置:TopK=640,query 数/上下文为 1024/32768、4096/8192、4096/32768。

Add an opt-in H64 pipeline specialization selected for B200 with 148 SMs and QNorm disabled. Keep the original specialization for other devices, H128, V4 Decode and mixed FP4 caches.

The 32-element validity mask bypasses redundant predicates only when every bit is set; partial and empty masks retain the existing handling.
Load the 32-bit validity word after index production and before the QK completion wait. Preserve the original index-buffer lifetime and barrier ordering in fallback specializations.
Reuse the immediately preceding PV KV-empty full-completion signal before overwriting the single S buffer or rescaling O. Track the slot and phase across CLC jobs, starting at slot 2 / phase 1.

This is restricted to the one-CTA, three-KV-slot H64 prefill pipeline; Decode and other specializations retain the separate S/O signal.
Read the last two x32 output tiles into registers before processing either tile. Wait for all TMEM reads and preserve the 128-reader handoff before allowing the next query to overwrite O.

Keep per-tile RoPE, reductions, quantization and final-job behavior unchanged. The fallback path still releases O after its last tile load.
Use idle warp 11 for MMA issuance in the measured large-query prefill cases. Preserve warp 8 for smaller or unmeasured shapes, and keep the warpgroup, math, barriers and participant counts unchanged.

The original pipeline suggested contention between warp 8 and Softmax warp 12. Hardware timing supports a small gain; it does not establish exclusive scheduler causality.
Use one elected warp-11 thread to fetch the next raw FP8 block while WG1 converts and stores the current block. Replace three BF16 slots with two slots plus an independent 36,864-byte raw buffer.

All 128 consumers finish raw loads before releasing the raw slot. Account for the additional issuer in index-buffer and CLC barriers. Preserve the measured Decode assignment of MMA/CLC to warps 9/8. Mixed FP4, H128 and QNorm paths keep the original in-place pipeline.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants