Chapter 12 · Section 12.5 · 65 min

Shrinking the KV cache, MQA, GQA, and MLA

FlashAttention removed the T×TT\times T matrices. It left alone the other memory cost of attention, the one that grows with every token a model generates. To produce token T+1T+1, every layer needs the keys and values of all TT earlier tokens, so a serving system keeps them in the KV cache (§6.5). For Llama 2 7B in bf16 that is half a megabyte per token. Run it on 32K tokens and one sequence would hold 16 GiB of cache, more than the model's own weights, all of which every decoding step must read from HBM.

And generation keeps getting longer. Reasoning models write thousands of tokens of chain of thought before they answer (§18.5), and reinforcement learning from verifiable rewards spends much of its time generating such rollouts (§18.4), so the cost of decoding, and with it the cache, sits on the critical path of training as well as serving.

So the question for this section is how to make the cache smaller without making the model worse. The answers form a family. Share one key and value head among all query heads (multi-query attention), share them among groups of query heads (grouped-query attention), or compress keys and values together into a small latent vector that every head decompresses for itself (multi-head latent attention). We derive each, implement each, and put them side by side on real models, with the quality each one costs.

The cache, recalled, and why it sets the speed of decoding

§6.5 counted the cache. With LL layers, nkvn_{kv} key-value heads of width dkd_k (take dv=dkd_v = d_k), a context of TT tokens, a batch of BB sequences, and bb bytes per number (§6.5 writes ss), it holds

MKV=2⋅L⋅nkv⋅dk⋅T⋅B⋅b bytes,M_{\text{KV}} = 2\cdot L\cdot n_{kv}\cdot d_k\cdot T\cdot B\cdot b \text{ bytes},

one key and one value vector per key-value head per layer for every token, and §6.5 showed that at long context and large batch it outgrows the weights. For ordinary multi-head attention nkv=hn_{kv} = h, the number of query heads, and h dk=dh\,d_k = d. Everything in this section changes one part of that product, the 2nkvdk2n_{kv}d_k numbers that each token leaves behind in each layer.

The cache is not only a capacity problem. It sets the speed of decoding. At each step a query head does 2dk2d_k FLOPs per cached token for its score and 2dk2d_k for its weighted value, 4dk4d_k in all, and the cache supplies 2dk2d_k numbers per token per key-value head. With hh query heads sharing nkvn_{kv} key-value heads, the arithmetic intensity of decode attention is

Idecode=4dk h2dk nkv b=2b⋅hnkvFLOPs per byte,I_{\text{decode}} = \frac{4d_k\,h}{2d_k\,n_{kv}\,b} = \frac{2}{b}\cdot\frac{h}{n_{kv}} \quad\text{FLOPs per byte},

which in bf16 is just h/nkvh/n_{kv}, the number of query heads that share each key-value head. For multi-head attention that is 1 FLOP per byte, more than a hundred times below an A100's ridge point of about 153 (§12.1). Decode attention is about as memory-bound as a computation can be, and its time per token is the size of the cache divided by the HBM bandwidth. Shrinking the cache speeds it up in direct proportion. Shazeer (2019) made this argument when he introduced the first fix, with the ratio of memory access to arithmetic for incremental multi-head attention growing like Θ(T/d+1/B)\Theta(T/d + 1/B) in our notation, the T/dT/d term coming from the cache.

Notice what is missing from that formula, the batch size. For the weight matrices, batching is the cure for low intensity, because one weight read from HBM serves every sequence in the batch, so the intensity of a linear layer is about BB FLOPs per byte (§11.2). The cache gets no such reuse. Each sequence attends to its own keys and values, so a batch of BB sequences reads BB times the bytes for BB times the FLOPs, and the intensity of decode attention stays at h/nkvh/n_{kv} however large the batch. Larger batches make the weights cheaper per token and leave attention exactly as memory-bound as before, which is why at long context attention, not the weights, ends up dominating decode time. The only levers on that intensity are architectural, how many query heads share each cached vector, which is what the rest of this section is about.

Python · cell 1

The spread is a factor of about 67 between GPT-3 and DeepSeek-V3, even though DeepSeek-V3 has almost four times as many parameters. GPT-3's 4.5 MiB per token means a single 128K-token sequence would need more than half a terabyte of cache. Nearly every large model released since 2023 uses one of the methods below, and the rest of the section explains them.

Warm-up

KV cache bytes for MHA, GQA, MQA, and MLA

Implement kv_cache_bytes(cfg, T, batch=1, bytes_per=2) for a config dictionary with keys "layers", "heads", "head_dim", and "attn", where "attn" is one of "mha", "gqa" (with a key "kv_heads"), "mqa", or "mla" (with keys "d_c" and "d_rope", the latent width and the width of the shared rotary key). Return the bytes to cache batch sequences of T tokens. MLA caches dc+drd_c + d_r numbers per token per layer, not two vectors.

Hidden tests, hints, and AI help, from $1.99 a month.

Multi-query attention, one key head for everyone

Shazeer's idea was the most drastic one available. Keep hh query heads, each with its own projection, but give all of them a single key head and a single value head,

Q(i)=XWQ(i)    (i=1,…,h),K=XWK,V=XWV,H(i)=softmax⁡ ⁣(Q(i)K⊤dk+M)V,\mathbf{Q}^{(i)} = \mathbf{X}\mathbf{W}_Q^{(i)} \;\;(i = 1, \dots, h), \qquad \mathbf{K} = \mathbf{X}\mathbf{W}_K, \qquad \mathbf{V} = \mathbf{X}\mathbf{W}_V, \qquad \mathbf{H}^{(i)} = \operatorname{softmax}\!\Big(\frac{\mathbf{Q}^{(i)}\mathbf{K}^\top}{\sqrt{d_k}} + \mathbf{M}\Big)\mathbf{V},

with WK,WV∈Rd×dk\mathbf{W}_K, \mathbf{W}_V \in \R^{d\times d_k} and the mask M\mathbf{M} of §4.4. In plain words, every head still asks its own question and gets its own pattern of weights, but all heads look up the same keys and read the same values. The cache shrinks by a factor of hh, and the decode intensity rises to hh FLOPs per byte, because one cached key now serves hh dot products.

What does it cost? Shazeer trained Transformer translation models on WMT14 English to German, widening the feed-forward layer of the multi-query model (from 4,096 to 5,440) so both had the same number of parameters. The table collects his numbers (Tables 1 and 2 of the paper, times in TPUv2 microseconds per output token, encoder plus decoder).

dev BLEUtest BLEU, beam 4training stepgreedy decoding per token
multi-head26.728.413.21.7 + 46
multi-query26.528.513.01.5 + 3.8

Decoding became about 12 times faster for the decoder, at a cost of 0.2 dev BLEU and no loss in test BLEU. On a billion-word language-modeling benchmark, perplexity went from 29.9 to 30.2. PaLM (Chowdhery et al. 2022) adopted multi-query attention for all its sizes, reporting a neutral effect on quality and training speed and a large saving at inference, and so did Falcon 7B.

The catch showed up later and at scale. Ainslie et al. (2023) found multi-query models less stable. T5-Large models trained with it from scratch had frequent loss spikes and diverged immediately when fine-tuned on long-input tasks. Its quality also trails multi-head attention, by a little on their tasks and by more on the hard benchmarks of DeepSeek's later ablation (below). A single key head is a real bottleneck. All hh heads must find what they need in the same dkd_k-dimensional keys.

Grouped-query attention, the middle ground

The obvious compromise is to have some key-value heads, but fewer than query heads. Split the hh query heads into nkvn_{kv} groups of g=h/nkvg = h/n_{kv} consecutive heads, and give each group one key-value head. Query head ii (counting from 0) uses key-value head

κ(i)=⌊ig⌋,H(i)=softmax⁡ ⁣(Q(i)(K(κ(i)))⊤dk+M)V(κ(i)).\kappa(i) = \Big\lfloor \frac{i}{g} \Big\rfloor, \qquad \mathbf{H}^{(i)} = \operatorname{softmax}\!\Big(\frac{\mathbf{Q}^{(i)}\big(\mathbf{K}^{(\kappa(i))}\big)^\top}{\sqrt{d_k}} + \mathbf{M}\Big)\mathbf{V}^{(\kappa(i))} .

This is grouped-query attention (GQA, Ainslie et al. 2023). With nkv=hn_{kv} = h it is multi-head attention, and with nkv=1n_{kv} = 1 it is multi-query attention, so it interpolates between the two. The cache shrinks by gg, and the decode intensity is gg FLOPs per byte in bf16. The mapping is contiguous (heads 0,…,g−10, \dots, g-1 share key-value head 0), the convention of Llama's repeat_kv and of PyTorch's enable_gqa, which both repeat each key-value head gg times in a row. A checkpoint trained with one mapping gives nonsense under another.

Ainslie et al. also showed how to get a GQA model without pretraining from scratch. Take a multi-head checkpoint, build each group's key and value projections by mean-pooling the projections of the heads in the group, and continue pretraining (uptraining) for a small fraction of the original steps, α=5%\alpha = 5\%. Mean-pooling worked better than keeping the first head of each group, which worked better than random initialization, in the order of how much information each preserves. On T5-XXL, with GQA applied to the decoder's self- and cross-attention, they reported these results (Table 1, average over summarization, translation, and question-answering tasks).

modelinference time per sample (s)average score
MHA, T5-Large0.3746.0
MHA, T5-XXL1.5147.2
MQA, T5-XXL0.2446.6
GQA-8, T5-XXL0.2847.1

GQA with 8 groups was almost as good as multi-head attention and almost as fast as multi-query attention. The authors chose 8 groups as "a favorable middle ground", and the number stuck. Llama 2 70B, every Llama 3 model, Mistral 7B, and many others use 8 key-value heads, whatever their number of query heads.

Implementing it without copying

The simplest implementation repeats each key-value head gg times and runs ordinary multi-head attention. That works, and wastes the memory and bandwidth the method was meant to save. The better way is a reshape. View the queries of shape (B, h, T, d_k) as (B, n_kv, g, T, d_k) and the keys as (B, n_kv, 1, T, d_k), and let broadcasting pair each group of queries with its own key-value head.

Python · cell 2

Notice what does and does not change. The cache and the key and value projections shrink by gg. The attention core does not, since every query head still computes TT scores against TT keys. GQA saves memory and bandwidth, not FLOPs, which is exactly what decoding needs. Decode kernels use the same reshape on the GPU. At one new token per sequence, the gg query heads of a group become gg rows of a single query tile against one key tile, so each key loaded from HBM serves gg dot products, and a small matrix product on tensor cores replaces gg matrix-vector products. That is where the intensity h/nkvh/n_{kv} of the previous section actually comes from in a kernel.

Sharing meets tensor parallelism

Tensor parallelism splits a layer's heads across GPUs (§10.4), and the key-value heads have to split with them. With tt GPUs, each holds h/th/t query heads and, when tt divides nkvn_{kv}, nkv/tn_{kv}/t key-value heads, so its share of the cache shrinks by tt as well. When tt exceeds nkvn_{kv} there are not enough key-value heads to go around. Each must be stored on t/nkvt/n_{kv} GPUs, and the cache per GPU stops shrinking at one key-value head. Llama 2 70B's 8 key-value heads split evenly over the 8 GPUs of a typical node, while a 16-way split would store every one of them twice. Multi-query attention is the extreme case, its single head copied to every GPU. MLA's latent, below, is shared by all heads in the same way, which is why serving systems such as SGLang offer data-parallel attention for MLA models, giving each GPU different sequences rather than different heads.

Interview

Grouped-query attention with a key-value head mapping

Implement gqa(Q, K, V, causal=False) with Q of shape (B, h, T_q, d_k) and K, V of shape (B, n_kv, T_k, d_k), where hh is a multiple of nkvn_{kv} and query head ii uses key-value head ⌊i/(h/nkv)⌋\lfloor i/(h/n_{kv})\rfloor. With causal=True, use the bottom-right alignment of §4.4, so query tt (counting from 0) may see keys up to t+Tk−Tqt + T_k - T_q, which is what decoding with a cache needs. Use no Python loops over heads or positions, and do not materialize repeated copies of K\mathbf{K} or V\mathbf{V}.

Hidden tests, hints, and AI help, from $1.99 a month.
Core

Convert a multi-head checkpoint to grouped-query attention

Implement mha_to_gqa(W_K, W_V, h, n_kv). The inputs are multi-head key and value projections of shape (d, h * d_k), where columns i * d_k:(i + 1) * d_k belong to head ii. Return projections of shape (d, n_kv * d_k) whose key-value head jj is the mean of the g=h/nkvg = h/n_{kv} consecutive heads jg,…,(j+1)g−1jg, \dots, (j+1)g - 1, as in the uptraining recipe of Ainslie et al. (2023).

Hidden tests, hints, and AI help, from $1.99 a month.

Multi-head latent attention, compress instead of share

Sharing throws information away. Every head in a group must use the same keys. DeepSeek-V2 (DeepSeek-AI 2024) asked a different question. Is there a small vector per token from which every head's key and value can be reconstructed? If the keys and values of all heads together have low rank, the answer is yes, and that vector is all the cache needs to hold.

Low-rank joint compression

For token tt with hidden state ht∈Rd\mathbf{h}_t\in\R^d, multi-head latent attention (MLA) computes a latent vector and decompresses it into per-head keys and values,

ct=WDKVht∈Rdc,ktC=WUKct∈Rhdk,vtC=WUVct∈Rhdk,\mathbf{c}_t = \mathbf{W}^{DKV}\mathbf{h}_t \in \R^{d_c}, \qquad \mathbf{k}^C_t = \mathbf{W}^{UK}\mathbf{c}_t \in \R^{hd_k}, \qquad \mathbf{v}^C_t = \mathbf{W}^{UV}\mathbf{c}_t \in \R^{hd_k},

with the down-projection WDKV∈Rdc×d\mathbf{W}^{DKV}\in\R^{d_c\times d}, up-projections WUK,WUV∈Rhdk×dc\mathbf{W}^{UK}, \mathbf{W}^{UV}\in\R^{hd_k\times d_c}, and dc≪hdkd_c \ll hd_k. (DeepSeek writes vectors as columns, so their matrices act from the left. We follow them in this subsection.) Only ct\mathbf{c}_t is cached. Queries are compressed the same way, ctQ=WDQht\mathbf{c}^Q_t = \mathbf{W}^{DQ}\mathbf{h}_t and qtC=WUQctQ\mathbf{q}^C_t = \mathbf{W}^{UQ}\mathbf{c}^Q_t, which saves activation memory in training but does nothing for the cache. DeepSeek-V2 uses h=128h = 128 heads of width dk=128d_k = 128, so hdk=16,384hd_k = 16{,}384, and compresses keys and values into dc=512d_c = 512 numbers, with dc′=1,536d_c' = 1{,}536 for queries.

Decompressing ksC\mathbf{k}^C_s for every cached token at every step would cost 2hdkdc2hd_kd_c FLOPs per cached token per step, far more than the 4hdk4hd_k of the attention itself. The trick that makes MLA practical is that you never have to. Look at head ii's score between the query at tt and the key at ss. Write WiUK∈Rdk×dc\mathbf{W}^{UK}_i \in \R^{d_k\times d_c} for the rows of WUK\mathbf{W}^{UK} that produce head ii's key, so ks,iC=WiUKcs\mathbf{k}^C_{s,i} = \mathbf{W}^{UK}_i\mathbf{c}_s. Then

qt,iC⊤ks,iC=qt,iC⊤WiUKcs=(WiUK⊤qt,iC)⊤cs.\mathbf{q}_{t,i}^{C\top}\mathbf{k}^C_{s,i} = \mathbf{q}_{t,i}^{C\top}\mathbf{W}^{UK}_i\mathbf{c}_s = \big(\mathbf{W}^{UK\top}_i\mathbf{q}^C_{t,i}\big)^\top\mathbf{c}_s .

In plain words, instead of decompressing every cached latent into a key, move the decompression matrix onto the query. The query is computed once per step, the keys are many, so this is far cheaper, and the score is a dot product between a dcd_c-dimensional "absorbed" query and the cached latent itself. DeepSeek-V2 says WUK\mathbf{W}^{UK} "can be absorbed into" the query projection.

The values work the same way from the other side. Head ii's output is ∑sats,iWiUVcs=WiUV(∑sats,ics)\sum_s a_{ts,i}\mathbf{W}^{UV}_i\mathbf{c}_s = \mathbf{W}^{UV}_i\big(\sum_s a_{ts,i}\mathbf{c}_s\big), a weighted sum of latents, decompressed once, and since the output projection multiplies it next, WiUV\mathbf{W}^{UV}_i can be folded into WO\mathbf{W}_O. So at inference MLA is multi-query attention, with one shared "key" and one shared "value" per token, both equal to the latent cs\mathbf{c}_s of width 512 (the rotary part of the next subsection adds 64 more numbers to the key), and with per-head queries that live in the latent space. That is why its decode intensity is high. Every one of the 128 heads reads the same cached vector.

Why RoPE gets in the way, and the decoupled fix

Rotary embeddings (§5.5) rotate queries and keys by position-dependent matrices Rt\mathbf{R}_t and Rs\mathbf{R}_s. Apply them to the decompressed key and the score becomes

(Rtqt,iC)⊤(RsWiUKcs)=qt,iC⊤ Rs−t WiUK cs.(\mathbf{R}_t\mathbf{q}^C_{t,i})^\top(\mathbf{R}_s\mathbf{W}^{UK}_i\mathbf{c}_s) = \mathbf{q}^{C\top}_{t,i}\,\mathbf{R}_{s-t}\,\mathbf{W}^{UK}_i\,\mathbf{c}_s .

A matrix that depends on the pair of positions now sits between the query and WiUK\mathbf{W}^{UK}_i. The absorbed query WiUK⊤q\mathbf{W}^{UK\top}_i\mathbf{q} would have to be recomputed for every cached position, because matrix products do not commute and Rs−t\mathbf{R}_{s-t} cannot be moved out of the way. The paper's verdict is that keys for all prefix tokens would have to be recomputed during inference, which defeats the purpose.

DeepSeek's fix is to decouple position from content. Each query gets a few extra dimensions qt,iR=RoPE⁡(WiQRctQ)∈Rdr\mathbf{q}^R_{t,i} = \operatorname{RoPE}(\mathbf{W}^{QR}_i\mathbf{c}^Q_t)\in\R^{d_r} that carry RoPE, and each token gets one extra rotary key ktR=RoPE⁡(WKRht)∈Rdr\mathbf{k}^R_t = \operatorname{RoPE}(\mathbf{W}^{KR}\mathbf{h}_t)\in\R^{d_r} shared by all heads, MQA-style. The full query and key of head ii are concatenations, and the score is a sum of a content part and a position part,

qt,i=[qt,iC; qt,iR],ks,i=[ks,iC; ksR],sts,i=qt,iC⊤ks,iC+qt,iR⊤ksRdk+dr.\mathbf{q}_{t,i} = \big[\mathbf{q}^C_{t,i};\, \mathbf{q}^R_{t,i}\big], \quad \mathbf{k}_{s,i} = \big[\mathbf{k}^C_{s,i};\, \mathbf{k}^R_s\big], \quad s_{ts,i} = \frac{\mathbf{q}^{C\top}_{t,i}\mathbf{k}^C_{s,i} + \mathbf{q}^{R\top}_{t,i}\mathbf{k}^R_s}{\sqrt{d_k + d_r}} .

The content part has no rotation, so it can be absorbed. The rotary part is a small ordinary dot product with a cached, already rotated key. The cache per token per layer is ct\mathbf{c}_t and ktR\mathbf{k}^R_t, dc+drd_c + d_r numbers. DeepSeek-V2 uses dr=64=dk/2d_r = 64 = d_k/2 and dc=512=4dkd_c = 512 = 4d_k, so the cache is 92dk=576\tfrac{9}{2}d_k = 576 numbers per token per layer, which the paper notes is the same as GQA with only 2.25 groups.

The cell checks the whole construction. It builds a small MLA layer, computes one decoding step the explicit way (decompress every cached key and value) and the absorbed way (attend in the latent space), and compares.

Python · cell 3

The two agree to machine precision. In a real model the absorbed matrices are precomputed once, and the absorbed path never forms a per-head key at all.

Research

One MLA decoding step in the latent space

The setup defines a small MLA layer (weights in the dictionary W, rows-as-tokens convention), the rope function, and mla_reference(x, pos, cache_c, cache_kr), which decompresses every cached key and value. Implement mla_step(x, pos, cache_c, cache_kr) that returns the same output vector of shape (d,) without decompressing the cache. Absorb W["UK"] into the query and W["UV"] into W["O"], and attend directly over cache_c of shape (T, d_c) and the shared rotary keys cache_kr of shape (T, d_r). Your function must not multiply anything of length TT by W["UK"] or W["UV"] (the tests run it on a cache of 100,000 tokens and measure its peak memory).

Hidden tests, hints, and AI help, from $1.99 a month.

How good is it?

DeepSeek-V2 reports two ablations that, together, are the best public evidence on this trade-off (Appendix D). With dense 7B models trained on 1.33T tokens, and parameter counts matched by adjusting depth, multi-head attention beat GQA with 8 groups, which beat MQA, on every benchmark they list.

7B dense, 1.33T tokensMQAGQA-8MHA
BBH (3-shot)33.235.637.0
MMLU (5-shot)37.941.245.2
C-Eval (5-shot)30.037.742.9
CMMLU (5-shot)34.638.443.5

Then, comparing MLA with MHA in mixture-of-experts models (§13.5), MLA used 14% of MHA's cache in a 16B-parameter model (15.6K against 110.6K elements per token) and 4% in a 250B model, and scored higher on most benchmarks, for example MMLU 50.0 against 48.7 in the small model and 59.0 against 57.5 in the large one, though not all (C-Eval in the small model was 50.9 against 51.6). These are results from one lab's training setup, not a law. They do suggest that compressing keys and values loses less than sharing them.

The headline number needs care. DeepSeek-V2 reports a 93.3% smaller KV cache than DeepSeek 67B. That figure includes quantizing the cache to about 6 bits per element on average. By element count, MLA alone shrinks it by about 82%, as the cell computes from the two configurations.

Python · cell 4

The last lines show the other half of the story. Decode attention with GQA-8 runs at 4 FLOPs per byte, and with MLA at over 200, close to an H100's ridge point. MLA turns decoding from a pure bandwidth problem into something close to a balanced one, which is why it pairs so well with large batches. Its costs are complexity (absorbed weights, the decoupled rotary part, a kernel specialized for a 576-wide key and 512-wide value) and more FLOPs per cached token, which matters for prefill. For that reason serving implementations typically use the absorbed form only for decoding. In prefill, which is compute-bound anyway, they decompress the keys and values and run ordinary multi-head attention, with query-key width dk+dr=192d_k + d_r = 192 and value width 128 per head. DeepSeek-V3 and Kimi K2 kept MLA with the same dc=512d_c = 512 and dr=64d_r = 64.

Interview

How long one decoding step spends in attention

Implement decode_attention_time(cfg, T, batch, peak, bw, bytes_per=2) returning (seconds, bound), the roofline time max⁡(F/π,NIO/β)\max(F/\pi, N_{\text{IO}}/\beta) of the attention core in one decoding step over all layers, and "memory" or "compute" for whichever term is larger. Each of the batch sequences has one new query per query head and TT cached tokens. The config is a dictionary with "layers", "heads", "head_dim", and "attn", as in Exercise 12.5a ("mha", "gqa" with "kv_heads", "mqa", or "mla" with "d_c" and "d_rope"). Count only reading the cache (bytes) and the score and weighted-sum FLOPs. Per cached token per layer, a query head does 4dk4d_k FLOPs, except in absorbed MLA, where it does 2(dc+dr)+2dc2(d_c + d_r) + 2d_c. Ignore the weights and the projections.

Hidden tests, hints, and AI help, from $1.99 a month.

The trade-offs, side by side

methodnumbers cached per token per layerdecode intensity (bf16)quality, as reportedused by
MHA2hdk2hd_k1the baselineGPT-3, Llama 2 7B and 13B
GQA2nkvdk2n_{kv}d_kh/nkvh/n_{kv}close to MHA with 8 groups (T5-XXL), below it in DeepSeek's 7B ablationLlama 2 70B, Llama 3, Mistral, Qwen2.5, Gemma 2 and 3
MQA2dk2d_khhsmall drop on translation, larger on hard benchmarks, less stablePaLM, Falcon 7B
MLAdc+drd_c + d_rabout 1.9h1.9h, 242 for DeepSeek-V2at or above MHA in DeepSeek's MoE ablationDeepSeek-V2 and V3, Kimi K2

Two other levers multiply with these, and are covered elsewhere. Fewer tokens in the cache, by attending only to a window of recent tokens plus a few sinks, is the subject of §12.6. Fewer bits per number, storing the cache in 8 or 4 bits, and paging it into fixed-size blocks so that sequences of different lengths share memory without fragmentation (the PagedAttention of §12.4), are what serving systems built around paged attention and quantized KV caches do. Sharing keys and values across adjacent layers (cross-layer attention, Brandon et al. 2024) is a further option some models use.

How much KV cache does each attention variant need, for real model shapes?
Bytes cached per token for the chosen model's layers and heads, under each way of sharing or compressing keys and values. The highlighted row is what the model actually uses.
Loading interactive figure…

In PyTorch

A grouped-query attention layer is a few lines with enable_gqa=True, which passes the smaller key and value tensors straight to the kernel. The module below caches keys and values for decoding and reports how many bytes its cache holds.

🔥 In PyTorchfor a machine with PyTorch

Read more

Watch next

🎬 How DeepSeek Rewrote the Transformer [MLA] · Welch LabsThe clearest visual explanation of multi-head latent attention and why the KV cache matters.
🎬 How Attention Got So Efficient [GQA/MLA/DSA] · Jia-Bin Huang · starts at 7:07From the KV cache through MQA, GQA, MLA with decoupled RoPE, and DeepSeek Sparse Attention.
🎬 LLaMA explained: KV-Cache, Rotary Positional Embedding, RMS Norm, Grouped Query Attention, SwiGLU · Umar JamilA careful whiteboard treatment of the KV cache and grouped-query attention in LLaMA.