MLA FLOPs、RoPE、Query Compression 与 MTP 笔记
本文关心的是 DeepSeek-V2 MLA 里一个很关键的问题:MLA 到底省了什么,FLOPs 又被转移到了哪里,RoPE等其他部分在MLA中的计算方式会有什么变化?本文会先比较 MHA 和 MLA 在 prefill/training/decode 中的 FLOPs 差异,代入论文中真实参数分析FLOPs计算的具体细节,再顺着同一套记号讨论 RoPE、query compression 和 MTP。这样做可以让很多看起来像“架构设计”的问题,其实可以直接从矩阵乘顺序和 cache format 中看出来。
基本上,全文可以分成四部分:
- MHA 和 MLA 在 prefill/training/decode 中的 FLOPs 对比。
- 为什么 RoPE 会阻碍 MLA absorption。
- 为什么 MLA 里还要压缩 。
- 为什么 MTP 会削弱 MLA 的加速收益。

Notation
为方便推导,本文把一次 multiply-add 记为 FLOPs, 并且只统计矩阵乘的 FLOPs,暂时忽略 softmax、mask、normalization、RoPE elementwise rotation 和 bias term。MLA 推导里用到的矩阵的主要维度如下:
| Symbol | Meaning | Shape / role |
|---|
| prefill sequence length | number of tokens in prefill or training |
| decode KV-cache length | number of cached historical tokens |
| hidden size | |
| number of attention heads | head index |
| per-head query/key dimension | |
| per-head value dimension | |
| MLA KV latent dimension | |
| MLA query latent dimension | |
为了避免 head index 掩盖 shared latent,下面更有用的 tensor 视角是:
因此 和 是所有 heads 共享的 latent,而 位于 head space,例如 ,。下面有些公式仍然保留单 head slice 来方便阅读,但真正的 storage/computation convention 应该按这里的 tensor shape 理解。
Baseline FLOPs
先从 baseline multi-head attention (MHA) 开始, 推导每一步的矩阵乘和 FLOPs。便于与MLA对比,理解MLA的优化到底在哪里。
MHA Prefill
首先从输入 计算 projection:
对于 MHA prefill,完整 score 表达式 ,attention output ,output projection 计算公式如下,其中 是 softmax 之后的 attention matrix:
对于attention score 的计算,从 FLOPs 角度看,关键是让 dominating 的 项系数尽可能小。由于 ,合理的计算顺序是:
然后计算 , 的 FLOPs,这里可以把 value/output path 合在一起看。按 tensor shape 写,可以理解为 head-wise 的四矩阵乘法:
最优顺序是先把 投影到 ,再乘 。对所有 heads 来说,标准顺序是:
如果先算 ,那么 cost 变成 。如果先合并 ,后面的 attention 乘法二次项系数也会变成 而不是 。由于通常 ,标准顺序更优,因为 quadratic term 是 ,而不是 。
但对于 causal prefill/training,实现上只需要计算 lower triangle。可见的 query-key pair 数量是: 所以 causal-aware attention FLOPs 是: 因此, causal-aware prefill FLOPs 是:
如果实现中显式考虑 causal mask,把中间项替换为 。
MLA Prefill
对于 MLA prefill,首先计算共享的 KV latent:。这里的 是所有 heads 共用的 latent 表示。
Without Query Compression.
对于不使用 query compression 的 MLA,完整 score 表达式 和 attention output 如下:
从 FLOPs 角度看,仍然要盯住 项的系数。通常 ,所以 prefill 里不应该先算 再乘 ;这样会把 dominating 的 项变成 。
key side 还有另一个计算顺序问题。按 tensor shape 看,score 里的 key side 是:
shared-latent 顺序是:
如果先形成 effective per-head key projection ,再和 相乘,那么 activation-side cost 是:
这里还没有算额外的一次性 weight precomposition cost 。代入 DeepSeek-V2 参数 ,shared-latent 顺序约为 FLOPs,而 effective- 顺序是 FLOPs。也就是说,便宜的路径是先算 ,先利用共享的 bottleneck,再扩展到各个 heads。
因此计算顺序是:
然后计算 的 FLOPs,其中 ,:
这个顺序在 kernel 层面是可实现的。最直接的做法是先 materialize ,然后调用标准 FlashAttention kernel;更节省内存的做法则是在每个 attention tile 内从 计算 。
不使用 query compression 时,causal-aware MLA prefill FLOPs 是:
如果实现中显式考虑 causal mask,把中间的 attention 项替换为 。
With Query Compression.
使用 query compression 后,完整 score 表达式变成:
attention output 保持不变:
最优计算顺序是:
路径和 no-Q-compression 情况相同。使用 query compression 时,non-causal-aware MLA prefill 总 FLOPs 是:
如果实现中显式考虑 causal mask,把中间的 attention 项替换为 。
Prefill Comparison
从上面的公式可以看到,如果二者都显式计算同样形状的 和 ,那么 MHA 和 MLA 的 quadratic attention FLOPs 是相同的;prefill 阶段真正的差异主要来自 linear projection part。
MHA 的 linear projection FLOPs 是 。MLA without query compression 是 。MLA with query compression 是 。
代入 DeepSeek-V2 attention 配置 ,MHA linear projection cost 是 ,MLA without query compression 是 ,MLA with query compression 是 。也就是说,MLA no-Q 约为 MHA 的 ,MLA Q-compression 约为 MHA 的 。
MLA prefill 不会降低 quadratic attention FLOPs,但会显著降低 linear projection FLOPs;with query compression 时这一点尤其明显。在 DeepSeek-V2 配置下,MLA with query compression 的 projection FLOPs 只有 MHA 的约 。
MHA Decode
有 KV cache 时,历史的 已经在之前的 decode steps 中算好。当前 step 只需要计算并 append 新的 :
对所有 heads 来说,当前 token projection cost 是:
这里的计算顺序实际上由 KV cache 决定:先从当前 token 的 生成 ,其中 被 append 到 cache;然后只用当前 去读历史 计算 scores。这样 decode-length 相关的乘法发生在 维度上,而不是重新从 hidden dimension 处理整段 。然后 attention 读取 cached :
所以 cached decode FLOPs 是:
MLA Decode
对于 MLA decode,cache 中存的不是 full per-head ,而是共享的 latent cache。在当前 step,我们 append 一个新的 latent vector:
因此当前 token 的 linear projection cost 是明确包含在 decode 里的。没有 query compression 时:
有 query compression 时:
MLA decode 不需要额外 append full ;真正写入 cache 的是 shared latent, 只在 attention 需要时 reconstruct 或 absorb。
完整的 score 和 value 表达式如下。这里保留两个变体:MLA without query compression 和 MLA with query compression。它们的区别主要在 current-token query path,而 latent-cache attention path 是共享的。
对于输入 query ,cached execution 首先构造当前 query。没有 query compression 时:
有 query compression 时, 的计算被 factorize 为:
之后两个变体使用相同的 cached attention path。score side 的顺序是先把 吸收到 current query 上,得到 ,再和 latent cache 相乘;这样 只作用在长度为 的 current token 上,而不是作用在长度为 的整个 cache 上。value side 也是同样的原则:先算 得到 latent weighted sum,再乘 ,而不是先为所有 cached tokens reconstruct full 。
output projection 与 MHA 相同:
所以 cached all-head decode attention FLOPs 是:
对于整个 MLA decode layer:
其中 是 decode-length part:一个 用来读取 latent cache 计算 scores,另一个 用来再次读取 latent cache 计算 weighted latent value。这个顺序在 kernel 层面可实现:一个 fused MLA decode kernel 可以计算 ,stream 的 cache blocks,做 online softmax,累积 ,最后再乘 。
Decode Comparison
把 MHA 和 MLA 的 decode 公式放在一起看,MHA cached decode 是:
MLA without query compression 是:
如果只看 FLOPs,MLA decode 不一定比 MHA 更小。比如 decode-length 相关项里,MHA 是 ,而 MLA latent path 里是 。在 DeepSeek-V2 的维度 下,后者的算术量并不小。但 decode 阶段通常是一个 memory-bound 的问题,MHA 每个 cached token 要存和读 full per-head cache: 所以每个 token 的 cache 规模大约是 。MLA 存的是 shared latent: 再加上较小的 decoupled RoPE cache。代入 ,MHA 的 full KV cache 每个 token 是 维,而 MLA 的 latent cache 是 维。即使 MLA 在 kernel 内多做一些 、latent score 和 latent value 的计算,它避免了反复从显存读取 full per-head 。
所以 MLA decode 的加速主要来自 cache 的变化:把长上下文 decode 中最重的 full KV 读写,换成了 shared latent cache 的读写。FLOPs 被转移到一些更小、更适合 fused kernel 的计算上;MLA 在这部分的本质还是用计算来换显存。
Why RoPE Gets in the Way
MLA 的 absorption trick 成立,有一个隐含前提:从 latent cache 到 key 的映射在不同 position 上是固定的。不考虑 RoPE 时,对于 cached token :
于是可以先计算一个 current-token vector ,再对所有 cached positions 复用它。这就是 latent-cache decode 背后最干净的代数结构。
问题在于,RoPE 会在 key path 中插入 position-dependent rotation。令 表示当前 position 的 query rotation, 表示 cached position 的 key rotation:
现在矩阵 依赖于 。这意味着我们不能只形成一个 ,然后对所有 cached tokens 复用它。重构并缓存所有 rotated per-head keys 当然可行,但这样基本又回到了普通 KV cache 的方向。
实际做法是 decouple content channel 和 positional channel:
content channel 仍然走 MLA latent-cache path;RoPE channel 则单独存,并且维度保持较小。所以 RoPE 并不会让 MLA 不可能实现,它只是阻止了“整个 key 都隐藏在一个 shared latent cache 后面”的最干净版本。
Why Compress Q Too
KV compression 和 Q compression 解决的是系统里的不同问题。KV compression 改变 decode 时存什么、读什么;Q compression 则 factorize query projection:
Activation memory. 没有 query compression 时,query activation 规模是 ;有 query compression 时,compressed query state 规模是 。关键条件很简单:
对于 DeepSeek-V2,,,,所以 。这会显著减少需要保存的 query activations,在 long-context prefill/training 中尤其重要。
Prefill/training FLOPs. Q compression 也可能带来 compute win。query projection 从:
当 时,Q compression 会降低 query-projection FLOPs。在 DeepSeek-V2 维度下,,而 。这也解释了为什么前面的 prefill comparison 中,MLA no-Q 和 MLA Q-compression 的差距很明显。
Decode. Q compression 只影响 current-token query path;它本身不会降低 latent cache-length term。score path 是:
实际实现中通常仍然经过较小的 per-head bottleneck 。score-side cost 是:
因为 远小于 和 ,所以 two-step path 通常更便宜。
Q compression 会节省 activation memory,也可能节省 prefill projection FLOPs,但对 decode compute 的改变相对有限;KV compression 仍然是主要的 decode-cache bandwidth win。
Why MTP Weakens MLA's Decode Acceleration Gain

DeepSeek-V3 Technical Report 中的 MTP 是额外 training objective。对于 prediction depth ,MTP module 会把上一 depth 的 representation 和 shifted token embedding 拼接起来:
然后 shared output head 预测第 个额外 future token:
正常 inference 时,DeepSeek-V3 可以直接丢弃这些 MTP modules,此时 MLA 的 decode path 不受 MTP 影响。真正需要讨论的是把 MTP modules 拿来做 speculative decoding:MTP path 先 draft 多个 future tokens,再由 main model verify。
先看 plain MLA 的收益。前面推导的 MLA decode 加速,主要来自 main decode path 的 cache format 改变:普通 MHA 每个 token 大约读取 维 cached ,而 MLA 主要读取 维 latent cache,再加上较小的 decoupled RoPE 部分。以 DeepSeek-V2/V3 风格的参数为例,,而 。也就是说,MLA 的优势可以粗略理解为:
引入 MTP 做 speculative decoding 后,实际每生成一组 accepted tokens 的成本不再只是 ,而是 draft 加 verify 的成本。假设一次 draft 个 tokens,最终接受 个 tokens,那么平均到每个 accepted token 上,大致是:
如果要让 MTP 不吃掉 MLA 原本的加速收益,就需要这个平均成本不要比 plain MLA decode 高太多;更强一点,如果希望 MTP 在 MLA 上继续带来 decode 加速,则需要:
问题在于,MTP draft path 并不是免费的。第 个 draft depth 至少包含 fusion projection、一个额外的 block,以及 output head:
其中 来自把 从 投影回 , 是 MTP Transformer block 的额外 decode cost, 是计算完整 logits 时 shared output head 的 cost。
关键点很简单: 跑在 shifted hidden stream 上,和 main model 的 hidden states、token alignment 都不同,所以不能直接复用 main cache。如果 MTP block 内部也使用 MLA,就要为这条 stream 维护一份自己的 latent cache。
因此,MTP 会削弱 MLA 在 decode 阶段的加速收益。MLA 把 main decode path 的 cache 读写压低了,但 MTP 又多出一条 draft stream、verify cost,以及一份不能直接复用 main cache 的 shifted latent states。从 FLOPs/cache 角度看,这些额外成本会吃掉一部分 MLA 的收益。