AI Infra Interviews logo
🚀 Inference & Serving
Foundational

The KV Cache

The KV cache stores each token's attention keys and values so decode never recomputes them, turning a quadratic cost into a linear one at the price of memory that grows with every token in every concurrent sequence. Its size, 128 KB per token for Llama 3.1 8B and 320 KB for 70B in bf16, is what caps concurrency and context on a given GPU, so it decides batch size, replica count and whether a model fits at all.

TL;DR: Every decode step needs the keys and values of every earlier token, so the engine keeps them in HBM instead of recomputing them, at 2 × layers × KV heads × head dimension × bytes per token. That is 320 KB per token for Llama 3.1 70B in bf16, so a single 128k-token context costs about 43 GB and the cache, not the weights, is what runs out first under load.

What is cached and why

Attention for a new token computes a query vector, then scores it against the key of every previous token and mixes their values. The keys and values of a token do not change once it is in the context: the same token at the same position produces the same K and V in every later step. Without a cache, decode step n recomputes K and V for all n prior tokens, and the total work over a response of length n grows with n squared. With the cache, each token's K and V are computed once, during prefill or on the step that emitted it, and read back on every later step.

KV CACHE (drag through decoding)
Themodelwritesonetokenatatime
without cache10 ops
with cache4 ops
With the cache, each token's keys and values are computed once and reused. Without it, every step recomputes them for all prior tokens, so total work grows with the square of the sequence. At step 4 that is 2.5x more compute wasted.

The trade is compute for memory. Reads are cheap on paper; the cost is that the cache lives in HBM for the life of the request, alongside the weights, and it grows one entry per layer per token for as long as the sequence runs.

The per-token formula

For standard multi-head or grouped-query attention, each layer stores one key and one value vector per KV head:

bytes per token = 2 × n_layers × n_kv_heads × head_dim × bytes_per_element

The leading 2 is K and V. Worked for the presets in the site's model table, all in bf16 (2 bytes):

ModelLayersKV headsHead dimPer token8k context128k context
Llama 3.1 8B328128128 KB1.1 GB17.2 GB
Llama 3.1 70B808128320 KB2.7 GB43 GB
Llama 3.1 405B1268128504 KB4.1 GB66 GB
Qwen3 235B-A22B944128188 KB1.6 GBnot supported
DeepSeek-V3 (MLA)61latent 512 + rope 64about 70 KB0.6 GB9.2 GB

Two things jump out. The 405B model has a smaller cache per parameter than the 8B because both use 8 KV heads; the cache scales with layers, not with model width. And DeepSeek-V3 caches a single 576-wide latent per layer with no factor of two, which is why a 671B-parameter model has a cache one fifth the size of a 70B dense model's.

Why it decides concurrency

The cache budget is whatever HBM is left after weights and working memory. Llama 3.1 70B in bf16 on two H100s under tensor parallelism has 160 GB total, about 141 GB of it weights, leaving under 15 GB after activations and CUDA context. At 320 KB per token that is roughly 45k cached tokens across all requests: five concurrent sequences at 8k, or a single request at 45k. Move the same model to four H100s and the budget becomes about 170 GB, over 500k tokens. The weights did not change; the batch you can serve went up ten times.

Llama 3.1 70B bf16, tensor parallel across H100s 2 × H100 160 GB: 141 weights, about 15 KV (45k tokens) 4 × H100 320 GB: 141 weights, about 170 KV (530k tokens) 8 × H100 640 GB: 141 weights, about 490 KV (1.5M tokens) weights KV budget

Engines log this budget at startup. vLLM prints a line of the form "GPU KV cache size: N tokens" followed by "Maximum concurrency for M tokens per request", and that pair is the first thing an operator reads on a new deployment. If N divided by your typical context is smaller than your target batch, you will preempt requests under load, and the num_preemptions counter climbing is the confirmation.

The cache as a bandwidth cost

Memory is the visible cost. The hidden one is that every decode step reads the entire cache of every sequence in the batch. At 8k context, one Llama 70B sequence reads about 2.7 GB of cache per step. A batch of 64 reads about 170 GB per step, more than the 141 GB of weights. Past that point the step time is dominated by cache traffic, throughput per added sequence flattens, and long-context batches on the same hardware run measurably slower per token than short-context ones. This is the mechanism behind "TPOT got worse when we raised the context limit", and it is why GQA and MLA exist.

What interviewers are listening for

They want the formula stated from memory and worked for one named model, with the assumptions (dtype, layers, KV heads) said out loud. Then they push: "your KV budget is 40 GB, context is 32k, what batch do you get" (about four sequences for the 70B). The follow-up held in reserve is "what changes for a mixture-of-experts model". The answer that sounds right and fails is "the cache shrinks because only some experts are active". Experts sit in the MLP; attention runs every layer for every token, so the cache is set by the attention configuration alone. Qwen3 235B-A22B caches 188 KB per token despite activating 22B parameters.

Common misconceptions

The cache is not the weights. Weights are fixed per model; the cache scales with batch times context and is what fails first.

Quantizing weights does not shrink the cache. The cache has its own dtype (fp8 KV halves it) and its own switch.

A longer supported context does not cost anything until someone uses it. A 128k limit with 2k-token traffic uses the same memory as a 2k limit; the scheduler reserves by actual length when paged.

Key takeaways

  • Bytes per token = 2 × layers × KV heads × head dim × bytes; 128 KB for Llama 3.1 8B, 320 KB for 70B, about 70 KB for DeepSeek-V3 in bf16.
  • Total cache = per token × context × batch, and it competes with weights for HBM; leftover HBM decides batch.
  • Every decode step re-reads every sequence's cache, so long contexts slow every token, not just the first.
  • MoE does not shrink the cache; attention heads and layers set it.
  • Read the engine's KV budget line at startup and watch preemptions; both tell you when the cache is the bottleneck.
RELATED CONCEPTS
LESSONS THAT TEACH THIS
PRACTICE THIS IN REAL QUESTIONS