LLMs
KV Cache
See which token projections caching saves, what attention must still read, and how memory use grows with context.
Jump to a section
Why this matters
A key-value cache stores attention keys and values from earlier tokens in an autoregressive decoder. When the next token arrives, the model can reuse that state instead of projecting the same prefix again.
The saving is in avoiding repeated work on past tokens. The current query still reads the relevant cached keys and values, so attention work and cache memory continue to grow with the context.
Build the mental model
In a causal decoder, token attends to tokens . When you later generate token , tokens are unchanged, so their keys and values are identical to what you already computed. Recomputing them is pure waste. The KV cache stores them; generation appends the new token's K and V and reuses everything else.
What caching stops you recomputing
At each decoding step, compare projecting the entire prefix with projecting only the new token’s key and value.
The visual loads as you reach this section.
Inspect the plotted values
x,y,series 1,1,Recompute prefix 1,1,Reuse cache 2,2,Recompute prefix 2,1,Reuse cache 3,3,Recompute prefix 3,1,Reuse cache 4,4,Recompute prefix 4,1,Reuse cache 5,5,Recompute prefix 5,1,Reuse cache 6,6,Recompute prefix 6,1,Reuse cache 7,7,Recompute prefix 7,1,Reuse cache 8,8,Recompute prefix 8,1,Reuse cache 9,9,Recompute prefix 9,1,Reuse cache 10,10,Recompute prefix 10,1,Reuse cache 11,11,Recompute prefix 11,1,Reuse cache 12,12,Recompute prefix 12,1,Reuse cache 13,13,Recompute prefix 13,1,Reuse cache 14,14,Recompute prefix 14,1,Reuse cache 15,15,Recompute prefix 15,1,Reuse cache 16,16,Recompute prefix 16,1,Reuse cache 17,17,Recompute prefix 17,1,Reuse cache 18,18,Recompute prefix 18,1,Reuse cache 19,19,Recompute prefix 19,1,Reuse cache 20,20,Recompute prefix 20,1,Reuse cache 21,21,Recompute prefix 21,1,Reuse cache 22,22,Recompute prefix 22,1,Reuse cache 23,23,Recompute prefix 23,1,Reuse cache 24,24,Recompute prefix 24,1,Reuse cache
The cache trades repeated computation for stored state. It grows with context length and must be read during attention. Decoding can become memory-bandwidth-bound, depending on batch size, hardware, and the attention implementation.
Work through the math
Without a cache, a naive decoder reprojects the full prefix. For dense key/value projections, step takes projection work. Summing over generated positions gives for those projections alone.
With a cache, each step projects only the new token ( under the same assumption). Its query still attends to the cached keys and values, requiring attention work. Caching removes repeated prefix projection; it does not make attention or memory use constant in context length.
Memory cost of the cache for one sequence:
The factor of 2 is for K and V. This grows linearly with sequence length and batch size and quickly dominates memory for long contexts. Two standard mitigations shrink it by sharing keys/values across query heads: Multi-Query Attention (MQA) uses a single K/V head for all query heads; Grouped-Query Attention (GQA) uses a few: cutting cache size (and bandwidth) by the head-sharing factor with minimal quality loss.
Key takeaway: Caching is safe because causal attention makes past keys/values immutable. Decoding can become memory-bandwidth-bound: you read the whole cache every step, which is why cache size, not FLOPs, limits long-context serving.
| No cache | With KV cache | |
|---|---|---|
| Per-step projection cost | Recompute K/V for whole prefix: | New token only: |
| Total cost for tokens | ||
| Memory | None extra | per sequence |
| Bottleneck | Compute | Memory bandwidth |
Read the implementation
1import torch
2from torch import Tensor
3
4
5class KVCacheAttention:
6 """Single-head causal attention with an incremental KV cache."""
7
8 def __init__(self) -> None:
9 self.k_cache: Tensor | None = None # (seq, d)
10 self.v_cache: Tensor | None = None
11
12 def step(self, q_t: Tensor, k_t: Tensor, v_t: Tensor) -> Tensor:
13 # q_t, k_t, v_t: (1, d) for the single new token
14 self.k_cache = k_t if self.k_cache is None else torch.cat([self.k_cache, k_t], dim=0)
15 self.v_cache = v_t if self.v_cache is None else torch.cat([self.v_cache, v_t], dim=0)
16 d = q_t.size(-1)
17 scores = (q_t @ self.k_cache.T) / d**0.5 # (1, seq_so_far): no future to mask
18 weights = torch.softmax(scores, dim=-1)
19 return weights @ self.v_cache # (1, d)
20
21
22attn = KVCacheAttention()
23d = 64
24for _ in range(5): # generate 5 tokens
25 out = attn.step(torch.randn(1, d), torch.randn(1, d), torch.randn(1, d))
26assert attn.k_cache.shape == (5, d) and out.shape == (1, d)Questions and trade-offs
- Conceptual: What does the KV cache store and why is caching it correct? (Past tokens' keys and values: they don't change as new tokens are generated in a causal decoder, so they can be reused.)
- Implementation: How does the cache change the per-step cost of decoding? (From recomputing K/V over the whole prefix each step to computing only the new token's projections plus attention over the cache.)
- Applied: Write the memory cost of the cache and name what makes it grow. (2 · layers · heads · d_head · seq_len · bytes: grows linearly with sequence length and batch size.)
- Systems-level: Why can autoregressive decoding become memory-bandwidth-bound? (Each step does little arithmetic but must read the entire growing cache from memory: bandwidth, not FLOPs, is the bottleneck.)
- Failure modes: How do MQA and GQA reduce the KV cache? (They share K/V heads across query heads (MQA uses one K/V head, GQA a few) shrinking cache size and bandwidth with little quality loss.)
Check your understanding
Without looking: explain what the KV cache stores and why reuse is valid, give the memory-cost formula, and say why decoding is memory-bound. Then name two techniques that shrink the cache. Check against Stages 1–3.
Oliver Perrin
Machine Learning Engineer · Founder, LiminalML
LiminalML brings together concept explanations, mathematical examples, and working visuals. Find more writing by Oliver on the LiminalML Substack.
Keep exploring the idea.
Ask a follow-up in a guided session, work through the self-check, or open the visual in Studio to build on it.
Related concepts
Deep Learning
Attention Mechanisms
Turn compatibility scores into attention weights, then combine values. Explore the geometry, normalization, and limits of the mechanism.
Deep Learning
Transformer Architecture
Follow attention, feed-forward layers, normalization, and residual connections through a Transformer block.
LLMs
LoRA and PEFT
Follow a frozen weight path and a trainable low-rank correction. Understand parameter savings, scaling, and merging.