Appearance
MLA, dim by dim
Opening
This note uses one lens throughout: write attention as tensor contractions, while keeping every dimension explicit. From this view, several seemingly separate MLA phenomena can be understood more deeply and more uniformly. Specifically, we get a few interesting conclusions:
- In prefill, MLA uses the KV latent bottleneck to reduce projection FLOPs, but it cannot absorb too early: the quadratic attention term should still live in
, not . - In decode, the best order changes because the cache already exists. After absorbing key/value projections, the length-
cache computation can go through the latent state instead of reconstructed per-head . - RoPE gets in the way exactly because it makes the latent-to-key map position-dependent, which breaks full key absorption.
- Q compression delays head expansion on the query side, reducing activation scale and usually projection FLOPs as well.
- MTP-style speculative decoding behaves differently on MLA because MLA decode no longer repeatedly reads the full KV cache; it reads a smaller latent cache and does extra computation on top.

Tensor View
Before the derivations, we first fix the tensor view used throughout the note. Every (h, T, D) order whenever it has a head axis: head first, sequence second, feature last. Per-head weights start with h; shared activations such as h axis. When a shared activation multiplies a per-head weight, the missing head axis is broadcast.
Einsum is all you need. In practice every one of the multiplications below can be written as a single einsum expression, and we find it much easier to keep track of what is being contracted vs. broadcast that way. For example:
The two indices that appear on both sides (like the d in this example) are contracted; the ones that appear only on one side (like h on T on h axis multiplies against a weight with an h axis, and the result inherits h.
Broadcast is legal only when dims line up. Broadcasting between batch dims follows the standard PyTorch rule: when we align the batch dimensions of 1 on one side — the side with the 1 is the one being replicated. A leading axis missing entirely (as when 1. If a pair is neither equal nor has a 1, the multiplication is undefined and the framework raises an error. Every matmul in this note satisfies the rule; whenever an activation without an h axis meets a weight with h, it is the activation's implicit 1 on the head axis being expanded to h.
Transpose convention. Throughout,
denotes swapping only the last two axes — the math-style matrix transpose that leaves batch dims untouched. In PyTorch this is A.transpose(-1, -2)(or the equivalentA.mT), notA.T, which reverses all axes. Sowith shape has of shape , and contracts on the last two axes cleanly.
Output projection is an einsum contraction, not a batched matmul. The final output projection h axis is a contracted index because it appears on both operands but not on the output, so the sum over heads is implicit. Equivalently,
Tensor FLOPs formula. For a 3D batched matmul the FLOPs count is simple. If we write the two operands as
The mental shortcut: multiply every output dimension by the contracted dimension
Two examples for concreteness:
where and produces . The contracted dim is ; the output dims are ; so . where and produces . The contracted dim is ; the output dims are ; so .
With this tensor view in hand, the rest of the note is mostly shape accounting: write the candidate contraction order, identify the output dimensions and contracted dimension, and read off the FLOPs.
Takeaway. Once every operation is written as a tensor contraction, FLOPs become explicit shape accounting.
Notation and Preliminaries
For broader background on the cache tradeoffs from MHA/MQA/GQA to MLA, see Su Jianlin's note. Here I do not try to survey those variants. I use MHA as the clean algebraic reference, and MLA as the object of the derivation.
For the derivations below, I count one multiply-add as
The main dimensions are:
| 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 |
The MHA reference form is:
The shapes are:
so
Then attention is:
Here
The MLA KV path replaces per-head
The shapes are:
Without query compression, the query path is still
with
These are the only structural definitions needed later. The remaining sections ask what FLOPs we get when the same objects are multiplied in different orders.
Baseline FLOPs
I start from baseline multi-head attention (MHA), derive each matrix multiplication and its FLOPs, and then compare it with MLA to see where the optimization actually comes from.
Prefill Comparison
Prefill FLOPs consist of three parts: the input projections, the quadratic attention computation, and the final output projection. The table below gives the result before the detailed derivation.
| Method | Input-projection FLOPs | Attention FLOPs | Output-projection FLOPs |
|---|---|---|---|
| MHA | |||
| MLA without query compression | |||
| MLA with query compression |
Plugging in the DeepSeek-V2 attention configuration
| Method | Input-projection FLOPs | Relative to MHA |
|---|---|---|
| MHA | ||
| MLA without query compression | ||
| MLA with query compression |
MLA prefill does not mainly reduce the quadratic attention FLOPs. It significantly reduces the linear projection FLOPs, and this reduction is especially clear with query compression.
MHA Prefill
For MHA prefill, the complete score expression is
From a FLOPs perspective, the important point is to minimize the coefficient of the dominating
The complete value and output chain is
Value/output order: use
The standard order is
Computing
Combining these terms, the dense MHA prefill FLOPs are
For causal prefill or training, the number of visible query-key pairs is
. An implementation that exploits the lower triangle replaces with .
MLA Prefill
For MLA prefill, first compute the shared KV latent
With query compression, it becomes
Key-projection order: use
The relevant key-projection chain is:
Computing the shared latent first gives
If we first form the effective per-head key projection
not counting the additional one-time weight-precomposition cost
With
Prefill score order: use
Because
The preferred order is therefore
The shared latent projection
Once
This computation order is implementable at the kernel level. The most direct implementation materializes
Without query compression, the dense MLA prefill FLOPs are
With query compression, they are
For causal prefill or training, replace the common attention term
Decode Comparison
Cached decode FLOPs consist of the current-token projections, the attention computation that scales with cache length
| Method | Current-token projection | Length- | Remaining local terms |
|---|---|---|---|
| MHA | |||
| MLA without query compression | |||
| MLA with query compression |
If we only look at FLOPs, MLA decode is not necessarily smaller than MHA. The MHA decode-length term is
However, decode is usually memory-bound. MHA stores and reads the full per-head cache
while MLA stores the shared latent
plus a smaller decoupled RoPE cache.
With
Therefore, MLA decode acceleration mainly comes from the cache change: it replaces the full per-head KV reads and writes in long-context decode with shared-latent cache reads and writes. The additional FLOPs are moved into smaller computations that can be executed inside fused kernels.
MHA Decode
With KV cache, historical
The computation order is determined by the cache: first generate
The score and value contractions are
The common output projection costs
MLA Decode
For MLA decode, the cache stores the shared latent instead of full per-head
The new
The complete score expressions are
Decode score order: use
Both variants first absorb
This applies
Decode value order: use
First form the latent weighted sum, then apply
This costs
The common output projection costs
For the whole MLA decode layer,
Here
This order is implementable at the kernel level. A fused MLA decode kernel can compute
Why RoPE Gets in the Way
MLA's absorption trick has an implicit prerequisite: the map from latent cache to key is fixed across positions. Without RoPE, for cached token
Then we can first compute a current-token vector
The problem is that RoPE inserts a position-dependent rotation into the key path. Let
Now the matrix
The practical solution is to decouple the content channel and the positional channel:
The content channel still uses the MLA latent-cache path; the RoPE channel is stored separately and kept small. Therefore RoPE does not make MLA impossible. It only blocks the cleanest version where the whole key is hidden behind one shared latent cache.
Why Compress Q Too
KV compression and Q compression solve different system problems. KV compression changes what we store and read during decode; Q compression factorizes query projection:
Activation memory. Without query compression, the query activation scale is
; with query compression, the compressed query state scale is . The key condition is simple: For DeepSeek-V2,
, , and , so . This significantly reduces the query activations that need to be saved, which is especially important in long-context prefill/training. Prefill/training FLOPs. Q compression can also bring a compute win. The query projection changes from:
When
, Q compression reduces query-projection FLOPs. Under DeepSeek-V2 dimensions, , while . This also explains why the previous prefill comparison shows a clear gap between MLA no-Q and MLA Q-compression. Decode. Q compression only affects the current-token query path; it does not reduce the latent cache-length term by itself.
Query-side order: use
, not . The score path is:
Implementations usually keep the smaller per-head bottleneck
. Excluding the shared step, the all-head score cost is: Since
is much smaller than and , the two-step path is usually cheaper.
Q compression saves activation memory and may also save prefill projection FLOPs, but it changes decode compute only modestly; KV compression is still the main decode-cache bandwidth win.
Why MLA Weakens MTP's Speculative-Decoding Gain: A Roofline View

The DeepSeek-V3 Technical Report uses MTP as an additional training objective. For prediction depth
Then the shared output head predicts the
During normal inference, DeepSeek-V3 can directly discard these MTP modules, so the MLA decode path is not affected by MTP. The interesting question appears when we repurpose MTP for speculative decoding: MTP drafts
Why Speculative Decoding Works on MHA
MHA decode is deeply memory-bound. Per generated token, the model reads a full
Speculative decoding exploits this imbalance. When you verify
Why This No Longer Works on MLA
MLA compresses the KV cache per token from
The consequence is sharp: even a modest number of speculation tokens (e.g.,
Concretely, the average per-accepted-token cost of MTP on top of MLA is:
For MTP to further accelerate decode on top of MLA, we would need:
MLA has shrunk the right-hand side (main decode is cheap now), but it has NOT shrunk the left. The MTP draft path costs the same:
Here
There is a second, subtler cost.
Summary
Speculative decoding accelerates MHA decode because MHA decode is memory-bound: verification cycles that would otherwise sit idle can be filled by drafted tokens at almost no additional cost. MLA changes the operating point. By compressing the KV cache by
This is not an argument against combining them; DeepSeek-V3 clearly still benefits from MTP as a training objective. It is an argument for calibrating expectations: on top of MLA, MTP-based speculative decoding delivers a much smaller speedup than it does on top of MHA.