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% of the single-token FLOPs and 10% of the KV cache of DeepSeek-V3.2. V4-Flash pushes that to 10% of FLOPs and 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 m, then attend sparsely over the compressed entries using a learned index.
Heavily Compressed Attention (HCA) — compress the KV cache by a much larger factor m′≫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% 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=6 tokens with hidden size d=2. The hidden states are:
H=h0h1h2h3h4h5=102101021110∈R6×2.
The attention-specific parameters we fix for this post:
Compressed head dimensionc=2 — the width of one compressed KV entry.
CSA compression ratiom=2 — how many tokens go into one CSA compressed entry.
HCA compression ratiom′=3 — how many tokens go into one HCA compressed entry.
Sparse selection budgetk=1 — how many CSA compressed entries each query keeps.
Number of query headsnh=1 — kept to one for readability. Production V4-Pro uses nh=128.
Indexer head dimensioncI=2 and number of indexer headsnhI=1.
Query compression dimensiondc=2.
Production V4-Pro values are c=512, m=4, m′=128, k=1024, nh=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=5 — the last token in the sequence — because a causal model attends only to preceding tokens, and t=5 has the most preceding context (tokens 0,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 128, 8 KV groups, L layers, and BF16 (2 bytes per element):
KV cache bytes per token per layer=2⋅(nhKV⋅dh)⋅2=2⋅(8⋅128)⋅2=4096 bytes.
For a sequence of n=106 tokens and L=61 layers (V4-Pro), that is:
n⋅L⋅4096=106⋅61⋅4096≈2.5⋅1011 bytes=250 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 106 tokens. CSA and HCA attack both numbers at their root: they reduce the number of stored KV entries by a factor m or m′, and they reduce how many entries each query reads by the sparse selection k.
2. Strategy: Compress Groups of Tokens into One Entry
The common idea behind CSA and HCA is the compression operation: take a window of m (or m′) contiguous hidden states, project each into a c-dimensional KV candidate, generate per-position weights, and combine them into a single compressed entry.
Symbolically, if C1:m∈Rm×c is a block of m KV candidates and S1:m∈Rm×c is a matching block of weights (one weight vector per candidate), then the compressed entry is:
CComp=j=1∑mSj⊙Cj∈Rc,
where ⊙ 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 m positions, with a separate softmax per coordinate.
HCA stops here. CSA adds a twist — each compressed entry draws from 2m positions, with overlap between neighbours, so that block boundaries blur — and then performs sparse selection over the compressed entries.
WaKV is the identity (stream a preserves H); WbKV swaps coordinates (stream b is a reshuffled view). WaZ keeps only column 0 of H; WbZ keeps only column 1 of H and places it in column 0 of Zb. These choices are intentional — they let us check each derivation by eye.
Computing Ca=H⋅WaKV (using the matrix multiplication rule(HW)ij=∑kHikWkj):
Ca=102101021110.
Computing Cb=H⋅WbKV (swap columns):
Cb=021110102101.
Computing Za=H⋅WaZ (column 0 of H into column 0 of Za; column 1 zeroed):
Za=102101000000.
Computing Zb=H⋅WbZ (column 1 of H into column 0 of Zb; column 1 zeroed):
Zb=021110000000.
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 m elements. With two, it sees 2m — 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 2m-Softmax
For each compressed index i∈{0,1,…,n/m−1}, CSA constructs the i-th compressed entry from a window of 2m raw positions:
positions mi,mi+1,…,m(i+1)−1 from stream a (the “current block”),
positions m(i−1),m(i−1)+1,…,mi−1 from stream b (the “previous block”).
For i=0 the b-positions would be −m,…,−1, which do not exist. The paper pads: Z−m:−1b is set to −∞ and C−m:−1b to zero.
Adding learnable positional biases Ba,Bb∈Rm×c (we choose Ba=Bb=0 for readability), the softmax is:
where Softmaxrow denotes a softmax applied down the row dimension — for each of the c output columns independently, normalize across the 2m stacked rows. Each column therefore gets its own independent softmax distribution over 2m positions.
The softmax function itself is:
Softmax(x)j=∑k=12mexp(xk)exp(xj).
We use m=2, so 2m=4. Let us compute both CSA compressed entries that query t=5 is allowed to see.
Block i=0
a-positions: 0,1; b-positions: −2,−1 (padded with −∞ in Zb, zero in Cb).
Column 0 of the stacked input:
[Z0:1,0aZ−2:−1,0b]=[1,0,−∞,−∞].
(Here ∥ stacks vertically; we display horizontally to save space.) The exponential of −∞ is 0, so the denominator is e1+e0=2.718+1=3.718. The softmax is:
Column 1: all zeros, so softmax =[0.25,0.25,0.25,0.25].
Notice the overlap already: block i=1‘s softmax includes positions 0 and 1 through the b-stream, while block i=0 also used positions 0 and 1 through the a-stream. This is the “overlapped” in overlapped compression — information from positions 0,1 is available to both compressed entries.
5. CSA Step 3 — The Weighted Sum
Given the softmax weights, the compressed entry is:
Each term is a c-dimensional vector; ⊙ denotes the Hadamard product (elementwise multiplication). The sum has exactly 2m terms — one per row of the softmax.
Sanity check — range: every entry is a convex combination (weights are non-negative and sum to one) of values in {0,1,2}, so C0Comp must lie in [0,2]2. Both components 0.731 and 1.000 fall in [0,2]. ✓
Note the overlap concretely: C0b=[0,1] (drawn from token h0) and C1b=[2,0] (drawn from token h1) both appear in C1Comp through the b-stream, even though tokens 0 and 1 are “in” block 0. The block boundary is intentionally blurred.
Interpretation. The six-token sequence has been compressed from a 6×2 tensor of raw KV entries into a 3×2 tensor of compressed entries (we showed the first two; C2Comp is analogous). The compression ratio is m=2, exactly as advertised.
6. CSA Step 4 — The Lightning Indexer
Compression alone cuts the KV cache by m. 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 t, the indexer performs four operations.
6.1 Produce a compressed latent query
ctQ=ht⋅WDQ,WDQ∈Rd×dc.
With our d=2, dc=2, pick WDQ=I (identity). Then c5Q=h5=[1,0].
6.2 Up-project to indexer query heads
[qt,1I;…;qt,nhII]=ctQ⋅WIUQ,WIUQ∈Rdc×cInhI.
With nhI=1 and cI=2, pick WIUQ=I. Then q5,1I=[1,0].
6.3 Produce per-head indexer weights
[wt,1I;…;wt,nhII]=ht⋅Ww,Ww∈Rd×nhI.
With nhI=1 and Ww=[1,1]T, we get w5,1I=h5⋅[1,1]T=1⋅1+0⋅1=1.
6.4 Score each compressed block
Given compressed indexer keys KsIComp∈RcI (produced by the same compression operation as CComp 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=1∑nhIwt,hI⋅ReLU(qt,hI⋅KsIComp).
ReLU (Rectified Linear Unit) is the activation 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-k largest scores — and a ranking is invariant to monotone transforms. ReLU is cheaper than softmax, does not require cross-block normalization (each It,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.
I5,1>I5,0 — the indexer says block 1 is more relevant to the query than block 0.
Cost. Computing It,s for one query and one compressed block is one dot product in RcI plus one ReLU plus one multiply-add per indexer head — O(cInhI) FLOPs. For n/m blocks and n queries, total indexer FLOPs are O(n⋅mn⋅cInhI). This is still quadratic in n, but with a much smaller constant (FP4, tiny cI) than full attention — the V4 paper measures this as negligible relative to the core attention.
7. CSA Step 5 — Top-k Sparse Selection
Given the index scores It,: across all allowed compressed blocks, we keep only the top k:
CtSprsComp={CsCompIt,s∈Top-k(It,:)}.
“Allowed” here means the causal conditions<⌊t/m⌋: the query at position t can only see compressed blocks whose rightmost token precedes t. For our query t=5 and m=2, ⌊5/2⌋=2, so s∈{0,1}.
The operator ⌊⋅⌋ is the floor function — round down to the nearest integer.
With k=1, we pick the block with the highest score:
C5SprsComp={C1Comp}={[1.747,0.750]}.
The query at t=5 will now perform its core attention against a set of size k=1 instead of the n=6 raw KV entries. This is where CSA’s 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 CtSprsComp.
Produce the core attention queries from the same latent ctQ we already computed for the indexer:
[qt,1;…;qt,nh]=ctQ⋅WUQ,WUQ∈Rdc×cnh.
Sharing ctQ 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=1, dc=2, c=2, pick WUQ=I. Then q5,1=[1,0].
The c is the scaled dot-product attention scaling (Vaswani et al., 2017) that keeps logit variance constant as c grows.
Key/Value sharing. Notice the key and value in the attention call are the same tensor CSprsComp. 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 c-dim vector per compressed block — not two, not nh copies — so the ledger is as small as it could be.
9. Bringing CSA Together — The Compression Architecture
Here is CSA at one glance:
10. HCA — The Heavily Compressed Extreme
HCA is CSA with three things removed:
One KV stream instead of two. No overlap.
Larger compression ratio m′≫m. The paper uses m′=128 against m=4.
No lightning indexer, no top-k. Every query attends to all compressed blocks densely.
For query t=5 under HCA, the causal condition is s<⌊t/m′⌋=⌊5/3⌋=1, so only block 0 is visible. The core attention is a dense softmax over one block, which trivially outputs C0HCA.
In a realistic 1M-context setting with m′=128 and n=106, HCA produces 106/128≈7813 compressed entries per layer — already 128× 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-k 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):
HCA is the special case of the operator with the overlap turned off and the top-k 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=128, BF16: n⋅8⋅128⋅2=2048n bytes.
CSA stores one compressed entry per m tokens, each of size c, in BF16 (2 bytes): mn⋅c⋅2 bytes.
HCA: m′n⋅c⋅2 bytes.
With V4-Pro values c=512, m=4, m′=128:
CSA cache per layer=4n⋅512⋅2=256n bytes,HCA cache per layer=128n⋅512⋅2=8n bytes.
For V4-Pro with L=61 layers, roughly half CSA and half HCA (exact ratio depends on the interleaving schedule), the total becomes:
61⋅204830⋅256+31⋅8=1249287680+248≈6.3%.
The paper’s measured number is 10% at 1M 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 n raw tokens: O(n⋅nh⋅dh) FLOPs per query — the quadratic wall.
CSA core attention: O(k⋅nh⋅c) FLOPs per query. With k=1024≪n=106 and c=512, this is independent of n — 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(mn⋅nhI⋅cI) FLOPs in FP4. With the paper’s nhI=64, cI=128, m=4, this is 4n⋅64⋅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/3 lower on future hardware per the paper’s Section 2.3.4).
Adding everything up, the paper reports single-token inference FLOPs at 1M context are 27% of V3.2 for V4-Pro and 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 m consecutive tokens into one c-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×d raw KV cache for an (n/m)×c or (n/m′)×c compressed cache is what turns 106-token contexts from a 250 GB infeasibility into a 25 GB routine.