skip to content

What problem does PagedAttention solve for the KV cache in vLLM?

level: middleimportance: should knowfreq 50%

answer

  1. virtual memory, applied to attention
  2. fixed-size blocks plus a per-sequence table
  3. no reservation for the worst-case length
  4. waste bounded to one partial block
  5. blocks can be shared copy-on-write

basics

~20 s

It stores each sequence's key/value cache in small fixed-size blocks tracked by a block table instead of one contiguous reservation sized for the maximum output length. That removes the huge reserved-but-unused waste, so far more sequences fit in the same VRAM.

solid answer

~60 s

Without paging, a server must reserve a contiguous KV buffer per sequence big enough for the worst case — typically the full `max_model_len` — because it cannot know in advance how many tokens the model will emit. A request that generates 50 tokens against a 32k reservation wastes almost all of it, and that waste, not compute, caps concurrency. PagedAttention borrows virtual memory's idea. The KV cache is carved into fixed-size blocks holding a handful of tokens each (16 by default in vLLM), and every sequence gets a **block table** mapping its logical token positions to physical blocks scattered anywhere in the pool. Blocks are allocated on demand as generation proceeds and freed the instant a sequence finishes, so internal waste is bounded by at most one partly-filled block per sequence and external fragmentation disappears. The indirection buys a second win: blocks are shareable. Sequences with a common prompt prefix, or multiple samples from one prompt, can point at the same physical blocks copy-on-write — the basis of prefix caching. More usable KV memory means a larger running batch, which is what makes continuous batching pay off.

go deeper

for a junior

Know that the KV cache holds past tokens' keys and values, and that vLLM splits it into fixed-size blocks rather than one big per-request reservation.

for a middle

Explain the waste it removes — worst-case reservations, internal waste and fragmentation — and how a block table gives each sequence a logical view of scattered physical blocks.

for a senior

Connect it to operations: larger running batches, prefix-cache reuse for long system prompts, and reading preemption and cache-usage metrics when the block pool saturates.

for a principal

Frame it as the capacity lever it is — KV memory, not FLOPs, sets concurrency, so context-length policy and prompt design are architectural decisions about how much traffic a GPU fleet can carry.

## What the KV cache is During autoregressive decoding, every previously generated token's key and value vectors must be available for the attention computation of the next token. Recomputing them each step would be quadratic, so servers cache them. The cache grows by one entry per token per layer, and it is the dominant *variable* memory cost of serving — model weights are fixed, KV cache scales with context length times concurrency. ## The pre-paging allocation problem Early serving stacks allocated the KV cache as one contiguous tensor per sequence. The size had to be chosen at admission time, before a single token had been generated, so implementations reserved room for the maximum possible length. Three kinds of waste followed: - **Internal waste**: a sequence that reserves 4096 slots and emits 60 tokens leaves ~98% of its reservation untouched for its whole lifetime. - **Reservation waste**: the space is unusable by anyone else even though it is provably empty. - **External fragmentation**: variable-sized contiguous reservations leave holes too small for the next request even when total free memory is ample. Measurements in the vLLM paper found that only a small fraction of KV memory in such systems held live tokens; the rest was reserved or fragmented. Since KV capacity determines how many sequences can run at once, that waste directly caps throughput. ## The paging idea Operating systems solved the identical problem with virtual memory: give each process a logical address space, split physical memory into fixed-size pages, and keep a page table mapping one to the other. Physical pages need not be contiguous, and they are allocated lazily. PagedAttention applies this to attention. The GPU's KV memory is a pool of fixed-size **blocks**, each holding a small number of tokens' keys and values for all layers and heads (vLLM's default block size is 16 tokens). Each sequence owns a **block table**: an ordered list of physical block ids for its logical positions. The attention kernel is written to gather keys and values through that table rather than assuming contiguity — this is the part that required a custom CUDA kernel, not just a bookkeeping change. Allocation is now incremental. A sequence starts with just enough blocks for its prompt, gains one block every 16 generated tokens, and returns all of them when it terminates. Waste per sequence is at most one partially filled block — a few tokens instead of thousands — and because every block is the same size, any free block satisfies any request, so external fragmentation cannot occur. ## Sharing, the second dividend Once a sequence's view of memory is an indirection table, two sequences can list the *same* physical block. Two use cases follow directly: - **Parallel sampling and beam search**: N candidates from one prompt share the prompt's blocks instead of holding N copies. When a shared block must diverge, the engine copies it first — copy-on-write, with a per-block reference count. - **Prefix caching**: many production prompts share a long fixed preamble — a system prompt, a tool catalogue, a retrieved document. If the tokens match, the blocks holding their KV state can be reused across requests, skipping that part of prefill entirely. vLLM exposes this as prefix caching; it cuts both time-to-first-token and prefill compute for prompt-heavy workloads. ## Operational consequences The headline effect is that a given GPU sustains a much larger running batch, which is precisely what continuous batching needs to pay off — the two features are complementary, not alternatives. vLLM sizes the block pool from the memory left after weights and activations, governed by `--gpu-memory-utilization`, and it will refuse to start if the remaining pool cannot hold even one sequence at `--max-model-len`. Under sustained overload the pool still fills. vLLM then **preempts** sequences: their blocks are freed and their state is either recomputed on readmission or swapped out. Preemption shows up in the server's metrics and as latency spikes, and it is the signal to lower max context, raise the memory fraction, quantize, or add capacity. Note also that paging removes *waste*, not *demand*: a workload that genuinely uses 128k-token contexts needs that memory regardless. TGI and other modern servers implement equivalent paged KV management, so the concept is portable across stacks rather than a vLLM-only trick.

  • How does paging enable prefix caching across different requests?
    Because a sequence addresses its cache through a block table, two requests whose prompts start with identical tokens can point at the same physical blocks. The server hashes block-aligned prefixes, finds an existing match, and reuses that KV state instead of recomputing prefill for the shared preamble. It cuts time-to-first-token sharply for workloads with long fixed system prompts or repeated documents, and reference counting keeps the blocks alive while anyone uses them.
  • What is the tradeoff in choosing the KV block size?
    Larger blocks mean fewer table entries and less gather overhead in the attention kernel, but more internal waste, since every sequence ends with one partially filled block. Smaller blocks waste less memory but add indirection cost and bookkeeping. Sixteen tokens is vLLM's default compromise; it is rarely worth tuning unless you are serving many extremely short sequences.
  • Does paged attention mean you can stop worrying about KV memory?
    No. It eliminates reserved-but-unused waste; it does not reduce the memory genuinely required by live tokens. A workload with long contexts and high concurrency will still exhaust the block pool, at which point the server preempts sequences and latency spikes. You still have to size VRAM against context length times expected concurrency.

It is the operating system's page table, moved inside the attention kernel: sequences get a logical view of their cache while the physical blocks live wherever there is room, and identical pages can be shared instead of copied.

saying these in an interview costs you the question

  • Thinking it compresses or quantizes the KV cache
  • Claiming it removes the need to size VRAM for context length
  • Believing the cache must still be contiguous per sequence
  • Confusing it with continuous batching — they are separate mechanisms
  • Assuming block sharing works across prompts that merely look similar

context