Pratham Patel
· 24 min read

DeepSeek-V4 Hybrid Attention: CSA and HCA from Scratch

Building the long-context attention of DeepSeek-V4 from the ground up — dual KV streams, overlapping softmax compression, the lightning indexer, top-$k$ sparse selection, and heavier compression — all derived step by step on a six-token example.

DeepSeek-V4 (DeepSeek-AI, 2026) introduces two Mixture-of-Experts models — V4-Pro (1.6T parameters, 49B activated) and V4-Flash (284B parameters, 13B activated) — both supporting a context length of one million tokens. At 1M-context inference, V4-Pro spends only 27%27\% of the single-token FLOPs and 10%10\% of the KV cache of DeepSeek-V3.2. V4-Flash pushes that to 10%10\% of FLOPs and 7%7\% of KV cache.

The architectural change that drives those numbers is not the Mixture-of-Experts backbone, not the Muon optimizer, not the Manifold-Constrained Hyper-Connections. It is the hybrid attention that interleaves two new attention mechanisms across layers:

  • Compressed Sparse Attention (CSA) — compress the KV cache by a factor of mm, then attend sparsely over the compressed entries using a learned index.
  • Heavily Compressed Attention (HCA) — compress the KV cache by a much larger factor mmm' \gg m, then attend densely over the (tiny) compressed cache.

We will derive both mechanisms from scratch on a six-token example, trace every softmax and weighted sum by hand, and verify the KV-cache ledger that yields the 10%10\% headline number.

The DeepSeek Sparse Attention (DSA) mechanism from V3.2, which CSA builds on top of, was derived in DeepSeek Sparse Attention from scratch. We will restate what we need so the post is self-contained.


The Running Example

We take a causal sequence of n=6n = 6 tokens with hidden size d=2d = 2. The hidden states are:

H=[h0h1h2h3h4h5]=[100221110110]R6×2.H = \begin{bmatrix} \mathbf{h}_0 \\ \mathbf{h}_1 \\ \mathbf{h}_2 \\ \mathbf{h}_3 \\ \mathbf{h}_4 \\ \mathbf{h}_5 \end{bmatrix} = \begin{bmatrix} 1 & 0 \\ 0 & 2 \\ 2 & 1 \\ 1 & 1 \\ 0 & 1 \\ 1 & 0 \end{bmatrix} \in \mathbb{R}^{6 \times 2}.

The attention-specific parameters we fix for this post:

  • Compressed head dimension c=2c = 2 — the width of one compressed KV entry.
  • CSA compression ratio m=2m = 2 — how many tokens go into one CSA compressed entry.
  • HCA compression ratio m=3m' = 3 — how many tokens go into one HCA compressed entry.
  • Sparse selection budget k=1k = 1 — how many CSA compressed entries each query keeps.
  • Number of query heads nh=1n_h = 1 — kept to one for readability. Production V4-Pro uses nh=128n_h = 128.
  • Indexer head dimension cI=2c^I = 2 and number of indexer heads nhI=1n_h^I = 1.
  • Query compression dimension dc=2d_c = 2.

Production V4-Pro values are c=512c = 512, m=4m = 4, m=128m' = 128, k=1024k = 1024, nh=128n_h = 128. We use smaller numbers so the arithmetic fits on one line. Every ratio we compute will scale up unchanged.

We focus on the query token t=5t = 5 — the last token in the sequence — because a causal model attends only to preceding tokens, and t=5t = 5 has the most preceding context (tokens 0,1,2,3,40, 1, 2, 3, 4) to attend over.


1. The Problem: Quadratic KV Cache

In standard Multi-Head Attention (Vaswani et al., 2017) with GQA (Ainslie et al., 2023) as the BF16 GQA8 baseline the DeepSeek-V4 paper uses, the KV cache stores one key and one value vector per token per KV head per layer. With head dimension 128128, 88 KV groups, LL layers, and BF16 (2 bytes per element):

KV cache bytes per token per layer=2(nhKVdh)2=2(8128)2=4096 bytes.\text{KV cache bytes per token per layer} = 2 \cdot (n_h^{KV} \cdot d_h) \cdot 2 = 2 \cdot (8 \cdot 128) \cdot 2 = 4096 \text{ bytes}.

For a sequence of n=106n = 10^6 tokens and L=61L = 61 layers (V4-Pro), that is:

nL4096=1066140962.51011 bytes=250 GB per request.n \cdot L \cdot 4096 = 10^6 \cdot 61 \cdot 4096 \approx 2.5 \cdot 10^{11} \text{ bytes} = 250 \text{ GB per request}.

This is a quadratic-in-sequence read pattern during decode — each new token must read all preceding KV entries — and a linear-in-sequence storage pattern. Both blow up at 10610^6 tokens. CSA and HCA attack both numbers at their root: they reduce the number of stored KV entries by a factor mm or mm', and they reduce how many entries each query reads by the sparse selection kk.


2. Strategy: Compress Groups of Tokens into One Entry

The common idea behind CSA and HCA is the compression operation: take a window of mm (or mm') contiguous hidden states, project each into a cc-dimensional KV candidate, generate per-position weights, and combine them into a single compressed entry.

Symbolically, if C1:mRm×cC_{1:m} \in \mathbb{R}^{m \times c} is a block of mm KV candidates and S1:mRm×cS_{1:m} \in \mathbb{R}^{m \times c} is a matching block of weights (one weight vector per candidate), then the compressed entry is:

CComp=j=1mSjCjRc,C^{\text{Comp}} = \sum_{j=1}^{m} S_j \odot C_j \in \mathbb{R}^{c},

where \odot is the Hadamard product (elementwise multiplication). This is a learned, data-dependent pooling over the window — every output coordinate is a convex combination of the same coordinate across the mm positions, with a separate softmax per coordinate.

HCA stops here. CSA adds a twist — each compressed entry draws from 2m2m positions, with overlap between neighbours, so that block boundaries blur — and then performs sparse selection over the compressed entries.

We derive CSA first, then specialize to HCA.


3. CSA Step 1 — Dual KV Streams and Compression Weights

CSA maintains two series of raw KV entries and two series of compression weights:

Ca=HWaKV,Cb=HWbKV,C^a = H \cdot W^{aKV}, \qquad C^b = H \cdot W^{bKV}, Za=HWaZ,Zb=HWbZ,Z^a = H \cdot W^{aZ}, \qquad Z^b = H \cdot W^{bZ},

where WaKV,WbKV,WaZ,WbZRd×cW^{aKV}, W^{bKV}, W^{aZ}, W^{bZ} \in \mathbb{R}^{d \times c} are learnable. All four have shape Rn×c\mathbb{R}^{n \times c} after the multiplication.

To trace the computation we pick concrete weight matrices:

WaKV=[1001],WbKV=[0110],W^{aKV} = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}, \quad W^{bKV} = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix}, WaZ=[1000],WbZ=[0010].W^{aZ} = \begin{bmatrix} 1 & 0 \\ 0 & 0 \end{bmatrix}, \quad W^{bZ} = \begin{bmatrix} 0 & 0 \\ 1 & 0 \end{bmatrix}.

WaKVW^{aKV} is the identity (stream aa preserves HH); WbKVW^{bKV} swaps coordinates (stream bb is a reshuffled view). WaZW^{aZ} keeps only column 0 of HH; WbZW^{bZ} keeps only column 1 of HH and places it in column 0 of ZbZ^b. These choices are intentional — they let us check each derivation by eye.

Computing Ca=HWaKVC^a = H \cdot W^{aKV} (using the matrix multiplication rule (HW)ij=kHikWkj(HW)_{ij} = \sum_k H_{ik} W_{kj}):

Ca=[100221110110].C^a = \begin{bmatrix} 1 & 0 \\ 0 & 2 \\ 2 & 1 \\ 1 & 1 \\ 0 & 1 \\ 1 & 0 \end{bmatrix}.

Computing Cb=HWbKVC^b = H \cdot W^{bKV} (swap columns):

Cb=[012012111001].C^b = \begin{bmatrix} 0 & 1 \\ 2 & 0 \\ 1 & 2 \\ 1 & 1 \\ 1 & 0 \\ 0 & 1 \end{bmatrix}.

Computing Za=HWaZZ^a = H \cdot W^{aZ} (column 0 of HH into column 0 of ZaZ^a; column 1 zeroed):

Za=[100020100010].Z^a = \begin{bmatrix} 1 & 0 \\ 0 & 0 \\ 2 & 0 \\ 1 & 0 \\ 0 & 0 \\ 1 & 0 \end{bmatrix}.

Computing Zb=HWbZZ^b = H \cdot W^{bZ} (column 1 of HH into column 0 of ZbZ^b; column 1 zeroed):

Zb=[002010101000].Z^b = \begin{bmatrix} 0 & 0 \\ 2 & 0 \\ 1 & 0 \\ 1 & 0 \\ 1 & 0 \\ 0 & 0 \end{bmatrix}.

Why two streams and not one? The very next step will softmax over twice as many positions as one block’s width. If there were only one stream, the softmax for a block would only see mm elements. With two, it sees 2m2m — giving each compressed entry a view that extends across the block boundary. The paper calls this overlapped compression. We will see the overlap in Step 2.


4. CSA Step 2 — The Overlapping 2m2m-Softmax

For each compressed index i{0,1,,n/m1}i \in \{0, 1, \ldots, n/m - 1\}, CSA constructs the ii-th compressed entry from a window of 2m2m raw positions:

  • positions mi,mi+1,,m(i+1)1mi, mi+1, \ldots, m(i+1)-1 from stream aa (the “current block”),
  • positions m(i1),m(i1)+1,,mi1m(i-1), m(i-1)+1, \ldots, mi-1 from stream bb (the “previous block”).

For i=0i = 0 the bb-positions would be m,,1-m, \ldots, -1, which do not exist. The paper pads: Zm:1bZ^b_{-m:-1} is set to -\infty and Cm:1bC^b_{-m:-1} to zero.

Adding learnable positional biases Ba,BbRm×cB^a, B^b \in \mathbb{R}^{m \times c} (we choose Ba=Bb=0B^a = B^b = 0 for readability), the softmax is:

[Smi:m(i+1)1aSm(i1):mi1b]=Softmaxrow ⁣([Zmi:m(i+1)1a+BaZm(i1):mi1b+Bb]),\begin{bmatrix} S^a_{mi:m(i+1)-1} \\ S^b_{m(i-1):mi-1} \end{bmatrix} = \text{Softmax}_{\text{row}}\!\left( \begin{bmatrix} Z^a_{mi:m(i+1)-1} + B^a \\ Z^b_{m(i-1):mi-1} + B^b \end{bmatrix} \right),

where Softmaxrow\text{Softmax}_{\text{row}} denotes a softmax applied down the row dimension — for each of the cc output columns independently, normalize across the 2m2m stacked rows. Each column therefore gets its own independent softmax distribution over 2m2m positions.

The softmax function itself is:

Softmax(x)j=exp(xj)k=12mexp(xk).\text{Softmax}(x)_j = \frac{\exp(x_j)}{\sum_{k=1}^{2m} \exp(x_k)}.

We use m=2m = 2, so 2m=42m = 4. Let us compute both CSA compressed entries that query t=5t = 5 is allowed to see.

Block i=0i = 0

  • aa-positions: 0,10, 1; bb-positions: 2,1-2, -1 (padded with -\infty in ZbZ^b, zero in CbC^b).

Column 00 of the stacked input:

[Z0:1,0a  Z2:1,0b]=[1, 0, , ].\left[Z^a_{0:1,0} \ \Big\Vert \ Z^b_{-2:-1,0}\right] = [\,1,\ 0,\ -\infty,\ -\infty\,].

(Here \Vert stacks vertically; we display horizontally to save space.) The exponential of -\infty is 00, so the denominator is e1+e0=2.718+1=3.718e^1 + e^0 = 2.718 + 1 = 3.718. The softmax is:

[S0,0a, S1,0a, S2,0b, S1,0b]=[ee+1, 1e+1, 0, 0][0.731, 0.269, 0, 0].[S^a_{0,0},\ S^a_{1,0},\ S^b_{-2,0},\ S^b_{-1,0}] = \left[\tfrac{e}{e+1},\ \tfrac{1}{e+1},\ 0,\ 0\right] \approx [0.731,\ 0.269,\ 0,\ 0].

Numerical check — the weights must sum to 11 (a softmax is a probability distribution):

0.731+0.269+0+0=1.000. 0.731 + 0.269 + 0 + 0 = 1.000. \ \checkmark

Column 11: all entries of Za[:,1]Z^a[:,1] and Zb[:,1]Z^b[:,1] are zero, so the input is [0,0,,][0, 0, -\infty, -\infty]. Softmax gives:

[S0,1a, S1,1a, S2,1b, S1,1b]=[0.5, 0.5, 0, 0].[S^a_{0,1},\ S^a_{1,1},\ S^b_{-2,1},\ S^b_{-1,1}] = [0.5,\ 0.5,\ 0,\ 0].

Block i=1i = 1

  • aa-positions: 2,32, 3; bb-positions: 0,10, 1 (now real, no padding).

Column 00 of the stacked input:

[Z2,0a, Z3,0a, Z0,0b, Z1,0b]=[2, 1, 0, 2].[Z^a_{2,0},\ Z^a_{3,0},\ Z^b_{0,0},\ Z^b_{1,0}] = [2,\ 1,\ 0,\ 2].

The denominator is e2+e1+e0+e2=7.389+2.718+1+7.389=18.496e^2 + e^1 + e^0 + e^2 = 7.389 + 2.718 + 1 + 7.389 = 18.496. The softmax is:

[7.38918.496, 2.71818.496, 118.496, 7.38918.496][0.400, 0.147, 0.054, 0.400].\left[\tfrac{7.389}{18.496},\ \tfrac{2.718}{18.496},\ \tfrac{1}{18.496},\ \tfrac{7.389}{18.496}\right] \approx [0.400,\ 0.147,\ 0.054,\ 0.400].

Numerical check: 0.400+0.147+0.054+0.400=1.00110.400 + 0.147 + 0.054 + 0.400 = 1.001 \approx 1 (rounding). \checkmark

Column 11: all zeros, so softmax =[0.25,0.25,0.25,0.25]= [0.25, 0.25, 0.25, 0.25].

Notice the overlap already: block i=1i = 1‘s softmax includes positions 00 and 11 through the bb-stream, while block i=0i = 0 also used positions 00 and 11 through the aa-stream. This is the “overlapped” in overlapped compression — information from positions 0,10, 1 is available to both compressed entries.


5. CSA Step 3 — The Weighted Sum

Given the softmax weights, the compressed entry is:

 CiComp = j=mim(i+1)1SjaCja + j=m(i1)mi1SjbCjb. \boxed{\ C_i^{\text{Comp}} \ = \ \sum_{j=mi}^{m(i+1)-1} S^a_j \odot C^a_j \ + \ \sum_{j=m(i-1)}^{mi-1} S^b_j \odot C^b_j.\ }

Each term is a cc-dimensional vector; \odot denotes the Hadamard product (elementwise multiplication). The sum has exactly 2m2m terms — one per row of the softmax.

Computing C0CompC_0^{\text{Comp}}

Column 00:

C0Comp[0]=0.731C0a[0]+0.269C1a[0]+0C2b[0]+0C1b[0].C_0^{\text{Comp}}[0] = 0.731 \cdot C^a_0[0] + 0.269 \cdot C^a_1[0] + 0 \cdot C^b_{-2}[0] + 0 \cdot C^b_{-1}[0].

Substituting C0a[0]=1C^a_0[0] = 1, C1a[0]=0C^a_1[0] = 0, and the padded-zero CbC^b entries:

C0Comp[0]=0.7311+0.2690+0+0=0.731.C_0^{\text{Comp}}[0] = 0.731 \cdot 1 + 0.269 \cdot 0 + 0 + 0 = 0.731.

Column 11:

C0Comp[1]=0.5C0a[1]+0.5C1a[1]+0+0=0.50+0.52=1.0.C_0^{\text{Comp}}[1] = 0.5 \cdot C^a_0[1] + 0.5 \cdot C^a_1[1] + 0 + 0 = 0.5 \cdot 0 + 0.5 \cdot 2 = 1.0.

So

C0Comp[0.731, 1.000]R2.C_0^{\text{Comp}} \approx [0.731,\ 1.000] \in \mathbb{R}^2.

Sanity check — range: every entry is a convex combination (weights are non-negative and sum to one) of values in {0,1,2}\{0, 1, 2\}, so C0CompC_0^{\text{Comp}} must lie in [0,2]2[0, 2]^2. Both components 0.7310.731 and 1.0001.000 fall in [0,2][0, 2]. \checkmark

Computing C1CompC_1^{\text{Comp}}

Column 00:

C1Comp[0]=0.400C2a[0]+0.147C3a[0]+0.054C0b[0]+0.400C1b[0].C_1^{\text{Comp}}[0] = 0.400 \cdot C^a_2[0] + 0.147 \cdot C^a_3[0] + 0.054 \cdot C^b_0[0] + 0.400 \cdot C^b_1[0].

Substituting C2a[0]=2C^a_2[0] = 2, C3a[0]=1C^a_3[0] = 1, C0b[0]=0C^b_0[0] = 0, C1b[0]=2C^b_1[0] = 2:

C1Comp[0]=0.4002+0.1471+0.0540+0.4002=0.800+0.147+0+0.800=1.747.C_1^{\text{Comp}}[0] = 0.400 \cdot 2 + 0.147 \cdot 1 + 0.054 \cdot 0 + 0.400 \cdot 2 = 0.800 + 0.147 + 0 + 0.800 = 1.747.

Column 11:

C1Comp[1]=0.25C2a[1]+0.25C3a[1]+0.25C0b[1]+0.25C1b[1].C_1^{\text{Comp}}[1] = 0.25 \cdot C^a_2[1] + 0.25 \cdot C^a_3[1] + 0.25 \cdot C^b_0[1] + 0.25 \cdot C^b_1[1].

Substituting 1,1,1,01, 1, 1, 0:

C1Comp[1]=0.251+0.251+0.251+0.250=0.75.C_1^{\text{Comp}}[1] = 0.25 \cdot 1 + 0.25 \cdot 1 + 0.25 \cdot 1 + 0.25 \cdot 0 = 0.75.

So

C1Comp[1.747, 0.750]R2.C_1^{\text{Comp}} \approx [1.747,\ 0.750] \in \mathbb{R}^2.

Note the overlap concretely: C0b=[0,1]C^b_0 = [0, 1] (drawn from token h0h_0) and C1b=[2,0]C^b_1 = [2, 0] (drawn from token h1h_1) both appear in C1CompC_1^{\text{Comp}} through the bb-stream, even though tokens 00 and 11 are “in” block 00. The block boundary is intentionally blurred.

Interpretation. The six-token sequence has been compressed from a 6×26 \times 2 tensor of raw KV entries into a 3×23 \times 2 tensor of compressed entries (we showed the first two; C2CompC_2^{\text{Comp}} is analogous). The compression ratio is m=2m = 2, exactly as advertised.


6. CSA Step 4 — The Lightning Indexer

Compression alone cuts the KV cache by mm. Sparse selection cuts the per-query read by another factor. CSA’s lightning indexer is the mechanism that scores which compressed entries the query should actually attend to.

For query token tt, the indexer performs four operations.

6.1 Produce a compressed latent query

ctQ=htWDQ,WDQRd×dc.\mathbf{c}_t^Q = \mathbf{h}_t \cdot W^{DQ}, \qquad W^{DQ} \in \mathbb{R}^{d \times d_c}.

With our d=2d = 2, dc=2d_c = 2, pick WDQ=IW^{DQ} = I (identity). Then c5Q=h5=[1,0]\mathbf{c}_5^Q = \mathbf{h}_5 = [1, 0].

6.2 Up-project to indexer query heads

[qt,1I; ; qt,nhII]=ctQWIUQ,WIUQRdc×cInhI.[\mathbf{q}^I_{t,1};\ \ldots;\ \mathbf{q}^I_{t,n_h^I}] = \mathbf{c}_t^Q \cdot W^{IUQ}, \qquad W^{IUQ} \in \mathbb{R}^{d_c \times c^I n_h^I}.

With nhI=1n_h^I = 1 and cI=2c^I = 2, pick WIUQ=IW^{IUQ} = I. Then q5,1I=[1,0]\mathbf{q}^I_{5,1} = [1, 0].

6.3 Produce per-head indexer weights

[wt,1I; ; wt,nhII]=htWw,WwRd×nhI.[w^I_{t,1};\ \ldots;\ w^I_{t,n_h^I}] = \mathbf{h}_t \cdot W^w, \qquad W^w \in \mathbb{R}^{d \times n_h^I}.

With nhI=1n_h^I = 1 and Ww=[1,1]TW^w = [1, 1]^T, we get w5,1I=h5[1,1]T=11+01=1w^I_{5,1} = \mathbf{h}_5 \cdot [1, 1]^T = 1 \cdot 1 + 0 \cdot 1 = 1.

6.4 Score each compressed block

Given compressed indexer keys KsICompRcIK^{\text{IComp}}_s \in \mathbb{R}^{c^I} (produced by the same compression operation as CCompC^{\text{Comp}} but with a separate set of weight matrices — we assume they are given for this section and return to their construction below), the index score is:

 It,s=h=1nhIwt,hIReLU ⁣(qt,hIKsIComp). \boxed{\ I_{t,s} = \sum_{h=1}^{n_h^I} w^I_{t,h} \cdot \text{ReLU}\!\left(\mathbf{q}^I_{t,h} \cdot K^{\text{IComp}}_s\right).\ }

ReLU (Rectified Linear Unit) is the activation ReLU(x)=max(x,0)\text{ReLU}(x) = \max(x, 0). It clamps the per-head contribution to be non-negative — a block that is “negatively” scored by one head does not drag down the total.

Why ReLU and not softmax?

This is the part that confuses almost everyone. In standard attention the scores are softmax-normalized, because we want a probability distribution over keys. Here we want a ranking — the top-kk largest scores — and a ranking is invariant to monotone transforms. ReLU is cheaper than softmax, does not require cross-block normalization (each It,sI_{t,s} is computed independently), and keeps the indexer’s output bounded below.

Concretely, the ReLU lets us implement the whole indexer in FP4 without exploding logits — the paper notes that “attention computation within the lightning indexer is performed in FP4 precision,” which is only viable because we never exponentiate. This is the straight-through estimator strategy (Jacob et al., 2018) applied to attention scoring.

Numerical check

Suppose the compressed indexer keys come out to:

K0IComp=[0.5, 0.5],K1IComp=[1.0, 1.0].K^{\text{IComp}}_0 = [0.5,\ 0.5], \qquad K^{\text{IComp}}_1 = [1.0,\ 1.0].

Then:

q5,1IK0IComp=10.5+00.5=0.5,\mathbf{q}^I_{5,1} \cdot K^{\text{IComp}}_0 = 1 \cdot 0.5 + 0 \cdot 0.5 = 0.5, q5,1IK1IComp=11.0+01.0=1.0.\mathbf{q}^I_{5,1} \cdot K^{\text{IComp}}_1 = 1 \cdot 1.0 + 0 \cdot 1.0 = 1.0.

Both are positive, so ReLU leaves them unchanged:

I5,0=1ReLU(0.5)=0.5,I5,1=1ReLU(1.0)=1.0.I_{5,0} = 1 \cdot \text{ReLU}(0.5) = 0.5, \qquad I_{5,1} = 1 \cdot \text{ReLU}(1.0) = 1.0.

I5,1>I5,0I_{5,1} > I_{5,0} — the indexer says block 11 is more relevant to the query than block 00.

Cost. Computing It,sI_{t,s} for one query and one compressed block is one dot product in RcI\mathbb{R}^{c^I} plus one ReLU plus one multiply-add per indexer head — O(cInhI)O(c^I n_h^I) FLOPs. For n/mn/m blocks and nn queries, total indexer FLOPs are O(nnmcInhI)O(n \cdot \tfrac{n}{m} \cdot c^I n_h^I). This is still quadratic in nn, but with a much smaller constant (FP4, tiny cIc^I) than full attention — the V4 paper measures this as negligible relative to the core attention.


7. CSA Step 5 — Top-kk Sparse Selection

Given the index scores It,:I_{t,:} across all allowed compressed blocks, we keep only the top kk:

CtSprsComp={ CsComp  It,sTop-k(It,:) }.C_t^{\text{SprsComp}} = \left\{\ C_s^{\text{Comp}}\ \Big|\ I_{t,s} \in \text{Top-}k(I_{t,:})\ \right\}.

“Allowed” here means the causal condition s<t/ms < \lfloor t/m \rfloor: the query at position tt can only see compressed blocks whose rightmost token precedes tt. For our query t=5t = 5 and m=2m = 2, 5/2=2\lfloor 5/2 \rfloor = 2, so s{0,1}s \in \{0, 1\}.

The operator \lfloor \cdot \rfloor is the floor function — round down to the nearest integer.

With k=1k = 1, we pick the block with the highest score:

C5SprsComp={C1Comp}={[1.747, 0.750]}.C_5^{\text{SprsComp}} = \{\, C_1^{\text{Comp}} \,\} = \{\,[1.747,\ 0.750]\,\}.

The query at t=5t = 5 will now perform its core attention against a set of size k=1k = 1 instead of the n=6n = 6 raw KV entries. This is where CSA’s O(nk)O(nk) cost comes from — see the DSA from scratch post for the full FLOPs derivation.


8. CSA Step 6 — Shared-KV Multi-Query Attention

The final stage is Multi-Query Attention (MQA) (Shazeer, 2019): all query heads share a single key and value stream, which here is CtSprsCompC_t^{\text{SprsComp}}.

Produce the core attention queries from the same latent ctQ\mathbf{c}_t^Q we already computed for the indexer:

[qt,1; ; qt,nh]=ctQWUQ,WUQRdc×cnh.[\mathbf{q}_{t,1};\ \ldots;\ \mathbf{q}_{t,n_h}] = \mathbf{c}_t^Q \cdot W^{UQ}, \qquad W^{UQ} \in \mathbb{R}^{d_c \times c n_h}.

Sharing ctQ\mathbf{c}_t^Q between the indexer and the core attention is an explicit optimization — the paper calls it out in Section 2.3.1 — because it halves the query-side projection cost.

For our nh=1n_h = 1, dc=2d_c = 2, c=2c = 2, pick WUQ=IW^{UQ} = I. Then q5,1=[1,0]\mathbf{q}_{5,1} = [1, 0].

Now perform core attention:

ot,i=CoreAttn ⁣(query=qt,i, key=CtSprsComp, value=CtSprsComp).\mathbf{o}_{t,i} = \text{CoreAttn}\!\left(\text{query}=\mathbf{q}_{t,i},\ \text{key}=C_t^{\text{SprsComp}},\ \text{value}=C_t^{\text{SprsComp}}\right).

With k=1k = 1, the softmax over one key is trivial (a softmax of a single-element set is [1][1]), so:

o5,1=1.000C1Comp=[1.747, 0.750].\mathbf{o}_{5,1} = 1.000 \cdot C_1^{\text{Comp}} = [1.747,\ 0.750].

Numerical check: with a one-element attention, the output must equal the single value. It does. \checkmark

With larger kk the softmax is:

ot,i=sTop-kSoftmaxs ⁣(qt,iCsCompc)CsComp.\mathbf{o}_{t,i} = \sum_{s \in \text{Top-}k} \text{Softmax}_s\!\left(\frac{\mathbf{q}_{t,i} \cdot C_s^{\text{Comp}}}{\sqrt{c}}\right) \cdot C_s^{\text{Comp}}.

The c\sqrt{c} is the scaled dot-product attention scaling (Vaswani et al., 2017) that keeps logit variance constant as cc grows.

Key/Value sharing. Notice the key and value in the attention call are the same tensor CSprsCompC^{\text{SprsComp}}. This is MQA’s defining property: the compressed entry serves as both the key (for scoring) and the value (for the weighted sum). The KV cache stores one cc-dim vector per compressed block — not two, not nhn_h copies — so the ledger is as small as it could be.


9. Bringing CSA Together — The Compression Architecture

Here is CSA at one glance:

Hidden states H n × d C^a, Z^a C^b, Z^b Softmax over 2m + weighted sum C^Comp (n/m) × c Query h_t 1 × d c_t^Q 1 × d_c Indexer queries q^I (low-rank) Core queries q (shared c^Q) Index scores I ReLU · w^I Top-k selector k ≪ n/m entries Shared-KV MQA core attention output o_t ∈ R^c, then grouped output projection

10. HCA — The Heavily Compressed Extreme

HCA is CSA with three things removed:

  1. One KV stream instead of two. No overlap.
  2. Larger compression ratio mmm' \gg m. The paper uses m=128m' = 128 against m=4m = 4.
  3. No lightning indexer, no top-kk. Every query attends to all compressed blocks densely.

Formally, given input hidden states HRn×dH \in \mathbb{R}^{n \times d}:

C=HWKV,Z=HWZ,WKV,WZRd×c.C = H \cdot W^{KV}, \qquad Z = H \cdot W^Z, \qquad W^{KV}, W^Z \in \mathbb{R}^{d \times c}.

Group into non-overlapping blocks of width mm':

Smi:m(i+1)1=Softmaxrow(Zmi:m(i+1)1+B),S_{m'i:m'(i+1)-1} = \text{Softmax}_{\text{row}}(Z_{m'i:m'(i+1)-1} + B),  CiComp=j=mim(i+1)1SjCj. \boxed{\ C_i^{\text{Comp}} = \sum_{j=m'i}^{m'(i+1)-1} S_j \odot C_j.\ }

This is exactly the compression sub-operation of CSA, with the bb-stream deleted and mm replaced by mm'.

HCA on our running example

Use m=3m' = 3, so n/m=6/3=2n / m' = 6/3 = 2 HCA compressed entries. Reuse WKV=WaKV=IW^{KV} = W^{aKV} = I and WZ=WaZW^Z = W^{aZ}, so:

C=H,Z=[100020100010].C = H, \qquad Z = \begin{bmatrix} 1 & 0 \\ 0 & 0 \\ 2 & 0 \\ 1 & 0 \\ 0 & 0 \\ 1 & 0 \end{bmatrix}.

Block i=0i = 0 (positions 0,1,20, 1, 2)

Column 00: Z0:2,0=[1,0,2]Z_{0:2,0} = [1, 0, 2]. Softmax denominator: e1+e0+e2=2.718+1+7.389=11.107e^1 + e^0 + e^2 = 2.718 + 1 + 7.389 = 11.107.

[S0,0,S1,0,S2,0]=[2.71811.107, 111.107, 7.38911.107][0.245, 0.090, 0.665].[S_{0,0}, S_{1,0}, S_{2,0}] = \left[\tfrac{2.718}{11.107},\ \tfrac{1}{11.107},\ \tfrac{7.389}{11.107}\right] \approx [0.245,\ 0.090,\ 0.665].

Sum check: 0.245+0.090+0.665=1.0000.245 + 0.090 + 0.665 = 1.000. \checkmark

Column 11: Z0:2,1=[0,0,0]Z_{0:2,1} = [0, 0, 0], so softmax =[1/3,1/3,1/3]= [1/3, 1/3, 1/3].

Weighted sum, column 00:

C0HCA[0]=0.2451+0.0900+0.6652=0.245+0+1.330=1.575.C_0^{\text{HCA}}[0] = 0.245 \cdot 1 + 0.090 \cdot 0 + 0.665 \cdot 2 = 0.245 + 0 + 1.330 = 1.575.

Weighted sum, column 11:

C0HCA[1]=130+132+131=1.000.C_0^{\text{HCA}}[1] = \tfrac{1}{3} \cdot 0 + \tfrac{1}{3} \cdot 2 + \tfrac{1}{3} \cdot 1 = 1.000.

So C0HCA[1.575, 1.000]C_0^{\text{HCA}} \approx [1.575,\ 1.000].

Block i=1i = 1 (positions 3,4,53, 4, 5)

Column 00: Z3:5,0=[1,0,1]Z_{3:5,0} = [1, 0, 1]. Denominator: e1+e0+e1=2.718+1+2.718=6.436e^1 + e^0 + e^1 = 2.718 + 1 + 2.718 = 6.436.

[S3,0,S4,0,S5,0][0.422, 0.155, 0.422].[S_{3,0}, S_{4,0}, S_{5,0}] \approx [0.422,\ 0.155,\ 0.422].

Column 11: all zeros [1/3,1/3,1/3]\to [1/3, 1/3, 1/3].

Weighted sum, column 00:

C1HCA[0]=0.4221+0.1550+0.4221=0.844.C_1^{\text{HCA}}[0] = 0.422 \cdot 1 + 0.155 \cdot 0 + 0.422 \cdot 1 = 0.844.

Weighted sum, column 11:

C1HCA[1]=131+131+130=0.667.C_1^{\text{HCA}}[1] = \tfrac{1}{3} \cdot 1 + \tfrac{1}{3} \cdot 1 + \tfrac{1}{3} \cdot 0 = 0.667.

So C1HCA[0.844, 0.667]C_1^{\text{HCA}} \approx [0.844,\ 0.667].

For query t=5t = 5 under HCA, the causal condition is s<t/m=5/3=1s < \lfloor t/m' \rfloor = \lfloor 5/3 \rfloor = 1, so only block 00 is visible. The core attention is a dense softmax over one block, which trivially outputs C0HCAC_0^{\text{HCA}}.

In a realistic 1M-context setting with m=128m' = 128 and n=106n = 10^6, HCA produces 106/128781310^6 / 128 \approx 7813 compressed entries per layer — already 128×128\times smaller than raw KV — and each query attends densely over all of them. There is no sparse selection, so there is no indexer cost, and no top-kk kernel.


11. Unified View — CSA and HCA on One Spectrum

CSA and HCA look like two different mechanisms, but they share a single equation. Define the general compressed attention operator parameterized by (m,k,overlap)(m, k, \text{overlap}):

 CompAttnm,k,overlap(H,t) = MQA ⁣(qt, Top-k ⁣(Indexer(t,Cm,overlapComp))). \boxed{\ \text{CompAttn}_{m,k,\text{overlap}}(H, t) \ = \ \text{MQA}\!\left(\mathbf{q}_t,\ \text{Top-}k\!\left(\text{Indexer}(t, C^{\text{Comp}}_{m,\text{overlap}})\right)\right).\ }

The three knobs:

KnobCSAHCA
Compression ratiomm (moderate)mmm' \gg m
Overlapyes (2m softmax)no (m softmax)
Top-kk selectionk<n/mk < n/mk=n/mk = n/m' (no-op)

HCA is the special case of the operator with the overlap turned off and the top-kk budget set to “everything”. CSA is the case with overlap on and a sparse budget. The indexer exists in both equations — it just becomes trivial (rank by score, keep all) in HCA, so the implementation omits it.

One framework, two specializations. The mathematical elegance lies in how a single compression-then-attend template, parameterized by two knobs, recovers both mechanisms as points on a continuous spectrum.


12. The Efficiency Ledger

Now the numbers that motivated everything.

12.1 KV cache per layer

Raw GQA8 baseline with head dimension dh=128d_h = 128, BF16: n81282=2048nn \cdot 8 \cdot 128 \cdot 2 = 2048n bytes.

CSA stores one compressed entry per mm tokens, each of size cc, in BF16 (2 bytes): nmc2\tfrac{n}{m} \cdot c \cdot 2 bytes.

HCA: nmc2\tfrac{n}{m'} \cdot c \cdot 2 bytes.

With V4-Pro values c=512c = 512, m=4m = 4, m=128m' = 128:

CSA cache per layer=n45122=256n bytes,\text{CSA cache per layer} = \tfrac{n}{4} \cdot 512 \cdot 2 = 256 n \text{ bytes}, HCA cache per layer=n1285122=8n bytes.\text{HCA cache per layer} = \tfrac{n}{128} \cdot 512 \cdot 2 = 8 n \text{ bytes}.

Numerical check against the baseline:

CSAGQA8=2562048=0.125=12.5%,\frac{\text{CSA}}{\text{GQA8}} = \frac{256}{2048} = 0.125 = 12.5\%, HCAGQA8=820480.004=0.4%.\frac{\text{HCA}}{\text{GQA8}} = \frac{8}{2048} \approx 0.004 = 0.4\%.

For V4-Pro with L=61L = 61 layers, roughly half CSA and half HCA (exact ratio depends on the interleaving schedule), the total becomes:

30256+318612048=7680+2481249286.3%.\frac{30 \cdot 256 + 31 \cdot 8}{61 \cdot 2048} = \frac{7680 + 248}{124928} \approx 6.3\%.

The paper’s measured number is 10%10\% at 1M1M context; the small gap reflects the sliding-window KV, sink logits, RoPE dimensions, and FP8 mixed precision, none of which we modeled here.

12.2 Per-query attention FLOPs

Baseline MHA over nn raw tokens: O(nnhdh)O(n \cdot n_h \cdot d_h) FLOPs per query — the quadratic wall.

CSA core attention: O(knhc)O(k \cdot n_h \cdot c) FLOPs per query. With k=1024n=106k = 1024 \ll n = 10^6 and c=512c = 512, this is independent of nn — the length-scaling moves entirely into the indexer and the compression step, both of which are cheaper than the original attention.

CSA indexer per query: O(nmnhIcI)O(\tfrac{n}{m} \cdot n_h^I \cdot c^I) FLOPs in FP4. With the paper’s nhI=64n_h^I = 64, cI=128c^I = 128, m=4m = 4, this is n464128=2048n\tfrac{n}{4} \cdot 64 \cdot 128 = 2048n — same asymptotic class as GQA MHA but in FP4 (which on current hardware is the same peak FLOPs as FP8, but theoretically 1/31/3 lower on future hardware per the paper’s Section 2.3.4).

Adding everything up, the paper reports single-token inference FLOPs at 1M1M context are 27%27\% of V3.2 for V4-Pro and 10%10\% for V4-Flash. These numbers are the end product of the ledger above.


13. Summary

DeepSeek-V4’s long-context efficiency reduces to one equation applied twice: compress mm consecutive tokens into one cc-dimensional entry via a learned per-coordinate softmax, and then either attend sparsely over the compressed stream (CSA, with an overlap-and-index twist) or densely over an even more compressed stream (HCA). Trading the n×dn \times d raw KV cache for an (n/m)×c(n/m) \times c or (n/m)×c(n/m') \times c compressed cache is what turns 10610^6-token contexts from a 250250 GB infeasibility into a 2525 GB routine.


Previous: Prefill-as-a-Service: How KVCache Goes Cross-Datacenter
Next: X-Token: Cross-Tokenizer Knowledge Distillation from Scratch

Enjoyed this post?

Subscribe to get notified when I publish new posts. No spam, unsubscribe anytime.