An Index — kept by Alek
Report A·02  ·  12 August 2026  ·  inference, memory, attention A technical index  ·  12 entries

KV cache.

The tensor that decides what your cluster can serve

Every token a model has already read is sitting in memory as two vectors per layer, per head, waiting to be read again on the next step and every step after it. That tensor is bigger than the model before you have served four long conversations, and reading it — not multiplying by it — is what sets your tokens per second. This page takes it apart: what is stored, why decoding is memory-bound by construction, and what each fix actually buys.

Applies to
Any decoder-only transformer at inference
Worst case shown
Llama-3.1-70B — KV outgrows weights at 3.3 sequences
Best case shown
DeepSeek-V3 MLA — 68.6 KiB/token, 71× under MHA
Hardware baseline
H100 SXM — 3.35 TB/s, 989 TFLOP/s bf16
Key ratio
Decode attention runs 37–295× below the roofline ridge
Claim under test
The cache is the deployment constraint, not the model
N° 01Premise

A cache you keep because attention looks backwards

Generation is a loop. The model has produced t1t-1 tokens and must produce the tt-th. Self-attention at that position needs a query from the current token and a key and value from every position up to it:

Attn(Q,K,V)=softmax ⁣(QKdh+M)V\mathrm{Attn}(Q,K,V)=\mathrm{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_h}}+M\right)V

with MM the causal mask. The naive loop recomputes KK and VV for the whole prefix at every step. That is pure waste, and the reason is worth stating precisely: the key and value at position ii are functions of the hidden state at position ii alone,

ki=WKhi,vi=WVhi,k_i = W_K\,h_i, \qquad v_i = W_V\,h_i,

and causality guarantees hih_i never changes once produced. Nothing to the right can influence it. So kik_i and viv_i are constants for the rest of the sequence, and recomputing them is recomputing a value you already have.

Cache them and the per-step cost collapses. Generating LL tokens goes from

t=1LΘ(t2)=Θ(L3)t=1LΘ(t)=Θ(L2)\sum_{t=1}^{L}\Theta(t^{2}) = \Theta(L^{3}) \quad\longrightarrow\quad \sum_{t=1}^{L}\Theta(t) = \Theta(L^{2})

in attention FLOPs, and the projections WKhW_K h, WVhW_V h go from Θ(L2)\Theta(L^2) to Θ(L)\Theta(L). This is not a marginal optimisation — without it, long-context generation is not merely slow, it is infeasible. Every production inference stack does it, which is why the interesting question is never whether to cache but what the cache then costs you.

The trade you just made

You converted a compute problem into a memory problem, and memory is the resource that scales worst. The cache grows linearly in sequence length and linearly in batch size, it is written once and read on every subsequent step, and it cannot be recomputed cheaply if you drop it. Everything in the remaining eleven sections is a consequence of those three facts.

N° 02What is stored

Two tensors per layer, and the arithmetic that follows

For one sequence the cache is a pair of tensors per layer, each of shape [nkv,L,dh][n_{kv},\,L,\,d_h] — number of key/value heads, positions so far, head dimension. Queries are not cached: the query for step tt is used once and discarded. Values are, because the weighted sum at every future step needs all of them.

ONE LAYER'S CACHE ONE DECODE STEP COST PER STEP K cache — [n_kv, L, d_h] HEAD 1 HEAD 2 HEAD n_kv ↑ NEWEST TOKEN V cache — [n_kv, L, d_h] GROWS ONE COLUMN PER TOKEN · NEVER REWRITTEN READ READ 1. project h_t → q_t, k_t, v_t THREE GEMVs · O(d²) EACH 2. append k_t, v_t to the cache ONE WRITE · d_h × n_kv × 2 VALUES 3. read the ENTIRE cache, attend L × n_kv × d_h × 2 VALUES — EVERY STEP THIS IS THE EXPENSIVE ONE 4. output projection → h_t′ reads scale with L the cache is re-read in full at every step, so decode gets slower as the conversation gets longer — linearly, per token writes stay constant one column per step regardless of L. the cache is append-only, which is what makes paging and prefix sharing possible at all
Figure A. The cache is append-only and read in full. That asymmetry — O(1)O(1) writes against O(L)O(L) reads — is the single most important structural fact about it, and every technique in §08 onwards exploits one side of it.

The size formula

Counting elements: two tensors, nln_l layers, nkvn_{kv} heads, dhd_h per head, bb bytes per element, BB sequences of length LL:

S  =  2K and V    nl    nkv    dh    b    L    BS \;=\; \underbrace{2}_{K \text{ and } V}\; \cdot\; n_l \;\cdot\; n_{kv} \;\cdot\; d_h \;\cdot\; b \;\cdot\; L \;\cdot\; B

Everything except LL and BB is fixed at architecture time. The per-token cost

s  =  2nlnkvdhb[bytes/token]s \;=\; 2\,n_l\,n_{kv}\,d_h\,b \qquad [\text{bytes/token}]

is therefore a property of the model you can read straight off its config, and it is the number worth memorising for any model you deploy. For Llama-3.1-70B — 80 layers, 8 KV heads, dh=128d_h=128, bf16:

s=2×80×8×128×2=327,680 bytes=320 KiBs = 2 \times 80 \times 8 \times 128 \times 2 = 327{,}680 \text{ bytes} = 320\ \mathrm{KiB}

Note what is absent. The number of attention heads nhn_h does not appear — only nkvn_{kv}. That gap is the entire subject of §05. Nor does the model's parameter count: a wide, shallow model and a narrow, deep one with the same nlnkvdhn_l \cdot n_{kv} \cdot d_h have identical cache costs and very different weights.

N° 03The budget

Where the memory actually goes

Weights are a fixed cost you pay once per GPU. The cache is a variable cost you pay per concurrent user per token, and it is the one that decides how many of them you can serve. Below, real configurations read from their published config.json.

Model Attn Layers KV heads d_h Bytes/token KiB/token Cache @128k Weights
Multi-head attention — one KV head per query head
GPT-2 XLMHA482564307,200300.037.5 GiB3.0 GB
Llama-2-7BMHA3232128524,288512.064.0 GiB13.4 GB
Grouped-query attention — query heads share a KV head
Llama-3.1-8BGQA-4328128131,072128.016.0 GiB16.0 GB
Mistral-7B-v0.3GQA-4328128131,072128.016.0 GiB14.4 GB
Llama-3.1-70BGQA-8808128327,680320.040.0 GiB141.2 GB
Qwen2.5-72BGQA-8808128327,680320.040.0 GiB145.4 GB
Multi-head latent attention — one compressed latent per layer
DeepSeek-V3MLA61576*70,27268.68.6 GiB1342 GB
  ↳ same geometry as MHAMHA611283204,997,1204880.0610.0 GiB

* MLA stores a single latent vector of 512 dims plus a 64-dim shared positional key per layer, not a per-head K and V — see §06. Weights are parameter count × 2 bytes. Cache @128k is one sequence at the model's full context window. Configurations read from each model's published config.json; GPT-2 XL's 128k figure is hypothetical, since its window is 1,024.

The crossover nobody budgets for

Llama-3.1-70B in bf16 is 141 GB of weights — already two H100s. Its cache costs 320 KiB per token, so:

3.3
Sequences of 128k
until KV = weights
40GiB
One 128k sequence
KV cache
1.3TiB
Batch of 32
at full context
16×
H100 80GB cards
for that batch alone

A batch of 32 users each holding a 128k context needs more HBM for their conversation history than sixteen H100s contain, before a single weight is loaded. This is why context length is sold in tiers and why long contexts cost more per token than short ones — you are not paying for compute, you are renting HBM.

Interactive

Work out your own budget

Every figure recomputes from the formula in §02. Presets are the real configurations from the table above.

Configuration

Memory

QuantityValueShare of HBM

Weights are estimated from the preset's published parameter count at the chosen precision and are held at bf16 regardless of the cache setting, since KV quantisation is independent of weight quantisation. Values use binary units: 1 GiB = 2³⁰ bytes.

N° 04Memory bound

Why decoding wastes almost all of your FLOPs

The cache is not only a capacity problem. It is a bandwidth problem, and this is the part that surprises people who size clusters by TFLOPs.

Take one decode step, one layer, batch BB, context LL. The attention over the cache reads

bytes  =  2BLnkvdhb\text{bytes} \;=\; 2\,B\,L\,n_{kv}\,d_h\,b

and performs a QKQK^\top and an AVAV, each 2BLnhdh2BLn_h d_h FLOPs:

FLOPs    4BLnhdh\text{FLOPs} \;\approx\; 4\,B\,L\,n_{h}\,d_h

The ratio is the arithmetic intensity — how much work you extract per byte moved. Note that BB, LL and dhd_h all cancel:

I  =  4BLnhdh2BLnkvdhb  =  2nhnkvbI \;=\; \frac{4BLn_h d_h}{2BLn_{kv}d_h\,b} \;=\; \frac{2\,n_h}{n_{kv}\,b}

This is a remarkable little result. The arithmetic intensity of decode attention is independent of context length and of batch size. You cannot batch your way out of it. It depends only on the query-to-KV head ratio and the bytes per element — which is to say, on two architectural decisions and a quantisation choice.

Figure B. Roofline for an H100 SXM: 3.35 TB/s HBM, 989 TFLOP/s dense bf16, giving a ridge point at 295 FLOP/byte. Decode attention lands two to three orders of magnitude to its left.
Configurationbf16fp8vs ridge
MHA (n_h/n_kv = 1)1.02.0295× under
GQA-44.08.074× under
GQA-88.016.037× under
MQA (n_kv = 1)32.064.09× under
H100 ridge point295

FLOP per byte of KV read. Even MQA in fp8 runs below the ridge, so decode attention is memory-bound on every configuration in current use. The practical reading: a faster GPU with the same bandwidth buys you nothing here.

The batch-1 floor

Weights make the point even more starkly. Every decode step must stream the whole model from HBM at least once. For 70B in bf16 that is 141 GB, so

tmin=141 GB3.35 TB/s=42 mst_{\min}=\frac{141\ \mathrm{GB}}{3.35\ \mathrm{TB/s}}=42\ \mathrm{ms}

— about 24 tokens/second, on hardware capable of 989 TFLOP/s, doing roughly 140 GFLOPs of useful work. Utilisation is around 0.3%. Batching is what recovers it, because the weight read amortises across the batch while each sequence contributes its own FLOPs — but the KV cache read does not amortise, which is why throughput stops improving once the cache dominates the byte traffic.

Prefill and decode are different machines

 PrefillDecode
Shape of workGEMM — LL tokens at onceGEMV — one token
Bound byComputeMemory bandwidth
Cache trafficWrite LL columnsRead all LL, write 1
Cost modelΘ(L2)\Theta(L^2) in attentionΘ(L)\Theta(L) per token
Scales with batchPoorly — already saturatedWell, until KV dominates
User metricTime to first tokenInter-token latency

Two workloads with opposite bottlenecks sharing one GPU is a scheduling problem, and a badly-behaved one: a long prefill blocks every decode behind it, producing the latency spikes users describe as stuttering. Chunked prefill interleaves them; disaggregation (§12) puts them on separate hardware entirely.

N° 05Head sharing

Fewer KV heads, held at the same quality

nkvn_{kv} appears linearly in both the size formula and the intensity formula, which makes it the highest-leverage number in the architecture. The obvious move is to shrink it — and the obvious risk is that you lose the representational diversity that made multi-head attention work.

MHA — 8 QUERY HEADS, 8 KV HEADS GQA — 8 QUERY HEADS, 2 KV HEADS MQA — 8 QUERY HEADS, 1 KV HEAD Q KV cache ∝ 8 full head diversity largest cache, lowest arithmetic intensity Q KV cache ∝ 2 groups of 4 share one KV the production default — near-MHA quality, 4–8× less Q KV cache ∝ 1 every head reads one KV smallest cache, measurable quality loss without retraining
Figure C. The three classical options. GQA interpolates between the two extremes and is what almost every open-weight model published since 2023 uses.

GQA partitions nhn_h query heads into gg groups sharing one KV head each, so nkv=gn_{kv}=g and the cache shrinks by nh/gn_h/g. The compression is exact and structural — there is no approximation in the attention itself, only fewer distinct keys and values to attend to. Crucially, it also raises arithmetic intensity by the same factor, so GQA buys capacity and speed:

SGQA=gnhSMHA,IGQA=nhgIMHAS_{\text{GQA}} = \frac{g}{n_h}\,S_{\text{MHA}}, \qquad I_{\text{GQA}} = \frac{n_h}{g}\,I_{\text{MHA}}

Models are not usually trained from scratch as GQA. The standard recipe is uptraining: take an MHA checkpoint, mean-pool the key and value projections within each group, then continue training on a small fraction — around 5% — of the original budget. The mean-pool matters; picking one head per group and discarding the rest is markedly worse.

Figure D. Cache size against context length, one sequence, bf16, for the Llama-3.1-70B geometry (80 layers, 64 query heads, dh=128d_h=128) under each scheme, with DeepSeek-V3's MLA for reference. Both axes logarithmic; the lines are parallel because every scheme is linear in LL — only the constant differs.
N° 06Latent attention

Caching a compressed latent instead of the keys

GQA removes heads. Multi-head Latent Attention removes the representation: it caches a single low-rank vector per token per layer and reconstructs every head's key and value from it on the fly. This is what lets DeepSeek-V3 — a 671B model — hold a smaller cache than an 8B Llama.

Project the hidden state down to a latent of dimension dcnhdhd_c \ll n_h d_h, and cache only that:

ctKV=WDKVhtRdc,ktC=WUKctKV,vt=WUVctKVc^{KV}_t = W^{DKV}\,h_t \in \mathbb{R}^{d_c}, \qquad k^{C}_t = W^{UK}c^{KV}_t, \qquad v_t = W^{UV}c^{KV}_t

The obvious objection is that you have traded memory for compute — you now decompress nhn_h keys and values on every step. The trick is that you never have to. Inside the score, the up-projection can be folded into the query projection ahead of time:

qtkiC  =  qtWUKciKV  =  (WUKqt)absorbed onceciKVq_t^{\top}k^{C}_i \;=\; q_t^{\top}W^{UK}c^{KV}_i \;=\; \underbrace{\left(W^{UK\top}q_t\right)}_{\text{absorbed once}}{}^{\top} c^{KV}_i

so attention scores are computed directly against the cached latents. WUKW^{UK} is a fixed weight matrix; it never needs to materialise a single key. The same absorption works on the value side into the output projection. This is the whole idea, and it is why MLA is not simply "compression with a decompression cost".

The numbers for DeepSeek-V3

61 layers, dc=512d_c = 512, plus a 64-dimensional shared positional key (§07). Per token:

s=(512+64)×61×2=70,272 Bs = (512 + 64)\times 61 \times 2 = 70{,}272\ \text{B}

Against the same head geometry as plain MHA — 128 heads, dk=192d_k = 192, dv=128d_v = 128:

sMHA=61×128×(192+128)×2=4,997,120 Bs_{\text{MHA}} = 61 \times 128 \times (192{+}128) \times 2 = 4{,}997{,}120\ \text{B}

a reduction of 71×, or 98.6%. The cache stops scaling with head count altogether — it scales with a rank you choose.

71×
Smaller than the
equivalent MHA
68.6KiB
Per token
671B parameters
4.7×
Smaller than
Llama-3.1-70B
8.6GiB
A full 128k
context

The comparison worth sitting with: DeepSeek-V3 has 9.5× the parameters of Llama-3.1-70B and a fifth of the cache. Cache cost and model capacity have been decoupled — which is exactly the move Engram makes for knowledge storage, one report over.

What it costs

  • Absorption inflates the query path. WUKqtW^{UK\top}q_t is computed per step per head; you have moved work out of memory and into compute, which is the correct direction on current hardware but is not free.
  • It is not a drop-in. GQA can be uptrained from an MHA checkpoint in ~5% of the original compute. MLA changes the attention algebra, and converting existing checkpoints to it is an active research problem rather than a recipe.
  • Kernels are scarcer. GQA is supported everywhere. MLA needs implementations that understand absorption, and a naive implementation that materialises keys throws away the entire benefit.
  • RoPE does not survive the compression — which needs its own section.
N° 07Position

Rotary embeddings bake position into the key

RoPE applies a position-dependent rotation to queries and keys before the dot product. With RmR_m the rotation for position mm,

Rmq,  Rnk  =  qRnmk\langle R_m q,\; R_n k \rangle \;=\; q^{\top}R_{n-m}^{\top}k

which is the property that makes it work: the score depends only on the relative offset nmn-m. But it also means the tensor you cache is RnknR_n k_n, not knk_nposition is already inside the stored value. Two consequences follow, and both are practical.

Cached prefixes are position-locked

A cached block of keys is only valid at the offset it was computed at. Reuse a system prompt at position 0 across requests and it is fine (§09); try to splice a cached document into the middle of a different prompt and the rotations are wrong. Doing it properly means either re-rotating — cheap, but you must have kept the unrotated keys — or accepting the mismatch, which degrades quality in ways that are hard to detect from a benchmark.

It breaks MLA's absorption

The absorption in §06 needs WUKW^{UK} to commute out of the dot product. Insert a position-dependent rotation between qq and kk and it no longer can: RR sits in the middle, and WUKRnmW^{UK\top}R_{n-m} differs for every pair of positions, so nothing can be precomputed.

Decoupled RoPE

DeepSeek's fix is to split the key in two. Most of it is compressed and carries no position; a small separate piece carries the rotation and is not compressed. Concatenate them and attend to both:

kt=[  WUKctKVcompressed, no RoPE  ;  RtWKRhtuncompressed, RoPE  ]k_t = \big[\; \underbrace{W^{UK}c^{KV}_t}_{\text{compressed, no RoPE}} \;;\; \underbrace{R_t\,W^{KR}h_t}_{\text{uncompressed, RoPE}} \;\big]

The compressed half keeps the absorption trick; the rotary half is small enough to cache directly, and — the detail that makes the accounting work — it is shared across all heads. So the per-token cost is dc+drd_c + d_r with dr=64d_r = 64, rather than dc+nhdrd_c + n_h d_r. Sixty-four dimensions buy back positional information for 128 heads at once.

This is the kind of design that only appears when the memory system is treated as a first-class constraint rather than an implementation detail. It costs 11% of the per-token cache and removes the one obstacle to the other 89%.

N° 08Paging

The allocator, not the algorithm

Everything above concerns how much cache a token needs. This section is about how much a naive serving system wastes, which through 2023 was most of it.

The obvious implementation reserves a contiguous buffer per sequence at its maximum possible length, because the cache grows and reallocation is expensive. That single decision produces three separate wastes:

  • Internal fragmentation. A request that could emit 2,048 tokens but stops at 200 holds a 2,048-token buffer for its lifetime. Most of the reservation is never touched.
  • Reservation waste. Slots allocated for tokens not yet generated are unusable by anyone else, even though the memory is provably idle right now.
  • External fragmentation. Variable-length contiguous buffers leave gaps too small for the next request, exactly like a filesystem.

Measured across real serving traces, systems built this way used roughly 20–40% of the KV memory they held. PagedAttention's contribution was to notice that this is a problem operating systems solved in the 1960s, and to apply the same answer: stop requiring contiguity.

CONTIGUOUS — RESERVE FOR THE WORST CASE PAGED — FIXED BLOCKS, INDIRECTED SEQUENCE A · 200 OF 2048 USED RESERVED, NEVER WRITTEN SEQUENCE B · WAITING — NO CONTIGUOUS GAP LARGE ENOUGH EFFECTIVE UTILISATION 20–40% the rest is held by requests that will never use it LOGICAL BLOCKS — SEQUENCE A 4 BLOCKS × 16 TOKENS BLOCK TABLE → PHYSICAL ANY ORDER · ANY GAP WASTE IS BOUNDED BY ONE PARTIAL BLOCK PER SEQUENCE < 4% and identical prefixes can point at the same physical block
Figure E. Paged allocation. Logical positions map through a per-sequence block table to physical blocks of fixed size (16 tokens is the common default), so the cache no longer needs to be contiguous and waste is capped at the last partial block.

The waste bound is worth writing down. With block size β\beta, each sequence wastes at most β1\beta - 1 slots in its final block, so for BB sequences of mean length Lˉ\bar L:

waste    β1Lˉ+β1   Lˉβ   0\text{waste} \;\le\; \frac{\beta-1}{\bar L + \beta - 1} \;\xrightarrow[\ \bar L \gg \beta\ ]{}\; 0

At β=16\beta=16 and Lˉ=512\bar L = 512 that is under 3%. The block size is a real trade-off rather than a free parameter: smaller blocks waste less but lengthen the block table and add indirection per attention step; larger blocks amortise the lookup but waste more and make prefix sharing coarser.

The second gain is the one that compounds. Because blocks are indirected, two sequences with a common prefix can point their block tables at the same physical blocks, with copy-on-write when they diverge. Parallel sampling, beam search and shared system prompts all stop duplicating memory. That is the bridge to §09.

N° 09Reuse

The cheapest cache is one you already filled

Prefill is Θ(L2)\Theta(L^2) and dominates time-to-first-token on long prompts. But real traffic repeats itself heavily: a fixed system prompt on every request, the same few-shot examples, the same document re-queried, an agent loop replaying its entire history each turn. If a prefix has been computed before, its keys and values are still valid — position is baked in (§07), but a prefix starts at position 0 by definition, so the rotations are correct.

Radix trees over the cache

Organise cached blocks as a radix tree keyed on token sequence. A new request walks the tree, matches the longest prefix already present, and prefills only the remainder. Nodes are reference-counted and evicted LRU. Matching is on exact token IDs — a single differing token forks the tree at that point, which is why prompt templates should put variable content last.

With a shared prefix of PP tokens and NN new ones, prefill attention drops from Θ((P+N)2)\Theta((P+N)^2) to Θ(N2+NP)\Theta(N^2 + NP) — and the quadratic term vanishes for the part you skipped.

When it pays

WorkloadShared prefixTTFT gain
Fixed system promptHighLarge
Few-shot classificationVery highLarge
Multi-turn chatGrows per turnLarge
Agent / tool loopsNearly totalVery large
Document Q&A, one docHighLarge
Independent user promptsNoneNone

Note the fourth row. An agent that replays its whole transcript each turn looks pathological until you realise every turn shares all but the last few hundred tokens with the previous one — reuse turns the worst-shaped workload into the best-cached one.

The security caveat. A cache shared across users is a side channel. If a hit is measurably faster than a miss, an attacker can test whether a particular prefix is already cached and learn what other users have sent. Production systems isolate the cache per tenant, or accept the leak knowingly.

N° 10Quantisation

Fewer bits per number

bb sits in the size formula as a plain multiplier, so halving it halves the cache and doubles arithmetic intensity. fp16 → fp8 is close to free on modern models and is the default in most serving stacks. Below that it stops being free, and the reason is structural rather than numerical.

Keys and values are not alike

Key vectors have persistent outlier channels — specific coordinates that are large across essentially every token, an artefact of how attention learns to route. Quantise a key per-token, and those coordinates set the scale for the whole vector, crushing everything else to a few levels.

Value vectors do not show that structure; their outliers are scattered per-token instead. So the two need opposite treatments — quantise keys per-channel, along the axis the outliers live on, and values per-token. This asymmetry is the core observation behind KIVI and most low-bit KV work since.

Rotate the problem away

A newer line of work attacks the outliers directly: apply a random orthogonal rotation to each key and value before quantising. Rotation preserves the dot products attention depends on while spreading variance evenly across coordinates, so no single channel dominates the scale.

PolarQuant (AISTATS 2026) reports ~6× memory reduction and up to 8× faster attention at 3 bits on H100, with no training or calibration — the rotation is data-independent. Work pushing toward 1-bit exists; it is not yet something to deploy without measuring your own task.

PrecisionBytes/eltCache vs bf16Intensity, GQA-8Practical status
bf16 / fp1621.00×8.0Baseline
fp8 (E4M3)10.50×16.0Widely deployed, near-lossless
int4, per-channel K0.50.25×32.0Viable with the right grouping
3-bit, rotated0.3750.19×42.7Recent, promising
1–2 bit≤0.25≤0.13×≥64Research

Intensity assumes the nh/nkv=8n_h/n_{kv}=8 geometry of Llama-3.1-70B; the H100 ridge point is 295, so even 3-bit KV remains memory-bound. Quantisation buys capacity first and speed second. Errors accumulate: a quantised key written at position 10 is re-read for every subsequent token, so per-token error is not independent — this is why long-generation quality degrades more than a short-prompt benchmark suggests.

N° 11Eviction

Keeping fewer tokens

Every technique so far stores all LL tokens more cheaply. The alternative is to stop storing some of them — turning a linear cost into a bounded one, at the price of information you cannot recover.

Sliding window

Keep the last w

Cache is capped at ww tokens regardless of context. Simple, and trained into the model rather than bolted on. Information beyond the window survives only through the residual stream, layer by layer.

Attention sinks

Keep the first few too

A pure sliding window collapses the moment the very first tokens are evicted. StreamingLLM's finding: models dump excess attention mass on the first few positions regardless of content. Retain 4 of them alongside the window and stability returns.

Heavy hitters

Keep what got attended to

Accumulated attention scores are heavily skewed — a small fraction of tokens receive most of the mass. H2O and SnapKV score and evict the rest, either continuously or once at the end of prefill.

The honest framing is that these are lossy and the loss is workload-dependent. Eviction policies are scored on benchmarks where the dropped tokens turned out not to matter; needle-in-a-haystack retrieval is precisely the task where a heavy-hitter policy discards the needle, because it was never attended to until the question arrived. A policy chosen on summarisation can fail badly on retrieval, and the failure is silent.

Interleaving is the current compromise: a few full-attention layers among many local-attention ones keeps a path for long-range information while capping most of the cache. It is an architectural decision made at training time, not a serving switch.

N° 12Edges

Moving it off the GPU, and what is still unsolved

Offload has a bandwidth budget

HBM is the only memory fast enough to be read every step, but cold sequences need not occupy it. Offloading to host DRAM over PCIe Gen5 ×16 gives roughly 64 GB/s against HBM's 3,350 — 52× slower. The arithmetic is unforgiving:

t=LsBWt = \frac{L\cdot s}{\text{BW}}

A 32k context of Llama-3.1-70B is 10 GiB, so a round trip over PCIe costs about 160 ms — acceptable to resume a paused conversation, ruinous inside a decode loop. Offload is a swap tier, not a capacity extension.

Disaggregated prefill and decode

Since §04's two phases have opposite bottlenecks, run them on different machines: compute-dense hardware for prefill, bandwidth-dense for decode, with the cache shipped between them. Each pool then scales on its own metric.

The cost is that the cache is now a network payload. The same 10 GiB moved over 400 Gb/s Ethernet is ~200 ms — so the transfer must overlap with computation, layer by layer, or you have moved the bottleneck rather than removed it.

What is genuinely unresolved

  • No agreed evaluation for lossy caching. Eviction and low-bit quantisation are reported on benchmarks chosen by their authors. There is no standard suite that stresses the failure modes — long-range retrieval, multi-hop reasoning over the discarded region, degradation over very long generations.
  • Compounding is under-studied. GQA and fp8 and paging and prefix reuse compose cleanly. Quantisation plus eviction plus a low-rank latent may not, and almost nothing reports the interaction.
  • MLA has no conversion recipe. GQA's uptraining path is what made it universal. Until an MHA or GQA checkpoint can be converted to MLA cheaply and reliably, its 71× stays available only to those training from scratch.
  • Position remains the obstacle to real reuse. RoPE means only true prefixes are shareable. Caching arbitrary fragments — a document usable at any offset in any prompt — needs either re-rotation with unrotated keys kept, or a positional scheme that separates content from place.
  • The bandwidth gap is widening. Compute per generation has grown faster than memory bandwidth for a decade. Every year the ridge point moves right and decoding falls further below it, which means architectural cache reduction keeps getting more valuable relative to faster arithmetic.

The short version

The KV cache is where a transformer's inference cost actually lives. It is linear in context and batch, it is re-read in full on every token, and it makes decoding memory-bound by construction — not by implementation quality. The fixes stack in a definite order: architecture first (GQA or MLA, decided at training time and worth 4–71×), then the allocator (paging and prefix reuse, worth ~3× in effective capacity for no quality cost), then precision (fp8 for free, lower with measurement), and eviction last, because it is the only one that throws information away.