Article

PagedAttention in Action: Why KV Cache Scheduling Matters More Than Compute for LLM Serving

A deep dive into KV Cache memory fragmentation, PagedAttention, continuous batching, and prefix caching — and how they boost throughput and reduce latency in LLM inference.

Background: The Bottleneck in Inference Services Is Often Not “Can the Model Compute?”

When optimizing LLM services for the first time, many people focus on GPU compute power, quantization, FlashAttention, or model size. But in real online services, a more easily underestimated problem is: requests have different lengths, end at different times, and KV Cache grows dynamically. If memory management is coarse, the number of requests that can fit on the GPU drops, throughput suffers, and queue times increase.

This is why vLLM and PagedAttention quickly became core technologies in mainstream inference engines. The PagedAttention paper points out that LLM services need to batch enough requests to achieve high throughput, but each request’s KV Cache is large, grows during generation, and is released upon completion. If managed poorly, memory fragmentation and redundant storage limit batch size. The paper further concludes that vLLM, through PagedAttention, achieves 2-4x throughput improvements over systems like FasterTransformer and Orca in test scenarios, while maintaining similar latency.

In other words, the key problem for LLM inference services isn’t “how to compute a single request as fast as possible,” but “how to stably co-locate many requests of varying lengths and lifetimes within limited GPU memory.”

Understanding KV Cache: Why It Eats Inference Throughput

Every time an autoregressive model generates a token, it needs to attend to all previous tokens. To avoid recomputing the Key and Value of historical tokens at each step, the inference engine caches them — this is the KV Cache.

After a request enters the model, it typically goes through two phases:

Prefill: Process the Input Context in One Shot

After the user provides a prompt, document, chat history, or tool results, the model must first pass these input tokens through the Transformer to generate the initial KV Cache. This phase is compute-heavy and resembles a “large matrix computation.”

Decode: Generate Tokens One by One

After the first token, the model generates one (or a few) tokens at a time, but each step requires reading the historical KV Cache. At this point, the task becomes memory-bound: the GPU isn’t limited by compute, but by constantly reading/writing KV Cache, scheduling requests, and moving data.

The problem is that different requests have different input lengths, output lengths, and end times. If the system pre-allocates a large contiguous block of VRAM for each request, short requests waste space, and long requests trigger expansion and fragmentation. The PagedAttention paper likens this to virtual memory management in operating systems: requests are like processes, tokens are like bytes, and KV Cache blocks are like pages.

The Core of PagedAttention: From “Contiguous Chunks” to “Paged Blocks”

Traditional approaches manage a request’s KV Cache as a contiguous tensor. This is simple to implement but unfriendly to dynamic requests:

  • The maximum request length is uncertain, so reserving too much is wasteful.
  • Actual output lengths vary; releasing short requests’ memory creates fragmentation.
  • In scenarios like beam search, parallel sampling, or shared system prompts, identical prefixes are hard to reuse.

PagedAttention divides each request’s KV Cache into fixed-size blocks. Logically, the request still sees a contiguous token sequence; physically, these blocks can be scattered across different VRAM locations, managed by a block table that maps logical to physical addresses.

This yields three direct benefits:

1. On-Demand Allocation, Reducing Internal Waste

Allocation happens as the request generates tokens. There’s no need to reserve full space upfront based on maximum length. This is especially important for chat, Agent, and RAG scenarios where output length is unpredictable.

2. Fixed Block Size, Mitigating External Fragmentation

All blocks are the same size. When released, they return to a pool for reuse by other requests. Compared to “large contiguous VRAM,” fixed-block management is closer to a memory pool, making it easier for the scheduler to determine how many more requests can fit.

3. Block-Level Sharing, Enabling Prefix Reuse

If multiple requests share the same prompt, system prompt, document beginning, or multi-turn chat history, they can reference the same KV blocks at the underlying level. Copy-on-write is used when modifications are needed, avoiding redundant storage.

This is why PagedAttention isn’t just an attention kernel optimization — it’s a server-side memory management problem.

Continuous Batching: Why Ordinary Batching Isn’t Enough

Many services initially batch multiple requests together to boost throughput. But ordinary batching has a classic problem: if one request in the batch is slow, it holds up the others; new requests must wait for the next batch.

The TensorRT-LLM documentation describes in-flight batching as continuous batching or iteration-level batching: context-phase sequences can be processed alongside generation-phase sequences, allowing better interleaving of requests, lower latency, and higher GPU utilization. It also emphasizes that input tensors must be packed for efficiency, avoiding padding single-token generation requests to the maximum input length.

This is critical for online systems, where real requests are a mix of lengths:

  • Some ask a single question.
  • Some bring tens of thousands of document tokens for RAG.
  • Some generate only 20 tokens and stop.
  • Some require long-form output with thousands of tokens.

The goal of continuous batching is for the scheduler to re-evaluate at each step: which requests are still decoding, which can enter prefill, which have finished and can release their KV Cache, and which new requests can be inserted in the gaps.

Chunked Prefill: Don’t Let Long Prompts Consume the Entire Budget

In long-context scenarios, prefill can easily consume the entire token budget in one shot, causing short requests to queue. The TensorRT-LLM documentation mentions chunked context, where the context is divided into chunks. This allows context chunks to be batched alongside generation-phase tokens, improving overall throughput and removing some input length constraints.

Here’s a practical breakdown:

StrategyBehaviorImpact
No chunkingLong document prefill consumes the entire budget → short requests waitTTFT jitter
With chunkingLong doc chunk 1 + short request decode → long doc chunk 2 + new request prefill → long doc chunk 3 + other request decodeMore stable TTFT

The benefit isn’t just throughput — it’s also tail latency stability. If your service has an SLA, such as a maximum TTFT threshold, chunked prefill is often more controllable than simply increasing max_num_tokens.

Prefix Caching: Don’t Recompute the Same Prefix

vLLM’s Automatic Prefix Caching (APC) caches the KV Cache of existing queries. If a new query shares the same prefix as a cached one, the corresponding KV Cache is reused, skipping computation for the shared part. The documentation lists typical benefit scenarios: users repeatedly querying the same long document, and multi-turn conversations repeatedly carrying the same chat history.

This optimization is particularly suitable for:

Long-Document Q&A

Users ask consecutive questions about the same contract, annual report, API documentation, or requirements document. The document content is identical, but the questions differ. Without prefix caching, the long document must be prefilled each time; with it, only the document’s KV Cache is reused.

Agent Fixed System Prompts

Many Agents have long system prompts, tool descriptions, output protocols, and safety rules. If these are identical each time, they serve as a stable prefix.

Multi-Turn Conversations

Multi-turn sessions repeatedly place historical messages into the context. Earlier turns are stable, while new messages are appended. As long as the request structure is stable, historical prefixes can be reused.

However, prefix caching isn’t free. It requires cache space, hit-rate statistics, eviction policies, and multi-tenant isolation. If request prefixes are frequently disrupted by timestamps, random IDs, or dynamic sorting fields, the hit rate will be poor.

SGLang’s RadixAttention: From “Same Prefix” to “Prefix Tree Reuse”

The RadixAttention proposed in the SGLang paper also revolves around KV Cache reuse. It organizes reusable KV Cache across multiple generation calls into a radix tree, which is well-suited for complex LLM programs, multi-turn interactions, few-shot learning, RAG, and JSON decoding. The paper’s abstract notes that SGLang’s runtime, including RadixAttention and compressed FSM, achieves up to 6.4x throughput improvements over contemporary systems in various language and multimodal tasks.

Compared to simple “exact full-prefix matching,” a radix tree provides structured prefix indexing:

Common system prompt
├── Tool description A
│   ├── User question 1
│   └── User question 2
└── Tool description B
    ├── User question 3
    └── User question 4

If your application uses fixed templates with dynamic variables, or if multiple tasks share a large amount of initial content, RadixAttention offers more potential than simply checking if the entire prompt is identical.

New Developments in 2026: PagedAttention Expands to More Hardware and Scheduling Strategies

PagedAttention was initially discussed primarily in the context of GPU serving engines, but recent work has extended it to TPUs, FlexAttention, and commodity GPU scheduling.

  • The Ragged Paged Attention paper (April 2026) targets TPU scenarios, proposing high-performance TPU attention kernels based on Pallas and Mosaic, and notes its integration as a foundation for vLLM and SGLang’s TPU backends.
  • PersistentKV (June 2026) focuses on page-aware decode scheduling, emphasizing that in long-context decoding, throughput is determined not just by individual attention kernels, but by how pages, KV-head groups, sequence splits, and workqueues are organized.

This indicates a trend: the competitive edge in LLM Serving is shifting from “having PagedAttention” to “co-designing KV Cache, scheduler, hardware backend, and prefix reuse.”

Engineering Implementation: Don’t Just Flip Switches — Build an Observability Loop

If you’re planning to implement PagedAttention, continuous batching, and prefix caching in your system, follow this order.

Step 1: Quantify Request Patterns

At a minimum, track these metrics:

Metric CategoryKey Metrics
Token distributionprompt_tokens p50 / p90 / p99, output_tokens p50 / p90 / p99
LatencyTTFT p50 / p90 / p99, TPOT p50 / p90 / p99
Concurrency & cancellationsConcurrent request count, request cancellation rate
CachePrefix cache hit rate
MemoryKV cache memory usage, GPU memory fragmentation or block pool utilization

Without these metrics, tuning becomes guesswork.

Step 2: Separate Prefill and Decode Pressure

  • If TTFT is high, it may be due to prefill queuing, long prompts blocking, or an improperly set batch token budget.
  • If TPOT is high, it may be due to decode-phase bandwidth, KV Cache reads, too few active sequences in the batch, or an unsuitable attention kernel.

Analyzing prefill and decode separately is far more effective than looking at “average response time” alone.

Step 3: Route Different Workloads to Different Pools

Don’t throw all requests into the same service pool. Split them by request pattern:

Pool TypeCharacteristics
Short Q&A poolLow latency, small context
Long document poolHigh token budget, chunked prefill
Agent poolHigh prefix reuse, enable prefix caching
Batch processing poolThroughput-first, relaxed latency

This is typically more stable than tuning a single global max_batch_size.

Step 4: Standardize Prompt Templates to Boost Cache Hits

To benefit from prefix caching, the application layer must also cooperate:

  • Fix the order of system prompts.
  • Avoid placing timestamps, traceIds, or random nonces at the prompt prefix.
  • Keep RAG document ordering stable.
  • Use fixed version numbers for tool descriptions; don’t concatenate different descriptions each time.
  • Place dynamic fields as late as possible.

Prefix caching isn’t a problem the model layer can solve alone — it requires prompt layout engineering at the application layer.

Step 5: Plan for Eviction and Isolation Policies

More caching doesn’t always mean better results. You must consider:

  • Whether multi-tenant cache sharing is allowed.
  • Whether KV Cache for sensitive documents needs isolation.
  • Whether cache eviction is LRU, cost-based, or hit-prediction-based.
  • Whether long-document caches will crowd out high-frequency short prefixes.
  • Whether all caches become invalid after model or tokenizer version changes.

Addressing these issues post-deployment is much harder.

Common Misconceptions

Misconception 1: Using vLLM Automatically Guarantees High Throughput

vLLM provides excellent underlying capabilities, but actual throughput depends on model size, request length distribution, batch token budget, VRAM size, concurrency, whether prefix caching is enabled, whether chunked prefill is used, and whether application-layer prompts are stable.

Misconception 2: A Larger max_batch_size Is Always Better

An excessively large batch parameter may increase throughput ceilings, but it can also increase queuing and time-to-first-token. The TensorRT-LLM documentation also emphasizes that max_num_tokens must balance throughput, GPU utilization, TTFT, and TPOT.

Misconception 3: Long-Context Problems Can Only Be Solved with More VRAM

More VRAM can alleviate the problem, but it can’t replace scheduling. What long-context scenarios truly need are: paged KV Cache, chunked prefill, prefix caching, proper queue isolation, and monitoring.

Misconception 4: Prefix Caching Automatically Solves All Redundant Computation

If dynamic fields are inserted at the beginning of the prompt each time, cache hits will be broken. The application layer must stabilize the prompt structure for the underlying cache to be effective.

Go-Live Checklist

  • Measured prompt/output token distribution
  • Separated TTFT and TPOT metrics
  • Monitored KV Cache block utilization
  • Enabled or evaluated PagedAttention / paged KV cache
  • Evaluated chunked prefill for long-context scenarios
  • Evaluated prefix caching for multi-turn conversations and long documents
  • Checked prompt prefix stability
  • Set up different business pools or routing strategies
  • Stress-tested p50/p90/p99, not just averages
  • Defined cache eviction, tenant isolation, and model upgrade invalidation strategies

Conclusion: LLM Service Optimization Is a System Engineering Problem of “Compute + Memory + Scheduling”

The value of PagedAttention isn’t a single trick; it’s that it makes the KV Cache management problem in LLM services explicit. Continuous batching addresses the dynamic entry and exit of requests, chunked prefill addresses long prompts monopolizing the budget, and prefix caching and RadixAttention address redundant context computation.

When a business enters the stage of multi-turn conversations, long-document Q&A, Agent tool calls, and high-concurrency serving, what truly determines cost and experience is often not “a slightly smaller model” or “a slightly more powerful GPU,” but whether the inference engine can organize KV Cache, request scheduling, cache reuse, and hardware backends into a stable system.

If treated as just a parameter toggle, the benefits will be limited. If treated as part of the service architecture, technologies like PagedAttention will truly deliver their value.

Key References

FAQ

What problem does PagedAttention primarily solve?
It solves memory fragmentation and pre-allocation waste caused by the dynamic growth of KV Cache during LLM inference, allowing more requests to reside on the GPU simultaneously.
What is the difference between continuous batching and ordinary batching?
Ordinary batching advances all requests in a batch together, while continuous batching dynamically adds new requests at each decoding step, interleaving prefill and decode at a finer granularity.
Is prefix caching suitable for all use cases?
No. It is best for scenarios with obvious shared prefixes, such as system prompts, long-document Q&A, and multi-turn conversations. If requests have almost no common prefix, the benefits are limited.