1. 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 reduces the FLOPs of the linear projections, but it cannot absorb too early: the quadratic core-attention term should still be computed in dk,dvd_k,d_v, not dcd_c.
  • In decode, the best order changes because the cache already exists. After absorbing key/value projections, the length-tt cache computation can go through the latent state instead of reconstructed per-head K,VK,V.
  • RoPE gets in the way exactly because it makes the latent-to-key map position-dependent, which breaks full key absorption.
  • Q compression mainly reduces training activation memory and prefill/training projection FLOPs, while contributing little to decode.
  • MTP-style speculative decoding behaves differently on MLA because MLA decode no longer reads the full per-head KV cache; it reads the compressed latent and RoPE caches and performs additional contractions instead.

2. Tensor View

Takeaway. Unlike a flattened matrix view, the tensor view keeps every meaningful axis explicit. FLOPs can be read directly from the output and contracted axes, while different computation orders can be compared in the same notation.

The tensor view is just this: every tensor contraction is determined by three kinds of axes — output axes, contracted axes, and broadcast axes. Once these axes are explicit, FLOPs become shape accounting.

  • Q(h,T,dk)=X(T,d)WQ(h,d,dk)    einsum("td,sdk->stk",X,WQ).\underset{(h,T,d_k)}{Q}=\underset{(T,d)}{X}\underset{(h,d,d_k)}{W_Q}\;\Longleftrightarrow\;\mathrm{einsum}(\texttt{"td,sdk->stk"},X,W_Q).

  • S(h,T,T)=Q(h,T,dk)K(h,dk,T)    einsum("stk,suk->stu",Q,K).\underset{(h,T,T)}{S}=\underset{(h,T,d_k)}{Q}\underset{(h,d_k,T)}{K^\top}\;\Longleftrightarrow\;\mathrm{einsum}(\texttt{"stk,suk->stu"},Q,K).

General Form. For a batched contraction whose output axes are B1,,Br,m,nB_1,\ldots,B_r,m,n and whose contracted axis is kk, the FLOPs count is:

FLOPs  =  2B1Brmnk.\mathrm{FLOPs} \;=\; 2 \cdot B_1 \cdots B_r \cdot m \cdot n \cdot k .

In words: multiply every output dimension by the contracted dimension, then multiply by 22 for multiply-adds (the standard GEMM convention counts one fused multiply-add as one multiplication plus one addition). Thus Q=XWQQ=XW_Q costs 2hTddk2hTdd_k, while S=QKS=QK^\top costs 2hT2dk2hT^2d_k.

3. Notation and Preliminaries

Takeaway. This section fixes notation by annotating shapes directly on the equations, then gives a basic introduction to MHA, MLA, and the RoPE rotation used later.

For broader background from MHA/MQA/GQA to MLA, see Jianlin Su's note. Here we do not try to survey those variants. We use MHA as the clean algebraic reference, MLA as the object of the derivation, and introduce only the RoPE identities needed for the later absorption discussion.

To keep the derivations simple, we use the following conventions:

  • Elementwise attention scores are always written as Ss,t,uS_{s,t,u}: hh is the number of heads, s=1,,hs=1,\ldots,h selects one head, tt is the query position, and uu is the key position. Thus rows of the score matrix correspond to tt, and columns correspond to uu.
  • RoPE is omitted from the MLA equations until Section 5.
  • We ignore the causal mask; readers can replace the full attention matrix with its causal form directly.
  • Softmax is omitted from the FLOPs comparison. MHA and MLA apply the same softmax to the same-shaped attention scores, while model FLOPs use the dominant GEMMs and tensor contractions as the comparable measure. Optimized implementations such as PyTorch SDPA also fuse softmax into scaled-dot-product attention. Section 5 still counts the explicit addition of the content and RoPE scores, because it is an extra operation introduced by the decoupled branch and scales with the full attention matrix.
  • AA^\top means transposing only the last two axes, leaving batch axes untouched.

MHA. The reference form is:

Q(h,T,dk)=X(T,d)WQ(h,d,dk),K(h,T,dk)=X(T,d)WK(h,d,dk),V(h,T,dv)=X(T,d)WV(h,d,dv).\underset{(h,T,d_k)}{Q} = \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q}, \qquad \underset{(h,T,d_k)}{K} = \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_K}, \qquad \underset{(h,T,d_v)}{V} = \underset{(T,d)}{X} \underset{(h,d,d_v)}{W_V}.

Then attention is:

S(h,T,T)=Q(h,T,dk)K(h,dk,T),P(h,T,T)=softmax ⁣(Sdk),\underset{(h,T,T)}{S} = \underset{(h,T,d_k)}{Q} \underset{(h,d_k,T)}{K^\top}, \qquad \underset{(h,T,T)}{P} = \operatorname{softmax}\!\left(\frac{S}{\sqrt{d_k}}\right), O(h,T,dv)=P(h,T,T)V(h,T,dv),Y(T,d)=O(h,T,dv)Wo(h,dv,d).\underset{(h,T,d_v)}{O} = \underset{(h,T,T)}{P} \underset{(h,T,d_v)}{V}, \qquad \underset{(T,d)}{Y} = \underset{(h,T,d_v)}{O} \underset{(h,d_v,d)}{W_o}.

The last line contracts both the head axis and the dvd_v axis. This is equivalent to the usual implementation that concatenates all heads and then applies one output projection: flattening (h,dv)(h,d_v) into a single hdvhd_v axis does not change the sum,

Yt,d=s=1hv=1dvOs,t,v(Wo)s,v,d=j=1hdv(Oconcat)t,j(Wo,concat)j,d.Y_{t,d}=\sum_{s=1}^{h}\sum_{v=1}^{d_v}O_{s,t,v}(W_o)_{s,v,d} =\sum_{j=1}^{hd_v}(O_{\mathrm{concat}})_{t,j}(W_{o,\mathrm{concat}})_{j,d}.

The FLOPs are also identical. In the tensor-contraction form, the output axes are TT and dd, and the contracted axes are hh and dvd_v, giving 2Tdhdv2Tdh d_v. After concatenation, the multiplication has shapes (T,hdv)(hdv,d)(T,hd_v)(hd_v,d), giving 2T(hdv)d=2Thdvd2T(hd_v)d=2Thd_vd.


MLA. The KV path replaces per-head K,VK,V projections with a shared latent. To keep the derivation simple, we temporarily omit RoPE from the MLA form until Section 5:

CKV(T,dc)=X(T,d)WDKV(d,dc),K(h,T,dk)=CKV(T,dc)WUK(h,dc,dk),\underset{(T,d_c)}{C^{KV}} = \underset{(T,d)}{X} \underset{(d,d_c)}{W_{DKV}}, \qquad \underset{(h,T,d_k)}{K} = \underset{(T,d_c)}{C^{KV}} \underset{(h,d_c,d_k)}{W_{UK}}, V(h,T,dv)=CKV(T,dc)WUV(h,dc,dv).\underset{(h,T,d_v)}{V} = \underset{(T,d_c)}{C^{KV}} \underset{(h,d_c,d_v)}{W_{UV}}.

The DeepSeek-V2 paper claims that query compression reduces activation memory during training; DeepSeek-V2-Lite, however, does not compress queries. This gives us a natural comparison: first derive MLA without query compression, then add it back and isolate what changes.

No query compression.

Q(h,T,dk)=X(T,d)WQ(h,d,dk).\underset{(h,T,d_k)}{Q} = \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q}.

Query compression.

CQ(T,dqc)=X(T,d)WDQ(d,dqc),Q(h,T,dk)=CQ(T,dqc)WUQ(h,dqc,dk).\underset{(T,d_{qc})}{C^Q} = \underset{(T,d)}{X} \underset{(d,d_{qc})}{W_{DQ}}, \qquad \underset{(h,T,d_k)}{Q} = \underset{(T,d_{qc})}{C^Q} \underset{(h,d_{qc},d_k)}{W_{UQ}}.

RoPE.

In the tensor view, RoPE inserts a position-dependent linear map on the last tensor dimension without changing the tensor shape. Because RoPE acts on QQ and KK, we use their tensor dimension dkd_k as the rotation dimension. For even dkd_k, the standard rotation matrix is

Rt=diag ⁣([cos(tθ1)sin(tθ1)sin(tθ1)cos(tθ1)],,[cos(tθdk/2)sin(tθdk/2)sin(tθdk/2)cos(tθdk/2)])Rdk×dk.R_t= \operatorname{diag}\!\left( \begin{bmatrix} \cos(t\theta_1) & -\sin(t\theta_1)\\ \sin(t\theta_1) & \cos(t\theta_1) \end{bmatrix}, \ldots, \begin{bmatrix} \cos(t\theta_{d_k/2}) & -\sin(t\theta_{d_k/2})\\ \sin(t\theta_{d_k/2}) & \cos(t\theta_{d_k/2}) \end{bmatrix} \right) \in\mathbb{R}^{d_k\times d_k}.

Writing the RoPE base as bb, we have θj=b2(j1)/dk\theta_j=b^{-2(j-1)/d_k}. RoFormer uses b=10000b=10000; modern language models may use a larger base for longer contexts. Since we use row vectors, stack the transposed position rotations into one tensor: R(T,dk,dk)\underset{(T,d_k,d_k)}{\mathcal R}, where Rt,:,:=Rt\mathcal R_{t,:,:}=R_t^\top.

RoPE and the subsequent score contraction are then

Q~(h,T,dk)=Q(h,T,dk)R(T,dk,dk),K~(h,T,dk)=K(h,T,dk)R(T,dk,dk),S(h,T,T)=Q~(h,T,dk)K~(h,dk,T).\underset{(h,T,d_k)}{\widetilde Q} = \underset{(h,T,d_k)}{Q} \underset{(T,d_k,d_k)}{\mathcal R}, \qquad \underset{(h,T,d_k)}{\widetilde K} = \underset{(h,T,d_k)}{K} \underset{(T,d_k,d_k)}{\mathcal R}, \qquad \underset{(h,T,T)}{S} = \underset{(h,T,d_k)}{\widetilde Q} \underset{(h,d_k,T)}{\widetilde K^\top}.

The two TT axes are identified as the same position axis, so only one TT remains in the output. The dkd_k axis of QQ or KK contracts with the first dkd_k axis of R\mathcal R, while the second dkd_k axis remains; R\mathcal R is broadcast across the head axis. Therefore the (h,T,dk)(h,T,d_k) shapes of Q,KQ,K are unchanged. Since RtRu=RutR_t^\top R_u=R_{u-t},

Ss,t,u=Qs,t,:RtRuKs,u,:=Qs,t,:RutKs,u,:,S_{s,t,u} = Q_{s,t,:}R_t^\top R_uK_{s,u,:}^\top = Q_{s,t,:}R_{u-t}K_{s,u,:}^\top,

so each rotation encodes absolute position, while the score depends only on relative position.

The actual elementwise computation. The rotation tensor above is an algebraic representation; an implementation need not construct a dense dk×dkd_k\times d_k matrix. Let m=dk/2m=d_k/2. For any qRdkq\in\mathbb R^{d_k}, define the adjacent-pair coordinates

qj,0:=q2j1,qj,1:=q2j,j=1,,m.q_{j,0}:=q_{2j-1}, \qquad q_{j,1}:=q_{2j}, \qquad j=1,\ldots,m.

Using the same θj\theta_j as above, the corresponding phase tensor is

Θ^t,2j1=Θ^t,2j=tθj,j=1,,m,Θ^(T,dk).\widehat\Theta_{t,2j-1} = \widehat\Theta_{t,2j} = t\theta_j, \qquad j=1,\ldots,m, \qquad \underset{(T,d_k)}{\widehat\Theta}.

The implementation then directly computes

Q~(h,T,dk)=Q(h,T,dk)cosΘ^(T,dk)+rotate_half ⁣(Q(h,T,dk))sinΘ^(T,dk),\underset{(h,T,d_k)}{\widetilde Q} = \underset{(h,T,d_k)}{Q} \odot \cos\underset{(T,d_k)}{\widehat\Theta} + \operatorname{rotate\_half}\!\left( \underset{(h,T,d_k)}{Q} \right) \odot \sin\underset{(T,d_k)}{\widehat\Theta}, K~(h,T,dk)=K(h,T,dk)cosΘ^(T,dk)+rotate_half ⁣(K(h,T,dk))sinΘ^(T,dk).\underset{(h,T,d_k)}{\widetilde K} = \underset{(h,T,d_k)}{K} \odot \cos\underset{(T,d_k)}{\widehat\Theta} + \operatorname{rotate\_half}\!\left( \underset{(h,T,d_k)}{K} \right) \odot \sin\underset{(T,d_k)}{\widehat\Theta}.

Here \odot is elementwise multiplication; the cosine and sine tensors broadcast across the head axis. In the pair notation above, rotate_half is defined by

rotate_half(q)j,0=qj,1,rotate_half(q)j,1=qj,0.\operatorname{rotate\_half}(q)_{j,0}=-q_{j,1}, \qquad \operatorname{rotate\_half}(q)_{j,1}=q_{j,0}.

It preserves the shape and removes no axis. This elementwise formulation is exactly equivalent to the rotation-tensor formulation above. We include it in the tensor view, but do not count it separately in the later FLOPs comparison of the main tensor contractions.

4. Computation Order and FLOPs in Prefill, Training, and Decode

We derive each tensor contraction and its FLOPs, comparing MHA with MLA.

4.1 Prefill and Training: The Standard Computation Order

Takeaway. MLA reduces the FLOPs of the linear projections. It does not reduce the T2T^2 core-attention FLOPs, because QKQK^\top and PVPV still contract over dkd_k and dvd_v, respectively.


MHA.

For MHA prefill, the complete score expression is

S=QK=X(T,d)WQ(h,d,dk)WK(h,dk,d)X(d,T).S = QK^\top = \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q} \underset{(h,d_k,d)}{W_K^\top} \underset{(d,T)}{X^\top}.

How to compute SS in a cost-effective way? From a FLOPs perspective, the important point is to minimize the coefficient of the dominating T2T^2 term. Since dk<dd_k<d, the computation order is to first construct Q=XWQQ=XW_Q and K=XWKK=XW_K to reduce the dd feature and keeping dkd_k, then we compute S=QKS=QK^\top:

X(T,d)WQ(h,d,dk)Q(h,T,dk),FLOPs=2Thddk,X(T,d)WK(h,d,dk)K(h,T,dk),FLOPs=2Thddk,Q(h,T,dk)K(h,dk,T)S(h,T,T),FLOPs=2hT2dk.\begin{aligned} \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q} &\rightarrow \underset{(h,T,d_k)}{Q}, &\qquad \mathrm{FLOPs}&=2Thdd_k, \\ \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_K} &\rightarrow \underset{(h,T,d_k)}{K}, &\qquad \mathrm{FLOPs}&=2Thdd_k, \\ \underset{(h,T,d_k)}{Q} \underset{(h,d_k,T)}{K^\top} &\rightarrow \underset{(h,T,T)}{S}, &\qquad \mathrm{FLOPs}&=2hT^2d_k. \end{aligned}

The complete value and output chain is:

P(h,T,T)X(T,d)WV(h,d,dv)Wo(h,dv,d)Y(T,d).\underset{(h,T,T)}{P} \underset{(T,d)}{X} \underset{(h,d,d_v)}{W_V} \underset{(h,d_v,d)}{W_o} \rightarrow \underset{(T,d)}{Y}.

We still focus on the T2T^2 term as our optimization goal. Here the ordering question is which tensor dimension accompanies the quadratic multiplication by PP. Constructing VV first ensures that attention operates on dvd_v, rather than carrying the wider model dimension dd through all token pairs.

use (P(XWV))Wo(P(XW_V))W_o, not ((PX)WV)Wo((PX)W_V)W_o or P(X(WVWo))P(X(W_VW_o)).

The standard order is XWVVXW_V\rightarrow V, PVOPV\rightarrow O, and OWoYOW_o\rightarrow Y:

F(P(XWV))Wo=2ThddvXWV+2hT2dvPV+2ThdvdOWo.F_{(P(XW_V))W_o} = \underbrace{2Thdd_v}_{XW_V} +\underbrace{2hT^2d_v}_{PV} +\underbrace{2Thd_vd}_{OW_o}.

Computing PXPX first gives

F((PX)WV)Wo=2hT2dPX+2Thddv(PX)WV+2ThdvdOWo.F_{((PX)W_V)W_o} = \underbrace{2hT^2d}_{PX} +\underbrace{2Thdd_v}_{(PX)W_V} +\underbrace{2Thd_vd}_{OW_o}.

Precomposing WVWoW_VW_o gives

FP(X(WVWo))=2hd2dvWVWo+2hTd2X(WVWo)+2hT2dP[X(WVWo)].F_{P(X(W_VW_o))} = \underbrace{2hd^2d_v}_{W_VW_o} +\underbrace{2hTd^2}_{X(W_VW_o)} +\underbrace{2hT^2d}_{P[X(W_VW_o)]}.

Both alternatives attach the quadratic contraction to dd rather than dvd_v. Since dvdd_v\ll d, the standard order is cheaper: 2hT2dv2hT^2d_v instead of 2hT2d2hT^2d.

Combining these terms, the dense MHA prefill FLOPs are

FMHA,prefill=2Tdh(2dk+dv)XWQ,XWK,XWV+2hT2(dk+dv)QK,PV+2ThdvdOWo.F_{\mathrm{MHA,prefill}} =\underbrace{2Tdh(2d_k+d_v)}_{XW_Q,\,XW_K,\,XW_V} +\underbrace{2hT^2(d_k+d_v)}_{QK^\top,\,PV} +\underbrace{2Thd_vd}_{OW_o}.

MLA.

For MLA prefill, first compute the shared KV latent CKV=XWDKVC^{KV}=XW_{DKV}. Without query compression, the complete score expression is

S=X(T,d)WQ(h,d,dk)WUK(h,dk,dc)WDKV(dc,d)X(d,T).S = \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q} \underset{(h,d_k,d_c)}{W_{UK}^\top} \underset{(d_c,d)}{W_{DKV}^\top} \underset{(d,T)}{X^\top}.

With query compression, it becomes

S=X(T,d)WDQ(d,dqc)WUQ(h,dqc,dk)WUK(h,dk,dc)WDKV(dc,d)X(d,T).S = \underset{(T,d)}{X} \underset{(d,d_{qc})}{W_{DQ}} \underset{(h,d_{qc},d_k)}{W_{UQ}} \underset{(h,d_k,d_c)}{W_{UK}^\top} \underset{(d_c,d)}{W_{DKV}^\top} \underset{(d,T)}{X^\top}.

Every way of forming the score has output shape (h,T,T)(h,T,T), so the factor hT2hT^2 is fixed; we only need to compare the contracted tensor dimension. We therefore want this dimension to be dkd_k (dk=128<dc=512<d=5120d_k=128<d_c=512<d=5120 in DeepSeek-V2). Thus, we can divide the formula into two parts: XWDQWUQXW_{DQ}W_{UQ} and XWDKVWUKXW_{DKV}W_{UK}.

Query-projection order: use (XWDQ)WUQ(XW_{DQ})W_{UQ}, not X(WDQWUQ)X(W_{DQ}W_{UQ}).

Without query compression, Q=XWQQ=XW_Q is the same as in MHA (FLOPs: 2Thddk2Thdd_k). It contains only one contraction, so there is no query-side computation order to choose. The comparison below appears only after WQW_Q is factorized into WDQWUQW_{DQ}W_{UQ}.

With query compression, the factorized order first maps each token to CQC^Q, then expands the head axis through WUQW_{UQ}:

X(T,d)WDQ(d,dqc)CQ(T,dqc),FLOPs=2Tddqc,CQ(T,dqc)WUQ(h,dqc,dk)Q(h,T,dk),FLOPs=2Thdqcdk.\begin{aligned} \underset{(T,d)}{X} \underset{(d,d_{qc})}{W_{DQ}} &\rightarrow \underset{(T,d_{qc})}{C^Q}, &\qquad \mathrm{FLOPs}&=2Tdd_{qc}, \\ \underset{(T,d_{qc})}{C^Q} \underset{(h,d_{qc},d_k)}{W_{UQ}} &\rightarrow \underset{(h,T,d_k)}{Q}, &\qquad \mathrm{FLOPs}&=2Thd_{qc}d_k. \end{aligned}

If we compose the weights first, the two contractions are

WDQ(d,dqc)WUQ(h,dqc,dk)WQeff(h,d,dk),FLOPs=2hddqcdk,X(T,d)WQeff(h,d,dk)Q(h,T,dk),FLOPs=2Thddk.\begin{aligned} \underset{(d,d_{qc})}{W_{DQ}} \underset{(h,d_{qc},d_k)}{W_{UQ}} &\rightarrow \underset{(h,d,d_k)}{W_Q^{\mathrm{eff}}}, &\qquad \mathrm{FLOPs}&=2hdd_{qc}d_k, \\ \underset{(T,d)}{X} \underset{(h,d,d_k)}{W_Q^{\mathrm{eff}}} &\rightarrow \underset{(h,T,d_k)}{Q}, &\qquad \mathrm{FLOPs}&=2Thdd_k. \end{aligned}

The activation-side costs of the two orders are therefore

F(XWDQ)WUQ=2Tddqc+2Thdqcdk=66.1MT,FX(WDQWUQ)=2Thddk=167.8MT(DeepSeek-V2).\begin{aligned} F_{(XW_{DQ})W_{UQ}} &=2Tdd_{qc}+2Thd_{qc}d_k =66.1\text{M}T, \\ F_{X(W_{DQ}W_{UQ})} &=2Thdd_k =167.8\text{M}T \qquad\text{(DeepSeek-V2)}. \end{aligned}

where the second expression excludes the additional weight-precomposition cost 2hddqcdk2hdd_{qc}d_k. The factorized order is cheaper when

dqc(d+hdk)<hddk.d_{qc}(d+hd_k)<hdd_k.

The saving comes from introducing CQ:(T,dqc)C^Q:(T,d_{qc}) between the two contractions, so the per-head contraction uses dqcd_{qc} rather than dd.

Similarly, for the key and score order, compute CKV=XWDKVC^{KV}=XW_{DKV}, reconstruct K=CKVWUKK=C^{KV}W_{UK}, then form S=QKS=QK^\top.

With the DeepSeek-V2 dimensions, the key-projection order (XWDKV)WUK(XW_{DKV})W_{UK} costs 22.0MT22.0\text{M}T, while X(WDKVWUK)X(W_{DKV}W_{UK}) costs 167.8MT167.8\text{M}T, excluding the one-time cost of composing the weights.

Value and output order: use (P(CKVWUV))Wo(P(C^{KV}W_{UV}))W_o, not ((PCKV)WUV)Wo((PC^{KV})W_{UV})W_o or P(CKV(WUVWo))P(C^{KV}(W_{UV}W_o)).

The standard order reconstructs VV before PVPV:

F(P(CKVWUV))Wo=2ThdcdvCKVWUV+2hT2dvPV+2ThdvdOWo.F_{(P(C^{KV}W_{UV}))W_o} =\underbrace{2Thd_cd_v}_{C^{KV}W_{UV}} +\underbrace{2hT^2d_v}_{PV} +\underbrace{2Thd_vd}_{OW_o}.

Computing PCKVPC^{KV} first changes the quadratic term to 2hT2dc2hT^2d_c. Precomposing WUVWoW_{UV}W_o instead changes it to 2hT2d2hT^2d. Since dv<dc<dd_v<d_c<d, reconstructing VV first keeps the quadratic contraction in the smallest tensor dimension, dvd_v.

Without query compression, the dense MLA prefill FLOPs are

FMLA,prefill,noQ=2ThddkQ=XWQ+2TddcCKV=XWDKV+2Thdc(dk+dv)K=CKVWUK,  V=CKVWUV+2hT2(dk+dv)S=QK,  O=PV+2ThdvdY=OWo.\begin{aligned} F_{\mathrm{MLA,prefill,noQ}} &=\underbrace{2Thdd_k}_{Q=XW_Q} \\ &\quad+\underbrace{2Tdd_c}_{C^{KV}=XW_{DKV}} +\underbrace{2Thd_c(d_k+d_v)}_{K=C^{KV}W_{UK},\;V=C^{KV}W_{UV}} \\ &\quad+\underbrace{2hT^2(d_k+d_v)}_{S=QK^\top,\;O=PV} +\underbrace{2Thd_vd}_{Y=OW_o}. \end{aligned}

With query compression, they are

FMLA,prefill,Qcomp=2TddqcCQ=XWDQ+2ThdqcdkQ=CQWUQ+2TddcCKV=XWDKV+2Thdc(dk+dv)K=CKVWUK,  V=CKVWUV+2hT2(dk+dv)S=QK,  O=PV+2ThdvdY=OWo.\begin{aligned} F_{\mathrm{MLA,prefill,Qcomp}} &=\underbrace{2Tdd_{qc}}_{C^Q=XW_{DQ}} +\underbrace{2Thd_{qc}d_k}_{Q=C^QW_{UQ}} \\ &\quad+\underbrace{2Tdd_c}_{C^{KV}=XW_{DKV}} +\underbrace{2Thd_c(d_k+d_v)}_{K=C^{KV}W_{UK},\;V=C^{KV}W_{UV}} \\ &\quad+\underbrace{2hT^2(d_k+d_v)}_{S=QK^\top,\;O=PV} +\underbrace{2Thd_vd}_{Y=OW_o}. \end{aligned}

Comparison.

Prefill FLOPs consist of three parts: the linear projections, the core-attention computation, and the final output projection. The table summarizes the derivation and substitutes the DeepSeek-V2 dimensions; the figure then plots the resulting FLOPs as functions of sequence length TT, except for the RoPE branch.

MethodLinear-projection FLOPsCore-attention FLOPs (QKQK^\top and PVPV)Output-projection FLOPsDeepSeek-V2 prefill FLOPs (except the RoPE branch)
MHA2Tdh(2dk+dv)2Tdh(2d_k+d_v)2hT2(dk+dv)2hT^2(d_k+d_v)2Thdvd2Thd_vd65,536T2+671,088,640T65{,}536T^2+671{,}088{,}640T
MLA without query compression2Tddc+2Thddk+2Thdc(dk+dv)2Tdd_c+2Thdd_k+2Thd_c(d_k+d_v)2hT2(dk+dv)2hT^2(d_k+d_v)2Thdvd2Thd_vd65,536T2+374,341,632T65{,}536T^2+374{,}341{,}632T
MLA with query compression2Tddc+2Tddqc+2Thdqcdk+2Thdc(dk+dv)2Tdd_c+2Tdd_{qc}+2Thd_{qc}d_k+2Thd_c(d_k+d_v)2hT2(dk+dv)2hT^2(d_k+d_v)2Thdvd2Thd_vd65,536T2+272,629,760T65{,}536T^2+272{,}629{,}760T

DeepSeek-V2 attention configuration: d=5120, h=128, dk=dv=128, dc=512, dqc=1536d=5120,\ h=128,\ d_k=d_v=128,\ d_c=512,\ d_{qc}=1536.

Prefill FLOPs of MHA and MLA, except the RoPE branch, as functions of sequence length

4.2 Decode: Latent Caching and Matrix Absorption

Takeaway. In one decode step, the current-query length is 11 while the cache length is tt. The cheaper order keeps this 11 outside, contracts away the other dimensions as early as possible, and expands only afterward. This avoids reconstructing full K,VK,V for all tt cached tokens: MLA's FLOPs grow faster with tt, but it reads much less cache data.


MHA.

Before decoding the current position tt, the historical K<t,V<tK_{<t},V_{<t} have already been computed in previous decode steps. The current step only computes the new qt,kt,vtq_t,k_t,v_t; after kt,vtk_t,v_t are appended, the cache read for this step is Kt,VtK_{\le t},V_{\le t}. Across all heads, the current-token projection cost is 2dh(2dk+dv)2dh(2d_k+d_v).

Unlike prefill, there is only one new query, so the quadratic T2T^2 interaction disappears. The growing factor is now tt: the current query scans tt cached keys, and the resulting probabilities combine tt cached values.

The score, value, and output contractions are

q(h,1,dk)Kt(h,dk,t)score(h,1,t),FLOPs=2htdk,p(h,1,t)Vt(h,t,dv)o(h,1,dv),FLOPs=2htdv,o(h,1,dv)Wo(h,dv,d)y(1,d),FLOPs=2hdvd.\begin{aligned} \underset{(h,1,d_k)}{q} \underset{(h,d_k,t)}{K_{\le t}^\top} &\rightarrow \underset{(h,1,t)}{\mathrm{score}}, &\qquad \mathrm{FLOPs}&=2htd_k, \\ \underset{(h,1,t)}{p} \underset{(h,t,d_v)}{V_{\le t}} &\rightarrow \underset{(h,1,d_v)}{o}, &\qquad \mathrm{FLOPs}&=2htd_v, \\ \underset{(h,1,d_v)}{o} \underset{(h,d_v,d)}{W_o} &\rightarrow \underset{(1,d)}{y}, &\qquad \mathrm{FLOPs}&=2hd_vd. \end{aligned}

Both chains follow the same principle: start from the singleton current-token state and choose the contraction order that keeps every intermediate as small as possible.

The full value-output chain, with every dimension explicit, is

y(1,d)=p(h,1,t)Vt(h,t,dv)Wo(h,dv,d).\underset{(1,d)}{y} = \underset{(h,1,t)}{p} \underset{(h,t,d_v)}{V_{\le t}} \underset{(h,d_v,d)}{W_o}.

The two associations have different costs:

F(pVt)Wo=2htdvpVt:(h,1,dv)+2hdvd(pVt)Wo:(1,d),F_{(pV_{\le t})W_o} = \underbrace{2htd_v}_{pV_{\le t}:(h,1,d_v)} +\underbrace{2hd_vd}_{(pV_{\le t})W_o:(1,d)}, Fp(VtWo)=2htdvdVtWo:(h,t,d)+2htdp(VtWo):(1,d).F_{p(V_{\le t}W_o)} = \underbrace{2htd_vd}_{V_{\le t}W_o:(h,t,d)} +\underbrace{2htd}_{p(V_{\le t}W_o):(1,d)}.

Evaluating from left to right keeps the singleton query axis as an output axis and contracts away the cache-length axis before the output projection. The other order applies WoW_o to all tt cached values, carrying both tt and dd in the intermediate, which is more costly.

The score path follows the same principle. With dimensions explicit,

xt(1,d)WQ(h,d,dk)Kt(h,dk,t)scoret(h,1,t).\underset{(1,d)}{x_t} \underset{(h,d,d_k)}{W_Q} \underset{(h,d_k,t)}{K_{\le t}^{\top}} \rightarrow \underset{(h,1,t)}{\mathrm{score}_t}.

Keep the singleton query axis 11 outside and contract the other feature and cache axes around it, rather than expanding them first.

Therefore, the cached MHA decode FLOPs are

FMHA,decode,cached=2dh(2dk+dv)xtWQ,xtWK,xtWV+2ht(dk+dv)qKt,pVt+2hdvdoWo.F_{\mathrm{MHA,decode,cached}} =\underbrace{2dh(2d_k+d_v)}_{x_tW_Q,\,x_tW_K,\,x_tW_V} +\underbrace{2ht(d_k+d_v)}_{qK_{\le t}^\top,\,pV_{\le t}} +\underbrace{2hd_vd}_{oW_o}.

MLA.

MLA caches the shared latent rather than per-head K,VK,V. At this single decode step, the new latent is

xt(1,d)WDKV(d,dc)ctKV(1,dc),FLOPs=2ddc,\underset{(1,d)}{x_t} \underset{(d,d_c)}{W_{DKV}} \rightarrow \underset{(1,d_c)}{c_t^{KV}}, \qquad \mathrm{FLOPs}=2dd_c,

and the cache after appending it is CtKV=[C<tKV;ctKV]C^{KV}_{\le t}=[C^{KV}_{<t};c_t^{KV}] with shape (t,dc)(t,d_c). We now write the complete contraction chains first, without assuming an association.

Score chain. It is enough to analyze the Q-compressed case. The complete contraction is

scoret(h,1,t)=xt(1,d)WDQ(d,dqc)WUQ(h,dqc,dk)WUK(h,dk,dc)(CtKV)(dc,t).\underset{(h,1,t)}{\mathrm{score}_t} = \underset{(1,d)}{x_t} \underset{(d,d_{qc})}{W_{DQ}} \underset{(h,d_{qc},d_k)}{W_{UQ}} \underset{(h,d_k,d_c)}{W_{UK}^{\top}} \underset{(d_c,t)}{(C^{KV}_{\le t})^{\top}}.

First consider the coefficient of tt. The cache is the only factor carrying the tt axis. If every factor on its left is contracted first, the left intermediate has shape (h,1,dc)(h,1,d_c), and the final contraction costs

2htdc.2htd_c.

This is the smallest possible length-tt term: hh, tt, and dcd_c are unavoidable, while the remaining query axis has size 11. Applying CtKVC^{KV}_{\le t} earlier would retain an additional feature axis in a length-tt intermediate. Therefore, the cache should be contracted last:

(xt(1,d)WDQ(d,dqc)WUQ(h,dqc,dk)WUK(h,dk,dc))(CtKV)(dc,t).\left( \underset{(1,d)}{x_t} \underset{(d,d_{qc})}{W_{DQ}} \underset{(h,d_{qc},d_k)}{W_{UQ}} \underset{(h,d_k,d_c)}{W_{UK}^{\top}} \right) \underset{(d_c,t)}{(C^{KV}_{\le t})^{\top}}.

It remains to choose the association of the four-factor prefix

xt(1,d)WDQ(d,dqc)WUQ(h,dqc,dk)WUK(h,dk,dc).\underset{(1,d)}{x_t} \underset{(d,d_{qc})}{W_{DQ}} \underset{(h,d_{qc},d_k)}{W_{UQ}} \underset{(h,d_k,d_c)}{W_{UK}^{\top}}.

The table lists the five online binary associations, followed by every distinct offline precomposition of two or three adjacent weights. Excluding the common final cache contraction 2htdc2htd_c, their per-step costs are shown below; the one-time offline composition costs are omitted.

AssociationFLOPs before the cache contractionDeepSeek-V2
(((xt(1,d)WDQ(d,dqc))WUQ(h,dqc,dk))WUK(h,dk,dc))\left(\left(\left(\underset{(1,d)}{x_t}\underset{(d,d_{qc})}{W_{DQ}}\right)\underset{(h,d_{qc},d_k)}{W_{UQ}}\right)\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)2ddqc+2hdqcdk+2hdkdc2dd_{qc}+2hd_{qc}d_k+2hd_kd_c82.8M82.8\text{M}
((xt(1,d)(WDQ(d,dqc)WUQ(h,dqc,dk)))WUK(h,dk,dc))\left(\left(\underset{(1,d)}{x_t}\left(\underset{(d,d_{qc})}{W_{DQ}}\underset{(h,d_{qc},d_k)}{W_{UQ}}\right)\right)\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)2hddqcdk+2hddk+2hdkdc2hdd_{qc}d_k+2hdd_k+2hd_kd_c257.9B257.9\text{B}
(xt(1,d)WDQ(d,dqc))(WUQ(h,dqc,dk)WUK(h,dk,dc))\left(\underset{(1,d)}{x_t}\underset{(d,d_{qc})}{W_{DQ}}\right)\left(\underset{(h,d_{qc},d_k)}{W_{UQ}}\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)2ddqc+2hdqcdkdc+2hdqcdc2dd_{qc}+2hd_{qc}d_kd_c+2hd_{qc}d_c26.0B26.0\text{B}
xt(1,d)((WDQ(d,dqc)WUQ(h,dqc,dk))WUK(h,dk,dc))\underset{(1,d)}{x_t}\left(\left(\underset{(d,d_{qc})}{W_{DQ}}\underset{(h,d_{qc},d_k)}{W_{UQ}}\right)\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)2hddqcdk+2hddkdc+2hddc2hdd_{qc}d_k+2hdd_kd_c+2hdd_c344.3B344.3\text{B}
xt(1,d)(WDQ(d,dqc)(WUQ(h,dqc,dk)WUK(h,dk,dc)))\underset{(1,d)}{x_t}\left(\underset{(d,d_{qc})}{W_{DQ}}\left(\underset{(h,d_{qc},d_k)}{W_{UQ}}\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)\right)2hdqcdkdc+2hddqcdc+2hddc2hd_{qc}d_kd_c+2hdd_{qc}d_c+2hdd_c1.06T1.06\text{T}
((xt(1,d)(WDQWUQ)offline(h,d,dk))WUK(h,dk,dc))\left(\left(\underset{(1,d)}{x_t}\underset{(h,d,d_k)}{(W_{DQ}W_{UQ})_{\mathrm{offline}}}\right)\underset{(h,d_k,d_c)}{W_{UK}^{\top}}\right)2hddk+2hdkdc2hdd_k+2hd_kd_c184.5M184.5\text{M}
(xt(1,d)WDQ(d,dqc))(WUQWUK)offline(h,dqc,dc)\left(\underset{(1,d)}{x_t}\underset{(d,d_{qc})}{W_{DQ}}\right)\underset{(h,d_{qc},d_c)}{(W_{UQ}W_{UK}^{\top})_{\mathrm{offline}}}2ddqc+2hdqcdc2dd_{qc}+2hd_{qc}d_c217.1M217.1\text{M}
xt(1,d)(WDQWUQWUK)offline(h,d,dc)\underset{(1,d)}{x_t}\underset{(h,d,d_c)}{(W_{DQ}W_{UQ}W_{UK}^{\top})_{\mathrm{offline}}}2hddc2hdd_c671.1M671.1\text{M}

With the DeepSeek-V2 dimensions, the first row remains the minimum even after allowing all offline precompositions. It also has the clearest interpretation: multiplying by the singleton xtx_t first eliminates dd, then the same length-11 axis is carried through dqcd_{qc}, dkd_k, and dcd_c. The chosen order is therefore left to right, with total cost

Fscore,Qcomp=2ddqcxtWDQ+2hdqcdkctQWUQ+2hdkdcqtWUK+2htdcqabs(CtKV).F_{\mathrm{score,Qcomp}} =\underbrace{2dd_{qc}}_{x_tW_{DQ}} +\underbrace{2hd_{qc}d_k}_{c_t^QW_{UQ}} +\underbrace{2hd_kd_c}_{q_tW_{UK}^{\top}} +\underbrace{2htd_c}_{q_{\mathrm{abs}}(C^{KV}_{\le t})^{\top}}.

Without Q compression, delete WDQ,WUQW_{DQ},W_{UQ} and replace them with WQW_Q. The chain has fewer factors, so we leave the same association argument to the reader; the resulting minimum is

Fscore,noQ=2hddkxtWQ+2hdkdcqtWUK+2htdcqabs(CtKV).F_{\mathrm{score,noQ}} =\underbrace{2hdd_k}_{x_tW_Q} +\underbrace{2hd_kd_c}_{q_tW_{UK}^{\top}} +\underbrace{2htd_c}_{q_{\mathrm{abs}}(C^{KV}_{\le t})^{\top}}.

Value and output chain. The complete value-side chain is

yt(1,d)=pt(h,1,t)CtKV(t,dc)WUV(h,dc,dv)Wo(h,dv,d).\underset{(1,d)}{y_t} = \underset{(h,1,t)}{p_t} \underset{(t,d_c)}{C^{KV}_{\le t}} \underset{(h,d_c,d_v)}{W_{UV}} \underset{(h,d_v,d)}{W_o}.

First consider the coefficient of tt. Contracting ptp_t with CtKVC^{KV}_{\le t} immediately removes the cache-length axis through the singleton query axis, at a cost of

2htdc.2htd_c.

This is the smallest possible length-tt term. If CtKVC^{KV}_{\le t} is expanded through WUVW_{UV} first, that step alone costs 2htdcdv2htd_cd_v. Therefore, ptCtKVp_tC^{KV}_{\le t} should be evaluated first, producing oc:(h,1,dc)o_c:(h,1,d_c).

It remains to associate the three-factor chain

oc(h,1,dc)WUV(h,dc,dv)Wo(h,dv,d).\underset{(h,1,d_c)}{o_c} \underset{(h,d_c,d_v)}{W_{UV}} \underset{(h,d_v,d)}{W_o}.

Excluding the common first contraction 2htdc2htd_c, the table includes the two online associations and the offline-precomposed variant; its one-time composition cost is omitted:

AssociationFLOPs after ptCtKVp_tC^{KV}_{\le t}DeepSeek-V2
((oc(h,1,dc)WUV(h,dc,dv))Wo(h,dv,d))\left(\left(\underset{(h,1,d_c)}{o_c}\underset{(h,d_c,d_v)}{W_{UV}}\right)\underset{(h,d_v,d)}{W_o}\right)2hdcdv+2hdvd2hd_cd_v+2hd_vd184.5M184.5\text{M}
oc(h,1,dc)(WUV(h,dc,dv)Wo(h,dv,d))\underset{(h,1,d_c)}{o_c}\left(\underset{(h,d_c,d_v)}{W_{UV}}\underset{(h,d_v,d)}{W_o}\right)2hdcdvd+2hdcd2hd_cd_vd+2hd_cd86.6B86.6\text{B}
oc(h,1,dc)(WUVWo)offline(h,dc,d)\underset{(h,1,d_c)}{o_c}\underset{(h,d_c,d)}{(W_{UV}W_o)_{\mathrm{offline}}}2hdcd2hd_cd671.1M671.1\text{M}

The first row remains the minimum. Again, the cheaper order keeps the length-11 axis outside and evaluates the chain from left to right. The complete value-side cost is

Fvalue=2htdcptCtKV+2hdcdvocWUV+2hdvdoWo.F_{\mathrm{value}} =\underbrace{2htd_c}_{p_tC^{KV}_{\le t}} +\underbrace{2hd_cd_v}_{o_cW_{UV}} +\underbrace{2hd_vd}_{oW_o}.

Combining the chosen associations, the complete cached-decode costs are

FMLA,decode,cached,noQ=2ddc+2hddkcurrent-token projections+2hdkdc+4htdcscore absorption and latent reductions+2hdcdv+2hdvdvalue and output projections,FMLA,decode,cached,Qcomp=2ddc+2ddqc+2hdqcdkcurrent-token projections+2hdkdc+4htdcscore absorption and latent reductions+2hdcdv+2hdvdvalue and output projections.\begin{aligned} F_{\mathrm{MLA,decode,cached,noQ}} &=\underbrace{2dd_c+2hdd_k}_{\text{current-token projections}} +\underbrace{2hd_kd_c+4htd_c}_{\text{score absorption and latent reductions}} +\underbrace{2hd_cd_v+2hd_vd}_{\text{value and output projections}}, \\ F_{\mathrm{MLA,decode,cached,Qcomp}} &=\underbrace{2dd_c+2dd_{qc}+2hd_{qc}d_k}_{\text{current-token projections}} +\underbrace{2hd_kd_c+4htd_c}_{\text{score absorption and latent reductions}} +\underbrace{2hd_cd_v+2hd_vd}_{\text{value and output projections}}. \end{aligned}

Comparison.

MethodCurrent-token setupScore chainValue + output chainCache elements/token (except RoPE branch)DeepSeek-V2 decode FLOPs (except RoPE branch)DeepSeek-V2 cache elements/token (except RoPE branch)
MHA2dh(2dk+dv)2dh(2d_k+d_v)2htdk2htd_k2htdv+2hdvd2htd_v+2hd_vdh(dk+dv)h(d_k+d_v)65,536t+671,088,64065{,}536t+671{,}088{,}64032,76832{,}768
MLA without query compression2ddc+2hddk2dd_c+2hdd_k2hdkdc+2htdc2hd_kd_c+2htd_c2htdc+2hdcdv+2hdvd2htd_c+2hd_cd_v+2hd_vddcd_c262,144t+374,341,632262{,}144t+374{,}341{,}632512512
MLA with query compression2ddc+2ddqc+2hdqcdk2dd_c+2dd_{qc}+2hd_{qc}d_k2hdkdc+2htdc2hd_kd_c+2htd_c2htdc+2hdcdv+2hdvd2htd_c+2hd_cd_v+2hd_vddcd_c262,144t+272,629,760262{,}144t+272{,}629{,}760512512

DeepSeek-V2 attention configuration: d=5120, h=128, dk=dv=128, dc=512, dqc=1536d=5120,\ h=128,\ d_k=d_v=128,\ d_c=512,\ d_{qc}=1536. Both FLOPs and cache values omit the RoPE branch.

DeepSeek-V2 decode FLOPs and per-layer cache growth, both excluding the RoPE branch

In this RoPE-free comparison, MLA's decode FLOPs grow 4×4\times faster with tt, while its cache is 64×64\times smaller than MHA's, a 98.4%98.4\% reduction in cached elements.

4.3 Why Compress Q Too?

The two MLA variants above give a direct ablation of Q compression. Since it factorizes only the query projection, its effects are concentrated on query-side activations and projection FLOPs rather than the cache path:

  • Training activation memory. We do not know DeepSeek's internal implementation, but we think recomputation and kernel fusion can be used to reduce activation memory.
  • Prefill and training FLOPs. The factorized projection is cheaper when dqc(d+hdk)<hddkd_{qc}(d+hd_k)<hdd_k, as the comparison in Section 4.1 shows.
  • Decode. Only the current-token query projection changes; the latent cache and the length-tt attention term remain the same. Q compression therefore contributes little to decode compared with KV compression.

5. Making MLA Compatible with RoPE

5.1 Why RoPE and MLA Compression Are Incompatible

Takeaway. Without RoPE, WUKW_{UK} can be absorbed into the query, producing one latent query that is reused across all key positions. With RoPE, a key-position-dependent RuR_u appears before WUKW_{UK}, so the same absorbed query can no longer be reused across all keys.

Start with the complete RoPE QKQK^\top expression. Fix a head ss. Let qs,tC:(1,dk)q_{s,t}^C:(1,d_k) be its content query at position tt, let cuKV:(1,dc)c_u^{KV}:(1,d_c) be the KV latent at position uu, and let WUK,s:(dc,dk)W_{UK,s}:(d_c,d_k). The reconstructed content key is ks,uC=cuKVWUK,sk_{s,u}^C=c_u^{KV}W_{UK,s}. Under the row-vector RoPE convention from Section 3, the query and key are right-multiplied by RtR_t^\top and RuR_u^\top, respectively. The score for this pair of positions is therefore

Ss,t,uRoPE=(qs,tCRt)((cuKVWUK,s)Ru)=qs,tCRtRuWUK,s(cuKV).\begin{aligned} S^{\mathrm{RoPE}}_{s,t,u} &= \left(q_{s,t}^C R_t^\top\right) \left((c_u^{KV}W_{UK,s})R_u^\top\right)^\top \\ &= q_{s,t}^C R_t^\top R_u W_{UK,s}^\top (c_u^{KV})^\top. \end{aligned}

The only question is how to parenthesize this matrix chain. First remove RoPE and recall why ordinary MLA supports key absorption. Let TT be the number of query positions and UU the number of key positions. The complete score chain is

SC(h,T,U)=QC(h,T,dk)WUK(h,dk,dc)(CKV)(dc,U).\underset{(h,T,U)}{S^C} = \underset{(h,T,d_k)}{Q^C} \underset{(h,d_k,d_c)}{W_{UK}^\top} \underset{(d_c,U)}{(C^{KV})^\top}.

One order reconstructs all keys from the latent before computing attention:

QC(h,T,dk)(WUK(h,dk,dc)(CKV)(dc,U))=QC(h,T,dk)(KC)(h,dk,U).\underset{(h,T,d_k)}{Q^C} \left( \underset{(h,d_k,d_c)}{W_{UK}^\top} \underset{(d_c,U)}{(C^{KV})^\top} \right) = \underset{(h,T,d_k)}{Q^C} \underset{(h,d_k,U)}{(K^C)^\top}.

The other order absorbs WUKW_{UK} into the query first:

(QC(h,T,dk)WUK(h,dk,dc))(CKV)(dc,U)=Q^C(h,T,dc)(CKV)(dc,U).\left( \underset{(h,T,d_k)}{Q^C} \underset{(h,d_k,d_c)}{W_{UK}^\top} \right) \underset{(d_c,U)}{(C^{KV})^\top} = \underset{(h,T,d_c)}{\widehat Q^C} \underset{(d_c,U)}{(C^{KV})^\top}.

WUKW_{UK} is a fixed weight, so the absorbed query Q^C=QCWUK\widehat Q^C=Q^CW_{UK}^\top is independent of the key position. Each query needs this (h,T,dc)(h,T,d_c) intermediate only once, after which it can be multiplied by every cached latent. This is key absorption.

Now put RoPE back. The complete score can be evaluated by reconstructing and rotating the key first:

(qs,tCRt)(Ru(WUK,s(cuKV))).\left(q_{s,t}^C R_t^\top\right) \left( R_u \left(W_{UK,s}^\top(c_u^{KV})^\top\right) \right).

Or we can try to put every factor before the key latent into the left parenthesis, just as we did above:

(qs,tCRtRuWUK,s)the part we would like to absorb into the query(cuKV).\underbrace{\left(q_{s,t}^C R_t^\top R_u W_{UK,s}^\top\right)}_{\text{the part we would like to absorb into the query}} (c_u^{KV})^\top.

The difference is now visible in the chain itself: the parenthesis contains RuR_u. It changes with key position uu, so it cannot produce one Q^C\widehat Q^C that is shared by all keys. Expanding all query and key positions makes the shape of this intermediate not (h,T,dc)(h,T,d_c) but

(h,T,U,dc).(h,T,U,d_c).

Here UU is the key-position axis: U=TU=T during prefill; for one decode step, T=1T=1 and U=tU=t. RoPE does not invalidate the matrix multiplication, but it turns the supposed “absorbed query” into a different object for every (t,u)(t,u) position pair. The key-length axis remains, defeating the purpose of absorption. If the cache stores only CKVC^{KV}, decoding must either reconstruct and rotate the historical keys or explicitly form this (h,1,t,dc)(h,1,t,d_c) intermediate.

Standard RoPE and position-independent key absorption therefore cannot occupy the same feature path. The elementwise RoPE formulation in Section 3 is exactly equivalent to the rotation-matrix form used here; rewriting the computation elementwise does not remove its dependence on key position uu, so it cannot recover position-independent absorption. Full MLA separates the two in Section 5.2: an absorbable content branch and a dedicated positional branch for RoPE.

5.2 Full Formulation of MLA: Decoupled Rotary Position Embedding

Takeaway. Full MLA keeps the content channel unrotated and absorbable, while a separate low-dimensional RoPE channel carries position. The cache stores CKVC^{KV} and one shared RoPE key, and the two channel scores are added before softmax.

Section 5.1 showed that one path cannot simultaneously support standard RoPE and position-independent key absorption. Full MLA therefore stops asking the same representation to do both jobs and splits the feature space into two branches: an unrotated content branch that remains fully compressible and absorbable, and a positional branch that is free to apply RoPE without preserving absorption. In short, one branch is designed to compress cleanly, while the other is designed to rotate cleanly.


MLA with decoupled RoPE.

Let drd_r denote the per-head dimension of the decoupled RoPE branch, written as dhRd_h^R in the DeepSeek-V2 report. We use the same rotation tensor as Section 3, now over drd_r:

R(r)(T,dr,dr),Rt,:,:(r)=(Rt(r)).\underset{(T,d_r,d_r)}{\mathcal R^{(r)}}, \qquad \mathcal R^{(r)}_{t,:,:}=(R_t^{(r)})^\top.

The query path is

CQ(T,dqc)=X(T,d)WDQ(d,dqc),\underset{(T,d_{qc})}{C^Q} = \underset{(T,d)}{X} \underset{(d,d_{qc})}{W_{DQ}}, QC(h,T,dk)=CQ(T,dqc)WUQ(h,dqc,dk),QR(h,T,dr)=CQ(T,dqc)WQR(h,dqc,dr),\underset{(h,T,d_k)}{Q^C} = \underset{(T,d_{qc})}{C^Q} \underset{(h,d_{qc},d_k)}{W_{UQ}}, \qquad \underset{(h,T,d_r)}{\overline Q^R} = \underset{(T,d_{qc})}{C^Q} \underset{(h,d_{qc},d_r)}{W_{QR}}, QR(h,T,dr)=QR(h,T,dr)R(r)(T,dr,dr).\underset{(h,T,d_r)}{Q^R} = \underset{(h,T,d_r)}{\overline Q^R} \underset{(T,d_r,d_r)}{\mathcal R^{(r)}}.

The query-side RoPE operation can remain unchanged. The key side is different: if the RoPE key were stored in compressed form, every decode step would have to expand and rotate all tt historical keys again, creating substantial repeated computation. Full MLA therefore caches the materialized KRK^R, rather than a compressed positional-key latent.

The key-value path is

CKV(T,dc)=X(T,d)WDKV(d,dc),\underset{(T,d_c)}{C^{KV}} = \underset{(T,d)}{X} \underset{(d,d_c)}{W_{DKV}}, KC(h,T,dk)=CKV(T,dc)WUK(h,dc,dk),V(h,T,dv)=CKV(T,dc)WUV(h,dc,dv),\underset{(h,T,d_k)}{K^C} = \underset{(T,d_c)}{C^{KV}} \underset{(h,d_c,d_k)}{W_{UK}}, \qquad \underset{(h,T,d_v)}{V} = \underset{(T,d_c)}{C^{KV}} \underset{(h,d_c,d_v)}{W_{UV}}, KR(T,dr)=X(T,d)WKR(d,dr),KR(T,dr)=KR(T,dr)R(r)(T,dr,dr).\underset{(T,d_r)}{\overline K^R} = \underset{(T,d)}{X} \underset{(d,d_r)}{W_{KR}}, \qquad \underset{(T,d_r)}{K^R} = \underset{(T,d_r)}{\overline K^R} \underset{(T,d_r,d_r)}{\mathcal R^{(r)}}.

Storing a separate positional key for every head would add hdrhd_r cache elements per token. Full MLA instead uses one KR:(T,dr)K^R:(T,d_r) shared by all hh query heads. The RoPE branch is therefore MQA-style: it keeps per-head positional queries QR:(h,T,dr)Q^R:(h,T,d_r), but uses a single shared positional key head. This keeps the additional cache at drd_r elements per token.

The full per-head query and key are the feature-axis concatenations

Q(h,T,dk+dr)=[QC(h,T,dk);QR(h,T,dr)],\underset{(h,T,d_k+d_r)}{Q} = \left[ \underset{(h,T,d_k)}{Q^C}; \underset{(h,T,d_r)}{Q^R} \right], K(h,T,dk+dr)=[KC(h,T,dk);KR(T,dr) broadcast over h].\underset{(h,T,d_k+d_r)}{K} = \left[ \underset{(h,T,d_k)}{K^C}; \underset{(T,d_r)}{K^R} \text{ broadcast over }h \right].

Why does the score become the sum of two terms? Concatenation partitions the last tensor dimension into two disjoint blocks. For head ss, query position tt, and key position uu, the full dot product is

Ss,t,u==1dk+drQs,t,Ks,u,=a=1dkQs,t,aCKs,u,aC+b=1drQs,t,bRKu,bR=Ss,t,uC+Ss,t,uR.\begin{aligned} S_{s,t,u} &= \sum_{\ell=1}^{d_k+d_r}Q_{s,t,\ell}K_{s,u,\ell} \\ &= \sum_{a=1}^{d_k}Q^C_{s,t,a}K^C_{s,u,a} + \sum_{b=1}^{d_r}Q^R_{s,t,b}K^R_{u,b} \\ &=S^C_{s,t,u}+S^R_{s,t,u}. \end{aligned}

The same identity can be read as block tensor multiplication:

S(h,T,T)=[QC(h,T,dk)QR(h,T,dr)][(KC)(h,dk,T)(KR)(dr,T) broadcast over h]=SC(h,T,T)+SR(h,T,T).\underset{(h,T,T)}{S} = \left[ \begin{array}{cc} \underset{(h,T,d_k)}{Q^C} & \underset{(h,T,d_r)}{Q^R} \end{array} \right] \left[ \begin{array}{c} \underset{(h,d_k,T)}{(K^C)^\top} \\ \underset{(d_r,T)}{(K^R)^\top} \text{ broadcast over }h \end{array} \right] = \underset{(h,T,T)}{S^C} + \underset{(h,T,T)}{S^R}.

There are no cross terms such as QC(KR)Q^C(K^R)^\top or QR(KC)Q^R(K^C)^\top: the content and RoPE coordinates occupy different feature blocks, and the dot product pairs coordinates only within the same block. Therefore, the two score channels can be computed separately:

SC(h,T,T)=QC(h,T,dk)(KC)(h,dk,T),SR(h,T,T)=QR(h,T,dr)(KR)(dr,T),\underset{(h,T,T)}{S^C} = \underset{(h,T,d_k)}{Q^C} \underset{(h,d_k,T)}{(K^C)^\top}, \qquad \underset{(h,T,T)}{S^R} = \underset{(h,T,d_r)}{Q^R} \underset{(d_r,T)}{(K^R)^\top}, P(h,T,T)=softmax ⁣(SC+SRdk+dr),\underset{(h,T,T)}{P} = \operatorname{softmax}\!\left( \frac{S^C+S^R}{\sqrt{d_k+d_r}} \right), O(h,T,dv)=P(h,T,T)V(h,T,dv),Y(T,d)=O(h,T,dv)Wo(h,dv,d).\underset{(h,T,d_v)}{O} = \underset{(h,T,T)}{P} \underset{(h,T,d_v)}{V}, \qquad \underset{(T,d)}{Y} = \underset{(h,T,d_v)}{O} \underset{(h,d_v,d)}{W_o}.

For cached decode, content absorption and the positional branch give

score(h,1,t)=(qC(h,1,dk)WUK(h,dk,dc))(CtKV)(dc,t)+qR(h,1,dr)(KtR)(dr,t),\underset{(h,1,t)}{\mathrm{score}} = \left( \underset{(h,1,d_k)}{q^C} \underset{(h,d_k,d_c)}{W_{UK}^\top} \right) \underset{(d_c,t)}{(C_{\le t}^{KV})^\top} + \underset{(h,1,d_r)}{q^R} \underset{(d_r,t)}{(K_{\le t}^R)^\top}, o(h,1,dv)=(p(h,1,t)CtKV(t,dc))WUV(h,dc,dv).\underset{(h,1,d_v)}{o} = \left( \underset{(h,1,t)}{p} \underset{(t,d_c)}{C_{\le t}^{KV}} \right) \underset{(h,d_c,d_v)}{W_{UV}}.

Thus the content key and value remain behind CKVC^{KV}, while only the shared rotated key KRK^R is cached explicitly.


Cache supplement.

FormCached tensors per token per layerElementsDeepSeek-V2
MHAK,VK,Vh(dk+dv)h(d_k+d_v)3276832768
MLA in Section 4, with RoPE omittedCKVC^{KV}dcd_c512512
Full MLACKV,KRC^{KV},K^Rdc+drd_c+d_r576576

For DeepSeek-V2, dc=512d_c=512 and dr=64d_r=64. The full cache reduction relative to MHA is therefore 32768/57656.9×32768/576\approx56.9\times, rather than the 64×64\times obtained from the RoPE-free latent alone.


FLOPs supplement.

This supplement follows the accounting in Section 4 and counts the tensor contractions introduced by the decoupled branch. It also counts the explicit addition SC+SRS^C+S^R: one addition for every score element, or hT2hT^2 FLOPs in prefill and htht FLOPs in cached decode. The structured elementwise RoPE rotations themselves remain outside the comparison.

PhaseAdded projectionsAdded score computationTotal added FLOPsDeepSeek-V2
Prefill / training2Thdqcdr+2Tddr2Thd_{qc}d_r+2Tdd_r2hT2dr+hT22hT^2d_r+hT^22Thdqcdr+2Tddr+2hT2dr+hT22Thd_{qc}d_r+2Tdd_r+2hT^2d_r+hT^225,821,184T+16,512T225{,}821{,}184T+16{,}512T^2
Cached decode2hdqcdr+2ddr2hd_{qc}d_r+2dd_r2htdr+ht2htd_r+ht2hdqcdr+2ddr+2htdr+ht2hd_{qc}d_r+2dd_r+2htd_r+ht25,821,184+16,512t25{,}821{,}184+16{,}512t

For cached decode, the coefficient of tt is now 4hdc+2hdr+h=278,6564hd_c+2hd_r+h=278{,}656 for DeepSeek-V2.

6. How MLA Changes MTP's Speculative-Decoding Gain

Takeaway. MHA speculative verification reuses KV-cache data and can hide the added query computation behind the memory-bound read. MLA reduces that read and moves attention toward the compute-bound regime, increasing the marginal cost of each additional query.

The DeepSeek-V3 Technical Report trains MTP as an auxiliary objective. Its actual configuration uses one MTP module (D=1D=1), which predicts one additional token. The module can be discarded for ordinary inference or used to draft a token for speculative decoding. On DeepSeek-V3, the report gives an 85%85\%-90%90\% acceptance rate for this second token and reports 1.8×1.8\times TPS with speculative decoding.

In MHA, several verification queries reuse the same KV-cache data. Because decode is strongly memory-bound, much of their additional attention computation can be hidden behind that cache read: memory bandwidth is already the bottleneck, while the compute units still have room. MLA changes this balance. It replaces the full per-head KV cache with CKVC^{KV} and the decoupled RoPE key, while absorption adds contractions around CKVC^{KV}. The cache read becomes smaller and attention moves toward the compute-bound regime, leaving less room to hide the additional computation. This is why MLA weakens part of MTP's speculative-decoding gain.

Contributions and Acknowledgements

This note was written under NonLinear-1. Place holder contributed to the derivations and writing. We thank Place holder for discussions and feedback.


How to cite this article.

If you would like to cite this article, please use:

NonLinear-1. (Jul. 8, 2026). “MLA, dim by dim” [Blog post]. Retrieved from https://nonlinear1.com/en/posts/mla-dim-by-dim

@online{nonlinear1-mla-dim-by-dim,
        title={MLA, dim by dim},
        author={{NonLinear-1}},
        year={2026},
        month={July},
        url={\url{https://nonlinear1.com/en/posts/mla-dim-by-dim}},
}