KV-Cache: Theory & Derivation

Why autoregressive transformers need a KV-cache: derivation from the attention computation, memory growth analysis, the relationship between KV-cache size and inference latency, and how cache enables O(Td) per-step generation.

Intermediate

Table of Contents

  1. Learning Objectives
  2. Notation
  3. Core Intuition
  4. The Redundancy in Autoregressive Attention
  5. KV-Cache: Formal Derivation
  6. Memory Growth Analysis
  7. Inference Latency with KV-Cache
  8. Prefill vs Decode Phases
  9. Multi-Layer KV-Cache
  10. Common Pitfalls
  11. Summary
  12. Exercises

Learning Objectives

  1. Identify the redundant computation in naive autoregressive attention.
  2. Derive the KV-cache update rule and show it reduces per-step complexity from O(T2d)O(T^2d) to O(Td)O(Td).
  3. Compute KV-cache memory as a function of LL, HH, dkd_k, and TT.
  4. Distinguish prefill (compute-bound) from decode (memory-bound) phases.
  5. Analyze how KV-cache size limits maximum batch size and context length.

Notation

  • KtRt×dk\mathbf{K}_{\leq t} \in \mathbb{R}^{t \times d_k} — cached keys up to step tt
  • VtRt×dv\mathbf{V}_{\leq t} \in \mathbb{R}^{t \times d_v} — cached values up to step tt
  • qtRdk\mathbf{q}_t \in \mathbb{R}^{d_k} — query at current step tt
  • LL — number of layers, HH — number of heads

Core Intuition

In autoregressive generation, the model generates one token at a time. At step tt, it needs to compute attention over all previous tokens 1,,t1, \ldots, t. Without caching, this means recomputing all keys and values from scratch at every step — O(t)O(t) redundant work per step, O(T2)O(T^2) total. The KV-cache stores previously computed keys and values, so each step only computes the new token's key/value and appends them.

KV-Cache During Generation

Generated tokensThecatsatonK cacheV cacheStep 4: compute K,V for token "on" — reuse 3 cached rows
Step
4
Cached (reuse)New compute
Explore: At each generation step, only the new token's K and V are computed. Past KV pairs are cached — avoiding O(T²) recomputation across autoregressive steps.

The Redundancy in Autoregressive Attention

Naive approach at step tt: Given all tokens x1,,xtx_1, \ldots, x_t:

  1. Compute Q,K,VRt×dk\mathbf{Q}, \mathbf{K}, \mathbf{V} \in \mathbb{R}^{t \times d_k} from all tt tokens.
  2. Compute attention: softmax(QKT/dk)V\text{softmax}(\mathbf{QK}^T/\sqrt{d_k})\mathbf{V}.
  3. Take only the last row (the new token's output).

Problem: Steps 1 and 2 have already been done for tokens 1,,t11, \ldots, t-1 at the previous step. We're recomputing K1:t1\mathbf{K}_{1:t-1} and V1:t1\mathbf{V}_{1:t-1} redundantly.

Total naive cost: t=1TO(td)=O(T2d)\sum_{t=1}^T O(t \cdot d) = O(T^2 d) — same as full-sequence attention.


KV-Cache: Formal Derivation

Key observation: The projections ki=xiWK\mathbf{k}_i = \mathbf{x}_i\mathbf{W}^K and vi=xiWV\mathbf{v}_i = \mathbf{x}_i\mathbf{W}^V depend only on token ii, not on future tokens (due to causal masking).

Cache update at step tt:

Kt=[Kt1ktT],Vt=[Vt1vtT].(1)\mathbf{K}_{\leq t} = \begin{bmatrix}\mathbf{K}_{\leq t-1} \\ \mathbf{k}_t^T\end{bmatrix}, \quad \mathbf{V}_{\leq t} = \begin{bmatrix}\mathbf{V}_{\leq t-1} \\ \mathbf{v}_t^T\end{bmatrix}. \tag{1}

Attention at step tt (only for the new token):

qt=xtWQ,outputt=softmax(qtKtTdk)Vt.(2)\mathbf{q}_t = \mathbf{x}_t\mathbf{W}^Q, \quad \text{output}_t = \text{softmax}\left(\frac{\mathbf{q}_t\mathbf{K}_{\leq t}^T}{\sqrt{d_k}}\right)\mathbf{V}_{\leq t}. \tag{2}

Per-step cost: Computing qtKtT\mathbf{q}_t\mathbf{K}_{\leq t}^T is O(tdk)O(t \cdot d_k). Over HH heads: O(td)O(t \cdot d).

Total cost for generating TT tokens: t=1TO(td)=O(T2d/2)\sum_{t=1}^T O(td) = O(T^2d/2) — same total FLOPs but now incrementally distributed, avoiding O(T2)O(T^2) at each step.


Memory Growth Analysis

At step tt, the KV-cache stores:

Cache size(t)=2×L×H×dk×t×bytes_per_element.(3)\text{Cache size}(t) = 2 \times L \times H \times d_k \times t \times \text{bytes\_per\_element}. \tag{3}

Factor 2: one for keys, one for values.

Example: LLaMA-2 70B (L=80,H=8L=80, H=8 GQA groups, dk=128d_k=128, FP16):

Per token=2×80×8×128×2=327,680 bytes320 KB.(4)\text{Per token} = 2 \times 80 \times 8 \times 128 \times 2 = 327{,}680 \text{ bytes} \approx 320 \text{ KB}. \tag{4}

For T=4096T = 4096 tokens: 320 KB×4096=1.28 GB320 \text{ KB} \times 4096 = 1.28 \text{ GB} per request.

For T=128KT = 128K tokens: 320 KB×128K=40 GB320 \text{ KB} \times 128K = 40 \text{ GB} — may exceed GPU memory for a single request!


Inference Latency with KV-Cache

Without cache: At step tt, load all tt token representations through LL layers. Cost: O(tClayer)O(t \cdot C_{\text{layer}}) per step.

With cache: At step tt, only process 1 new token through LL layers + one attention dot product against tt cached entries. Cost: O(Clayer+td)O(C_{\text{layer}} + t \cdot d) per step.

The KV read at each layer is:

Bytes read=2×H×dk×t×2 (FP16)=4Hdkt.(5)\text{Bytes read} = 2 \times H \times d_k \times t \times 2 \text{ (FP16)} = 4Hd_kt. \tag{5}

At large tt, this KV read dominates the per-step latency (memory-bandwidth-bound).


Prefill vs Decode Phases

Prefill phase (processing the prompt):

  • All prompt tokens processed in parallel (one forward pass, batch of TpromptT_{\text{prompt}} tokens).
  • Compute-bound (large GEMM operations).
  • Populates the KV-cache for all prompt tokens.

Decode phase (generating tokens one by one):

  • Single token processed per step.
  • Memory-bandwidth-bound (loading KV-cache + model weights for tiny batch).
  • Latency: dominated by memory reads.

Time to first token (TTFT): Determined by prefill time. Inter-token latency (ITL): Determined by decode step time.

Total generation time=Tprefill+TgenerateToutput.(6)\text{Total generation time} = T_{\text{prefill}} + T_{\text{generate}} \cdot T_{\text{output}}. \tag{6}

Multi-Layer KV-Cache

Each of the LL layers maintains its own KV-cache (keys and values are layer-specific since they depend on that layer's hidden state):

Total cache=L×(per-layer cache).(7)\text{Total cache} = L \times \text{(per-layer cache)}. \tag{7}

Important: KV-cache cannot be shared across layers because K(l)=h(l)WK(l)\mathbf{K}^{(l)} = \mathbf{h}^{(l)}\mathbf{W}_K^{(l)} depends on the layer-ll representation.

However, with GQA/MQA, the number of distinct K/V heads is reduced:

  • MHA: L×H×dkL \times H \times d_k per token per K and V
  • GQA: L×G×dkL \times G \times d_k per token (where G<HG < H)
  • MQA: L×dkL \times d_k per token

Common Pitfalls

Pitfall 1. Forgetting that KV-cache grows linearly with context. For a 128K context window, the cache alone can exceed the model weights in memory.

Pitfall 2. Not accounting for KV-cache in batch size planning. With BB concurrent requests: total KV memory = B×T×per-token cacheB \times T \times \text{per-token cache}. This often limits max batch size.

Pitfall 3. Assuming KV-cache is only relevant for generation. During prefill, the cache must be written — this write bandwidth can bottleneck short-prompt, long-generation workloads.


Summary

  • KV-cache eliminates redundant key/value computation during autoregressive generation.
  • Reduces per-step complexity from O(T2d)O(T^2d) to O(Td)O(Td) but introduces linear memory growth.
  • Cache size per token: 2LGdk2LGd_k elements (with GQA).
  • Prefill = parallel, compute-bound. Decode = sequential, memory-bound.
  • KV-cache size often determines max context length and batch size.

Exercises

Exercise 1. Compute the KV-cache memory (in GB) for GPT-3 175B (L=96,H=96,dk=128L=96, H=96, d_k=128) at T=2048T=2048 in FP16.

Exercise 2. Derive the total FLOPs saved by using KV-cache vs naive recomputation for generating TT tokens.

Exercise 3. For a GPU with 80 GB memory and a model taking 40 GB, compute the maximum batch size BB at context length T=4096T=4096 with per-token cache of 320 KB.

Exercise 4. Explain why the decode phase is memory-bandwidth-bound while prefill is compute-bound.

Exercise 5. Compute the speedup from using GQA (G=8G=8) vs MHA (H=32H=32) in terms of KV-cache memory and attention compute during decode.