KV-cache optimization
Why We Need It: Derivation from First Principles
The Naive Attention Cost
In multi-head self-attention, for sequence length , we compute:
Step-by-step cost analysis:
-
During training (teacher forcing): We have all tokens at once.
- are computed once
- Attention matrix: operations
- Total per layer:
-
During autoregressive generation (the problem):
- Generate token at position : compute ✓
- Generate token at position : compute (new), but recompute ✗
- Generate token at position : recompute and ✗
Why this step? Each generation step only adds one new token, but without caching, the model treats it as a fresh sequence and recomputes projections for all previous tokens.
Total naive cost for generating tokens:
This is quadratic in sequence length just for the projection overhead, before even doing attention!
The KV-Cache Solution
Derivation of savings:
Why this works: The causality mask in attention ensures token only attends to positions . Since for are fixed (the model is frozen during generation), their values never change—perfect for caching.
Full-model cache (across all layers) = .
Example: GPT-3 style model (, heads, , fp16):
- Per token, per layer:
- Per token, all 96 layers:
- For 2048 tokens (all layers):
Why this step? We need 2 matrices (K and V), each of size (num_tokens × num_heads × head_dim), stored per layer. The full-model cost multiplies by the number of layers, which is why the per-token figure jumps from ~48 KB (one layer) to ~4.7 MB (all 96 layers).
Worked Examples
Step 1: Previous cache state (after generating tokens 1-2):
Why this step? Each has shape (num_heads, head_dim) = (4, 64). We've generated 2 tokens so far.
Step 2: Compute projections for new token:
Step 3: Update cache (concatenation):
Step 4: Compute attention using only with all cached keys:
Why this step? (shape 4×64) multiplies with cached K (shape 3×4×64 → transpose to 4×64×3). Each head gets a 1×3 score vector (attending to 3 positions).
Cost: for one token's projections + for attention scores. No recomputation of .
Generating a 100-token sequence:
Without cache:
- Per layer, per token : Compute key-value pairs →
- Total: FLOPs
With cache:
- Per layer, per token: Compute 1 new K, V →
- Total: FLOPs
- Speedup: 50× (approximately matches the factor for )
Memory overhead:
- Cache size:
Why this matters: 3.7 MB is negligible compared to the 500 MB model weights, but saves 98% of projection compute.
Advanced Optimizations
1. Multi-Query Attention (MQA)
Cache reduction:
Why this step? With heads, MQA uses 32× less KV-cache memory. Quality drop is typically 1-2% perplexity.
2. Grouped-Query Attention (GQA)
Middle ground: Group query heads into groups (), each group shares one K, V head.
Example: Llama-2 70B uses , (8× cache reduction vs. standard).
3. Sliding Window / Local Attention
For very long sequences, only cache the last tokens:
Tradeoff: Constant memory, but loses long-range dependencies beyond tokens.
Use case: Summarization with 100K+ token contexts where recent context matters most.
4. FlashAttention Integration
FlashAttention computes attention without materializing the full score matrix by fusing operations and using SRAM tiling. KV-cache + FlashAttention synergize:
- Cache eliminates redundant K, V projections
- FlashAttention eliminates memory overhead of attention scores
Combined speedup: 10-20× for long sequences (both reduce different bottlenecks).
Common Mistakes
Why it feels right: also requires a matrix multiply, so caching seems logical.
Steel-man: In a typical forward pass, caching would help. But in autoregressive generation, each new token generates a fresh query that attends to all previous positions. We need a new for token because it encodes "what the new token is looking for."
The fix: Only K and V are reusable because they represent "what previous tokens offer." The query represents "what the current token needs"—it must be recomputed.
Why it feels right: The cache has the data, why not use it?
Steel-man: During training with full sequences, we use a causal mask. It's easy to forget this is still needed during generation.
The fix: Even with caching, apply causal mask:
This ensures token only sees positions (even though physically don't exist yet in autoregressive mode, this matters for batched generation).
Why it feels right: The notation says memory.
Steel-man: The notation hides the constants. For a 32-layer, 32-head model with in fp16:
For tokens: ~105 GB just for the cache (exceeds most GPU memory).
The fix: Use hybrid strategies like sliding window cache, offloading old cache to CPU, or sparse attention for ultra-long contexts.
Implementation Details
Typical PyTorch pattern:
class CachedAttention:
def __init__(self):
self.kv_cache = None # Will hold (batch, num_heads, seq_len, head_dim)
def forward(self, x, use_cache=True):
B, T, C = x.shape
Q = self.W_q(x) # Always compute fresh
K_new = self.W_k(x)
V_new = self.W_v(x)
if use_cache and self.kv_cache is not None:
K_cached, V_cached = self.kv_cache
K = torch.cat([K_cached, K_new], dim=2) # Concat along seq_len
V = torch.cat([V_cached, V_new], dim=2)
else:
K, V = K_new, V_new
# Standard attention computation
scores = (Q @ K.transpose(-2, -1)) / math.sqrt(self.head_dim)
atn = F.softmax(scores, dim=-1)
out = atn @ V
if use_cache:
self.kv_cache = (K.detach(), V.detach()) # Detach to avoid gradient retention
return outWhy detach? Prevents backprop through the entire cached history (only needed during training; during inference, no gradients anyway).
When to Use What
| Context Length | Strategy | Example Use Case |
|---|---|---|
| < 2K tokens | Standard KV-cache | Chatbot turns, code completion |
| 2K - 8K | KV-cache + MQA/GQA | Document Q&A |
| 8K - 32K | Sliding window cache | Long-form writing |
| 32K+ | Sparse/local + offload | Book summarization, legal docs |
Recall Explain to a 12-Year-Old
Imagine you're writing a story one word at a time. Every time you write a new word, you need to remember what all the previous words were about so your new word makes sense.
The slow way: Every time you write a new word, you re-read the entire story from the beginning and take notes about each word. Writing 100 words means reading the story 100 times!
The KV-cache way: The first time you read each word, you write down notes on sticky notes (that's the "cache"). When you write word #50, you just glance at your 49 sticky notes instead of re-reading the whole story. You only add one new sticky note for word #50.
The tradeoff: You need space on your desk for all the sticky notes (memory), but you save tons of time not re-reading. If your desk gets too crowded (very long story), you might only keep the last 20 sticky notes and throw away older ones (sliding window).
Connections
- 6.1.1-transformer-architecture - The self-attention mechanism that KV-cache optimizes
- 6.1.4-multi-head-attention - How multiple heads multiply cache requirements
- 6.2.3-inference-optimization - Broader category of deployment speedups
- 6.3.1-model-quantization - Reducing cache memory via lower precision (int8, fp16)
- 5.4.2-beam-search - Generation algorithm that becomes cache-intensive with multiple beams
- 6.1.11-sparse-attention - Alternative approach to reduce attention cost
- 7.2.1-llm-deployment - Real-world systems where cache management is critical
#flashcards/ai-ml
What is the primary problem KV-cache solves? :: During autoregressive generation, preventing redundant recomputation of key and value projections for previously generated tokens. Without caching, we recompute K and V for all prior tokens at every step, leading to cost instead of .
Derive the KV-cache memory formula per layer :: Cache size = . The 2 accounts for separate K and V matrices, is sequence length, is number of attention heads, is head dimension, and bytes is precision (2 for fp16, 4 for fp32). Multiply by for the full model.
Why don't we cache the Query (Q) projections?
What is Multi-Query Attention (MQA) and its cache benefit?
Calculate KV-cache size for GPT-2 Small generating 512 tokens :: GPT-2 Small: 12 layers, 12 heads, , fp16. Cache = bytes = 18.9 MB. This is per-sample; batch size multiplies this.
Why does KV-cache still require causal masking?
What is Grouped-Query Attention (GQA)?
When does KV-cache become a bottleneck?
Concept Map
Hinglish (regional understanding)
Intuition Hinglish mein samjho
Dekho, transformer jab ek-ek token generate karta hai (jaise GPT bolte-bolte agla shabd nikalta hai), toh har step pe woh saare purane tokens ka attention dobara se compute karta hai. Ab socho, token 1 ka key aur value toh badalta hi nahi jab aap token 100 generate kar rahe ho — phir bhi model har baar unhe naye sire se calculate karta hai. Yeh bilkul aisa hai jaise agla word likhne ke liye aap poori kitaab dobara padho. Bahut waste ho raha hai! KV-cache ka core idea simple hai: jo K aur V ek baar compute ho gaye, unhe memory mein store kar lo aur reuse karo. Bas naye token ka K aur V calculate karke cache mein append kar do, purane wale chhedo mat.
Iska maths dekhoge toh samajh aayega kitna bada farak padta hai. Bina cache ke, L tokens generate karne ka cost O(L²d²) aata hai — quadratic, matlab sequence lambi hote hi cost tezi se badhta hai. Cache ke saath yeh O(Ld²) ban jaata hai, yaani L guna speedup! Yeh kaam isliye karta hai kyunki attention mein causal mask hota hai — token t sirf apne se pehle wale tokens ko dekhta hai, aur woh purane K, V generation ke dauraan fixed rehte hain (model freeze hai). Fixed cheezein cache karne ke liye perfect hoti hain.
Lekin ek catch hai: yeh speed memory ke badle milti hai. Har token ke liye, har layer ke liye 2 matrices (K aur V) store karni padti hai. GPT-3 jaise bade model mein 2048 tokens ka cache lagbhag 9.6 GB tak pahunch sakta hai! Isiliye yeh topic important hai — real deployment mein aapko yeh tradeoff samajhna zaroori hai. Fast inference chahiye toh cache lagao, par phir memory ka intezaam bhi karna padega. Isi wajah se aage aap padhoge ki log KV-cache ko aur optimize karte hain (jaise multi-query attention, quantization) taaki memory bhi bache aur speed bhi mile.