InterviewHubPRO
Section Overview
Q11

Question 11 of 50

Part 2: LLM Inference, Serving & Runtime Optimization

22%
AI Engineering & LLMs (50 Qs)•Part 2: LLM Inference, Serving & Runtime Optimization

Autoregressive Generation & The KV Cache: Memory Footprint & Scaling Formulas

1. Two Phases of LLM Inference: Prefill vs. Decode

Transformer-based autoregressive models generate text in two radically distinct operational phases:

sql
  Phase 1: Prefill (Prompt Processing)             Phase 2: Decode (Token-by-Token)
  Compute-Bound (High Parallelism)                  Memory-Bandwidth Bound (Sequential)
  ┌─────────────────────────────────────┐           ┌─────────────────────────────────────┐
  │ Processes all prompt tokens (e.g.   │           │ Generates 1 token at a time:        │
  │ 2,048 tokens) concurrently via GEMM │ ──► K, V  │ Attends to all past tokens via      │
  │ matrix multiplication. Saturates GPU│   Cache   │ cached Keys and Values. GPU memory  │
  │ Tensor Cores.                       │           │ bandwidth is the primary bottleneck.│
  └─────────────────────────────────────┘           └─────────────────────────────────────┘

2. Why the KV Cache is Essential

In standard self-attention, generating token $t$ requires computing attention against all tokens $1 \dots t-1$. Without caching, previous key ($K$) and query/value ($V$) states must be recomputed at every step, creating an $O(N^2)$ generation cost.

  • The KV Cache stores computed Key and Value vectors for all past tokens in GPU VRAM.
  • Each new decoding step only computes Key and Value for the single newly generated token ($O(1)$ per step).

3. KV Cache Memory Sizing Formula

The memory footprint of the KV cache grows linearly with batch size and context sequence length:

$$\text{Memory (bytes)} = 2 \times 2 \times n_{\text{layers}} \times n_{\text{heads}} \times d_{\text{head}} \times \text{seq_len} \times \text{batch_size} \times \text{bytes_per_elem}$$

Where:

  • $2 \times$ accounts for both Keys and Values.
  • $\text{bytes_per_elem} = 2$ for FP16/BF16, or $1$ for FP8.

Concrete Example (Llama 3 70B, BF16, Context = 8,192, Batch Size = 8):

  • Layers = 80, KV Heads = 8 (Grouped-Query Attention), Head Dim = 128
  • $\text{Bytes} = 2 \times 80 \times 8 \times 128 \times 8,192 \times 8 \times 2 = 21,474,836,480\text{ bytes} \approx \mathbf{21.47\text{ GB}}$

The KV cache for just 8 concurrent users consumes over 21 GB of GPU VRAM!

python
def calculate_kv_cache_vram_gb(
    num_layers: int, 
    num_kv_heads: int, 
    head_dim: int, 
    seq_len: int, 
    batch_size: int, 
    dtype_bytes: int = 2
) -> float:
    # 2 for Key and Value
    total_bytes = 2 * num_layers * num_kv_heads * head_dim * seq_len * batch_size * dtype_bytes
    return total_bytes / (1024 ** 3)

# Example: Llama 3 8B (32 layers, 8 KV heads, 128 head dim, 8k tokens, batch 16)
vram_gb = calculate_kv_cache_vram_gb(32, 8, 128, 8192, 16, dtype_bytes=2)
print(f"Required KV Cache VRAM: {vram_gb:.2f} GB")  # ~17.18 GB