The Roofline Model & KV Cache Mechanics
Serving large language models presents an architectural paradox: the exact same model running on the exact same GPU behaves like two completely different computational workloads depending on whether it is processing input tokens or generating output tokens.
This chapter details the physics of GPU execution: the Roofline Model, the mathematical separation between the Prefill and Decode phases, the exact memory scaling of the KV Cache, and how PagedAttention eliminates memory fragmentation.
Imagine reading a 10-page essay versus writing a 10-page essay.
When you read the essay (Prefill), your brain takes in whole paragraphs at once. You are using 100% of your cognitive reading capacity (Compute-Bound).
When you write the essay word by word (Decode), your hand can only move so fast. You know what you want to write, but you have to write one letter at a time, repeatedly dipping your pen into the ink bottle. Your brain is barely working hard, but you are waiting on the physical speed of the pen (Memory-Bandwidth-Bound).
In LLM serving, we use the Roofline Model to map out where these two phases sit on physical chips.
1. The Roofline Model & Operational Regimes
The Roofline Model defines the attainable performance of any kernel or workload on hardware:
Deriving Operational Intensity
For any neural network operation, the Operational Intensity () is:
Consider multiplying an activation matrix by a weight matrix :
- Total FLOPs: (1 multiply and 1 add per element).
- Bytes read (assuming weights dominate): .
Thus, the arithmetic intensity is:
The Batch Size Relationship: Notice that and cancel out! Arithmetic intensity is directly proportional to Batch Size (). At batch size (single user decode), arithmetic intensity is roughly FLOP/byte—deep inside the memory-bandwidth-bound region. At batch size , arithmetic intensity jumps to FLOPs/byte, moving close to the compute roof!
2. Prefill vs Decode Phases in LLMs
An LLM request is serviced in two fundamentally distinct phases:
| Metric | Prefill Phase (Prompt Processing) | Decode Phase (Token Generation) |
|---|---|---|
| Input | Prompt tokens ( tokens at once) | Exactly 1 token per sequence |
| Operation | General Matrix-Matrix Multiply (GEMM) | General Matrix-Vector Multiply (GEMV) |
| Bottleneck | Compute-Bound (High FLOPs/byte) | Memory-Bandwidth-Bound (Low FLOPs/byte) |
| Latency Metric | Time-To-First-Token (TTFT) | Time-Per-Output-Token (TPOT) |
| Hardware Saturation | Tensor Cores run at 70-85% MFU | Tensor Cores sit idle >80% of the time |
3. The KV Cache Memory Formula
During autoregressive decoding at step , the attention calculation needs to attend to all past tokens . Rather than recomputing earlier key and value vectors from raw embeddings, they are stored in the KV Cache.
The exact memory footprint for a single request of length tokens is:
Where:
- The factor of accounts for both Key and Value tensors.
- is the number of transformer blocks.
- is the number of key/value heads (in Multi-Head Attention, ; in Grouped-Query Attention, ).
- is the dimension of each attention head.
- is sequence length.
4. PagedAttention: Virtual Memory for LLM Serving
In traditional serving frameworks, memory for a request was allocated contiguously for the user's max_tokens setting.
Explore how PagedAttention partitions KV blocks into non-contiguous physical pages:
The PagedAttention Mechanism
- Logical to Physical Mapping: The KV Cache of a sequence is split into fixed-size logical blocks (e.g. 16 tokens per block).
- Block Table: The serving engine maintains a routing table that maps logical block numbers to arbitrary physical memory frames allocated from a global GPU memory pool.
- Zero External Fragmentation: Because every physical block has the exact same size, the memory allocator never needs to find contiguous blocks.
- Copy-on-Write for Parallel Sampling: When an agent or user branches into multiple outputs (e.g. Speculative Decoding, beam search, or tree-of-thought), child sequences share the parent prompt's physical blocks until one of them writes a new token.
5. Python Simulation: KV Cache Profiler
Run this live in-browser Python implementation to compute the exact KV Cache memory scaling across sequence lengths and batch sizes:
Always balance TTFT and TPOT. When serving interactive conversational agents, separate your clusters or workers into specialized Prefill Nodes (optimized for dense compute throughput) and Decode Nodes (optimized for memory bandwidth and PagedAttention batch concurrency).
Learn how iteration-level scheduling and draft models accelerate generation in Continuous Batching & Speculative Decoding.