Core optimizations
Preemption, swap and recomputation
When KV memory runs out under load, the scheduler pauses some sequences. It either swaps their blocks to host memory or drops them and recomputes the prefill later.
Even with paging, the KV cache can outgrow GPU memory under heavy load. The scheduler then preempts some running sequences to free blocks. It has two options. It can swap the blocks to CPU memory and copy them back later. Or it can recompute: drop the blocks, and re-run the prefill when the sequence resumes.
Concept
Preemption means pausing a running request and taking its memory. Swap means moving its KV to a slower memory tier. Recomputation means rebuilding its KV from the prompt. Both cost something, paid in latency for the paused request.
The big idea
Picture a restaurant at full capacity. A diner is asked to wait. Their dishes can go in the fridge and come back when the table is free (swap). Or the kitchen can throw them out and cook the order again (recompute). Either way the diner waits longer, and the kitchen spends time on the same dish twice.
The picture breaks in two places. The fridge has limited space and a slow door, which is the PCIe link to host memory. Recooking uses kitchen time that could serve other tables.
Walking through a preemption
GPU KV pool: fully used
Request C needs 200 blocks, none free
Preempt request A, which holds 300 blocks and shares none:
swap: copy 300 blocks to CPU memory; copy back when A resumes
recompute: drop 300 blocks; re-run A's prefill when A resumes
Request C now runsPaging makes both choices finer-grained. The engine can move or drop single blocks, not whole per-request slabs.
Choosing between them
| Situation | Usually better | Why |
|---|---|---|
| Moderate prompt, GPU compute often idle under memory pressure | Recompute | Re-prefill is cheap, and it keeps GPU memory full of active KV |
| Very long context, such as 100k tokens or more, with a fast host path | Swap, or a multi-tier cache | Rebuilding would dominate the wait |
Trade-off
Recomputation pays in time to first token for the paused request. Swapping pays in host bandwidth and latency jitter for everyone sharing the link. See long-context serving for the tiered approach.
Common mistakes
- Treating preemption as free. A preempted request pays in time to first token or in stutters.
- Preempting shared blocks first. A shared block hurts every sequence that uses it. Prefer unique tails.
- Ignoring preemption counts. Constant preemption means the server is oversubscribed. Add capacity or lower concurrency.
Check yourself
- What does paging change about preemption? Answer: the engine can move or drop individual blocks, not whole per-request slabs.
- When is recomputation the better choice? Answer: when re-running the prefill is cheap compared with moving the blocks over the host link, which is typical for moderate prompts.
Key takeaways
- Under memory pressure, the scheduler pauses sequences to free KV blocks.
- Swap moves blocks to host memory. Recomputation rebuilds them from the prompt.
- Moderate prompts usually favor recomputation. Very long contexts often favor swapping or tiered caching.
- Count preemptions. Constant preemption is a capacity signal.
Further learning
Next, read copy-on-write sharing if you have not already. For the long-context version of this trade-off, see serving long contexts.
Related
PagedAttention and KV memory management
How reserving contiguous max-length KV wastes most of GPU memory, and how fixed-size KV blocks with block tables eliminate fragmentation and unlock sharing.
Copy-on-write KV sharing
Sequences that share a prompt can point at the same KV blocks. Only the block that diverges is copied, which makes parallel sampling and beam search cheap.
Serving long contexts
Million-token prompts: quadratic prefill attention, linear KV growth, context parallelism, offloading, sparse/hybrid architectures, and how providers price the pain.