AI Fundamentals

PagedAttention: How LLM Servers Stop KV Cache From Wasting Memory

AI Foundations #30 explains why variable-length KV caches fragment GPU memory, how paging divides cache into blocks, how logical token positions map to physical memory, and why this helps serving throughput.

Approximately 5 min read · AI Foundations / Lesson 30

In AI Foundations #29, we saw that a server can reuse KV state when requests share the same token prefix.

But there is a more basic memory problem:

Where should all of those KV-cache tokens live in GPU memory?

Different requests have different prompt lengths, generate different numbers of tokens and finish at different times.

If the server reserves one large contiguous region for every request, memory can be wasted badly.

PagedAttention was designed to attack that problem.

First remember what KV cache stores

During generation, a transformer repeatedly attends to earlier tokens.

Instead of recomputing key and value tensors for every previous token on every decode step, the runtime stores them in the KV cache.

A simplified request might look like:

token 1 -> KV
token 2 -> KV
token 3 -> KV
...
token N -> KV

As the sequence grows, the cache grows.

With many simultaneous requests, KV cache can consume a large fraction of available accelerator memory.

The problem with reserving one big continuous region

Suppose a server admits three requests.

It does not know in advance exactly how long each output will become.

A simple allocator could reserve:

A: [....................]
B: [....................]
C: [....................]

Each region has enough room for the maximum possible sequence length.

If A stops after 200 tokens but its reservation allowed 4,000, much of that reservation sits unused.

Another problem appears when free memory exists but is broken into separated gaps.

For example:

used | free | used | free | used | free

The total free memory might be large, yet there may be no single contiguous region large enough for a new request.

That is fragmentation.

Paging changes the unit of allocation

PagedAttention borrows an idea familiar from operating systems: divide memory into fixed-size blocks instead of requiring one giant continuous region.

A sequence is represented logically as a continuous stream of tokens:

0 1 2 3 4 5 6 7 8 9 ...

But its KV data can be stored physically in blocks scattered through available memory:

logical block 0 -> physical block 12
logical block 1 -> physical block 4
logical block 2 -> physical block 31

The model still needs the correct K and V tensors for each token.

The runtime keeps a mapping from logical blocks to physical blocks so the attention kernel can find them.

Why this reduces waste

Instead of reserving the maximum sequence length up front, the runtime can allocate another block only when the sequence actually grows into it.

Imagine blocks that hold four token positions.

A seven-token sequence needs two blocks:

block A: tokens 0 1 2 3
block B: tokens 4 5 6 _

Only the unused portion of the final block is wasted.

That is much smaller than reserving thousands of unused token slots for every active request.

Physical blocks do not need to be adjacent

This is the crucial point.

A request might use:

physical block 8
physical block 19
physical block 3

Those blocks are not adjacent in device memory.

The logical block table makes them appear like one continuous sequence to the attention operation.

That means freed blocks can be reused by other requests without first compacting all active KV data into a single continuous region.

Requests can grow one block at a time

Generation is naturally incremental:

prompt
-> next token
-> next token
-> next token
...

Paged allocation fits that behavior.

As a request grows, the server assigns additional KV blocks.

When the request finishes, those blocks can return to the free pool.

This makes memory management more compatible with continuous batching, where requests enter and leave the active batch at different times.

These ideas are easy to mix up.

PagedAttention answers:

How do we store KV-cache memory efficiently?

Prefix caching answers:

Can another request reuse KV state that was already computed?

A server can use block-based KV memory without sharing prefixes.

It can also use a paged block system as the storage mechanism that makes shared prefix blocks easier to reference.

So the ideas complement each other, but they solve different problems.

Paging does not make KV cache free

The cache still consumes memory.

A longer context still means more key/value state.

Larger batch concurrency still means more live cache.

Higher-precision KV formats still require more bytes per token than lower-precision formats.

Paging mainly improves allocation efficiency and reduces fragmentation. It does not remove the underlying memory cost.

There is also bookkeeping

The server now needs data structures that track:

That indirection has implementation cost.

PagedAttention is useful because the memory savings and higher serving utilization can outweigh that cost for real multi-request workloads.

A simple mental model

Think of a hotel.

The bad strategy is:

Every guest reserves an entire floor because we do not know how many rooms they might eventually need.

The paged strategy is:

Give each guest a room now and assign more rooms as needed. The rooms can be on different floors because the front desk tracks the mapping.

The guest’s stay is logically continuous even though the physical rooms are not.

Why this matters to LLM serving

An LLM server often has two scarce resources:

  1. GPU compute;
  2. GPU memory.

Continuous batching helps use compute more effectively.

Paged KV management helps use memory more effectively.

If memory is wasted, the server may admit fewer concurrent requests even when the GPU has compute capacity left.

That is why PagedAttention became an important serving-system idea: it changes KV cache from a large per-request reservation problem into a block-allocation problem.

What comes next

Once KV state is divided into manageable blocks, the server can make more sophisticated decisions about:

The next layers of serving systems are built on top of this basic observation:

Tokens are logically ordered, but their cache does not have to occupy one continuous physical region.

Sources and further reading

Continue reading