vLLMPagedAttention

SOSP '23 arXiv: 2309.06180 Foundational Paper

Woosuk Kwon, Zhuohan Li, Siyuan Zhuang, Ying Sheng, Lianmin Zheng, Cody Hao Yu, Joseph E. Gonzalez, Hao Zhang, Ion Stoica

UC Berkeley

Overview

vLLM introduces PagedAttention, an attention algorithm inspired by operating-system virtual memory and paging, that fundamentally changes how LLM serving systems manage the key-value (KV) cache. By dividing the KV cache into fixed-size blocks that need not be contiguous in physical GPU memory, PagedAttention achieves near-zero memory waste and enables flexible memory sharing across sequences—delivering 2–4× throughput improvements over prior systems such as FasterTransformer and Orca.

The Core Insight

KV cache has OS-process-like behavior: it grows dynamically, has an unknown lifetime, and can be shared. Treat it like virtual memory with paging.

Key Innovation

PagedAttention: an attention kernel that operates on non-contiguous KV blocks mapped via a block table—exactly like page tables map virtual to physical memory.

Practical Impact

Near-zero waste (96.3% effective utilization vs. 20.4% in prior systems), enabling far larger batches and higher throughput.

i
Why This Paper Matters: Every major LLM serving system built after 2023—from SGLang to TensorRT-LLM to Mooncake—adopts or extends PagedAttention's core ideas. This paper is the foundational reference for all KV cache management research in the agentic AI survey.
Figure 1: Memory layout when serving a 13B-parameter LLM on NVIDIA A100
Figure 1 (from paper): Left: Memory layout when serving an LLM with 13B parameters on NVIDIA A100. The parameters (gray) persist in GPU memory throughout serving. The KV cache memory (red) is (de)allocated per serving request. Right: vLLM smooths out the rapid growth curve of KV cache memory seen in existing systems, leading to a notable boost in serving throughput.

The Memory Problem

In autoregressive LLM serving, each request requires storing key and value tensors for every previously generated token—the KV cache. For a 13B-parameter model (OPT-13B), a single token's KV cache occupies 800 KB (2 × 5120 × 40 × 2 bytes), and a full 2048-token sequence can require up to 1.6 GB. On an A100 GPU with 40GB RAM, roughly 65% goes to model parameters, leaving only ~30% for KV cache—the critical bottleneck.

Figure 2: Average percentage of memory wastes in different LLM serving systems
Figure 2 (from paper): Average percentage of memory wastes in different LLM serving systems during the basic sampling experiment.
Figure 3: KV cache memory management in existing systems
Figure 3 (from paper): KV cache memory management in existing systems. Three types of memory wastes -- reserved, internal fragmentation, and external fragmentation -- prevent other requests from fitting into the memory.

Three Sources of Memory Waste

Memory Waste in Existing LLM Serving Systems
Request A — max length 2048, actual length 10
Four
score
and
seven
years
ago
our
fathers
brought
forth
rsv
rsv
← 2038 slots: Internal Fragmentation →
External Fragmentation — unusable gaps between pre-allocated chunks
Request B — max length 512, actual length 5
You
only
live
once
!
rsv
← 507 slots: Internal Fragmentation →
Pre-allocation forces reserving max-length contiguous blocks. Actual usage can be as low as 20.4%.

Measured Memory Waste (OPT-13B on ShareGPT)

Token states
20.4%
Reservation
13.3%
Internal frag.
57.3%
External frag.
8.9%

Orca (Max): only 20.4% of KV cache memory stores actual token states. vLLM achieves 96.3%.

System Architecture

vLLM adopts a centralized scheduler with distributed GPU workers. The KV Cache Manager manages memory via block tables, analogous to how an OS kernel manages page tables. GPU workers only see physical block IDs—they never need to reason about memory sharing or fragmentation.

Figure 4: vLLM system overview
Figure 4 (from paper): vLLM system overview with centralized scheduler and distributed GPU workers connected through a KV cache manager.
vLLM System Architecture
Centralized Scheduler
FCFS policy + preemption
KV Cache Manager
Block tables + ref counts
CPU Block
Allocator
GPU Block
Allocator
Block Tables
Logical
Physical
#Filled
0
7
4
1
1
3
2
3
1
Control messages
Block IDs + tokens
Distributed GPU Workers
W0
Cache Engine
Model Shard 0
W1
Cache Engine
Model Shard 1
W2
Cache Engine
Model Shard 2
All-reduce sync via NCCL — SPMD execution
The scheduler broadcasts block tables to workers. Workers only see physical block IDs—sharing is transparent.
Virtual Memory Analogy: OS Paging ↔ PagedAttention
Operating System
Process
owns
Virtual Pages
mapped via Page Table to
Physical Frames (RAM)
unit = fixed-size page (4KB)
PagedAttention (vLLM)
Request / Sequence
owns
Logical KV Blocks
mapped via Block Table to
Physical KV Blocks (GPU DRAM)
unit = B tokens' KV vectors (B=16)
OS paging maps virtual-to-physical pages for processes; PagedAttention maps logical-to-physical KV blocks for sequences.

PagedAttention Algorithm

Traditional attention requires keys and values stored contiguously in memory. PagedAttention partitions the KV cache into blocks, each holding B tokens' keys and values. During attention computation, the kernel fetches blocks from non-contiguous physical locations via the block table, computing attention block-by-block.

PagedAttention: Block-wise Attention Computation
Query vector
qi ("forth")
attends to
Physical Block 7
Four
score
and
seven
Logical Block 0
Physical Block 1
years
ago
our
fathers
Logical Block 1
Physical Block 3
brought
forth
--
--
Logical Block 2 (partial)
Blocks reside at non-contiguous physical addresses (7, 1, 3). The kernel fetches each block separately, computes partial attention scores, then combines results.

Mathematical Formulation

For a sequence of length i, denote the key block Kj = (k(j-1)B+1, ..., kjB) and value block Vj similarly. The attention computation becomes block-wise:

// Block-wise attention computation (Equation 4 from the paper)
// B = block size (default 16 tokens per block)

// For each key block j = 1 ... ceil(i/B):
Aij = exp(qiT Kj / sqrt(d)) / sum(exp(qiT Kt 1 / sqrt(d)), t=1..ceil(i/B))

// Final output is weighted sum over value blocks:
oi = sum(Vj AijT, j=1..ceil(i/B))

// Key difference: kernel identifies and fetches DIFFERENT KV blocks
// from non-contiguous physical memory using the block table.
// Each GPU warp processes one block for coalesced memory access.

Key Mechanisms

1. Block-Level Memory Management

Dynamic Allocation on Demand

Unlike prior systems that pre-allocate max-length contiguous chunks, vLLM allocates physical blocks one at a time as the sequence grows. A new physical block is only allocated when the last logical block is full. This limits internal fragmentation to at most one block per sequence (the last, partially filled block).

Dynamic Block Allocation During Decoding
1
Prefill: 7 prompt tokens arrive
Four
score
and
seven
|
years
ago
our
1 slot free
Allocates 2 physical blocks (Block 7, Block 1). Block 1 has 1 empty slot.
2
Decode step 1: generate "fathers"
Block 1 has 1 free slot → store "fathers" there. No new block needed. Update #filled: 3 → 4.
3
Decode step 2: generate "brought"
Block 1 is now full → allocate new physical block 3. Store "brought" in Block 3, slot 0. Block table grows: logical block 2 → physical block 3, #filled = 1.
Blocks are allocated lazily, one at a time. Memory waste is limited to the last block's unfilled slots.

2. Copy-on-Write (CoW)

Shared Blocks with Reference Counting

When multiple sequences share a common prefix (parallel sampling, beam search), their logical blocks map to the same physical blocks with reference counts > 1. A copy is only triggered when a sequence tries to write to a shared block. This is identical to OS copy-on-write for forked processes.

Copy-on-Write in Parallel Sampling
Sample A1
L0 → P7
L1 → P1
shared
ref_count = 2
Sample A2
L0 → P7
L1 → P1
A1 writes to Block 1 (ref_count > 1) → triggers Copy-on-Write
Sample A1 (after CoW)
L0 → P7
L1 → P3 (new!)
Sample A2 (unchanged)
L0 → P7
L1 → P1
P7 is still shared (ref_count=1 for each now). P1's data was copied to new P3 for A1. Only one block of data is copied.

3. FCFS Scheduling with Preemption

First-Come-First-Served

vLLM serves requests in arrival order to prevent starvation. When GPU memory is exhausted, the latest request is preempted first (preserving fairness for earlier arrivals).

Recovery: Swap or Recompute

Swapping: Evict KV blocks to CPU RAM via PCIe (all-or-nothing per sequence). Recomputation: Discard blocks and regenerate KV cache from the prompt + generated tokens. Better for small block sizes.

4. Application to Decoding Scenarios

Parallel Sampling

Multiple samples from one prompt share the prompt's KV blocks. Only divergent generation blocks are separate. Saves 6–10% memory.

Beam Search

Beam candidates share blocks dynamically as the tree evolves. Candidates that are pruned have their blocks freed immediately. Saves 37–55% memory.

Shared Prefix

System prompts and few-shot examples are cached as shared physical blocks. New requests map their logical blocks to these cached blocks (copy-on-write protected). Achieves up to 3.6× higher throughput.

Block Sharing in Beam Search (k=4)
Candidate 0
B0
B1
B3
B6
B9
Candidate 1
B0
B1
B3
B6
B10
Candidate 2
B0
B7
B8
B6
B11
Candidate 3
B0
B7
B8
B6
B12
B0: shared by all 4
B1/B7: shared by pairs
B6: shared by all 4
B9-12: unique per candidate
Block sharing evolves dynamically as beam candidates diverge and merge. Pruned candidates' blocks are freed immediately.

Code Concepts

vLLM is implemented in ~8.5K lines of Python and ~2K lines of C++/CUDA. The block manager and scheduler are in Python; the PagedAttention kernel and block operations are fused CUDA kernels.

Block Table Data Structure

# Conceptual block table structure (per sequence)
# Maps logical block index -> (physical_block_id, num_filled, ref_count)

class BlockTable:
    """Maps logical KV blocks to physical blocks in GPU DRAM."""

    def __init__(self, block_size: int = 16):
        self.block_size = block_size  # tokens per block (B)
        self.entries: List[BlockEntry] = []

    def append_token(self, allocator: BlockAllocator):
        """Called for each new token generated."""
        if not self.entries or self.entries[-1].is_full():
            # Last block is full -> allocate a NEW physical block
            phys_block = allocator.allocate()
            self.entries.append(BlockEntry(phys_block, num_filled=1))
        else:
            # Room in current block -> just increment counter
            self.entries[-1].num_filled += 1

    def get_physical_ids(self) -> List[int]:
        """Returns physical block IDs for the attention kernel."""
        return [e.physical_block_id for e in self.entries]

Copy-on-Write Logic

# When a sequence writes to a block shared by others (ref_count > 1)

def cow_write(block_entry: BlockEntry, allocator: BlockAllocator):
    """Copy-on-write: fork a new physical block if shared."""
    if block_entry.ref_count > 1:
        # Shared block -> copy to new physical block
        old_phys = block_entry.physical_block_id
        new_phys = allocator.allocate()
        cuda_block_copy(src=old_phys, dst=new_phys)  # fused kernel
        block_entry.physical_block_id = new_phys
        block_entry.ref_count = 1
        allocator.dec_ref(old_phys)  # decrement old block's ref
    # ref_count == 1 -> write directly, no copy needed

Fused CUDA Kernels

// Conceptual structure of the PagedAttention CUDA kernel
// Three fused operations minimize kernel launch overhead:

// 1. Fused Reshape & Block Write
//    New KV cache is split into blocks, reshaped for coalesced reads,
//    and saved at positions specified by the block table.

// 2. Fused Block Read & Attention
//    Each GPU warp reads one KV block from the block table,
//    computes partial attention scores, then reduces across warps.
__global__ void paged_attention_kernel(
    const float* query,           // [num_heads, head_dim]
    const float* key_cache,       // [num_blocks, block_size, num_heads, head_dim]
    const float* value_cache,     // [num_blocks, block_size, num_heads, head_dim]
    const int*   block_tables,    // [num_seqs, max_blocks_per_seq]
    const int*   seq_lens,        // [num_seqs]
    float*       output           // [num_seqs, num_heads, head_dim]
) {
    // Each warp handles one KV block for one attention head
    int block_idx = block_tables[seq_id * max_blocks + logical_block];
    // Fetch K, V from physical block_idx (non-contiguous!)
    // Compute softmax(Q * K^T / sqrt(d)) * V per block
    // Reduce partial results across blocks
}

// 3. Fused Block Copy (for CoW)
//    Batches all pending copy operations into a single kernel launch.

Three Core API Methods

# vLLM supports all decoding via three sequence operations:

fork(seq)    # Create new sequence sharing parent's KV blocks
              # Used for: parallel sampling, beam search branching

append(seq)  # Add new token to sequence (may allocate block or trigger CoW)
              # Used for: every autoregressive decode step

free(seq)    # Release sequence's blocks (decrement ref counts)
              # Used for: completed sequences, pruned beam candidates

Performance Results

Throughput Improvement (Basic Sampling)

Configuration vs. FasterTransformer vs. Orca (Oracle) vs. Orca (Max)
OPT-13B, 1 GPU, ShareGPT up to 22× 1.7–2.7× 2.2–4.3×
OPT-66B, 4 GPUs, ShareGPT -- 1.7–2.7× 2.7–8×
OPT-175B, 8 GPUs, ShareGPT -- 1.7–2.7× 2.7–8×
OPT-13B, 1 GPU, Alpaca up to 22× 1.7–2.7× 2.2–4.3×

Memory Utilization

KV Cache Effective Utilization Comparison
vLLM
96.3%
Orca (Oracle)
38.2%
Orca (Pow2)
26.8%
Orca (Max)
20.4%

Memory Savings from Sharing

Parallel Sampling

# Output SequencesMemory Saved
26.1%
48.5%
69.8%

Beam Search

Beam WidthMemory Saved
237.6%
453.1%
655.2%

Batch Size Improvement

+
On OPT-13B with ShareGPT, vLLM batches 30.42 requests simultaneously on average, compared to 7.00 for Orca (Max) and 13.62 for Orca (Oracle)—a 2.2× and 4.3× improvement respectively. Higher batch sizes directly translate to better GPU utilization and higher throughput.

Kernel Overhead

Attention Kernel Latency: 20–26% Higher

The PagedAttention kernel is 20–26% slower than FasterTransformer's contiguous attention kernel due to block table lookups, extra branches, and handling variable sequence lengths. However, this only affects the attention operator, not other layers (FFN, LayerNorm, etc.). The end-to-end throughput improvement from better memory utilization far outweighs this overhead.

Block Size Selection

Block SizeShareGPT Perf.Alpaca Perf.Trade-off
1–4PoorPoorToo small: low GPU parallelism, high table overhead
16 (default)OptimalOptimalBest balance of utilization vs. internal fragmentation
32–128GoodDegradedLarger blocks increase internal frag for short sequences
256DegradedPoorToo large: approaches contiguous allocation behavior

Limitations for Agentic AI Systems

While PagedAttention is foundational, its design targets single-request serving within a single session. Modern agentic AI systems expose several gaps:

1. No Cross-Request KV Reuse

PagedAttention shares KV blocks within a single request (parallel sampling, beam search) but has no mechanism to cache and reuse blocks across different requests. Every new request recomputes its KV cache from scratch. Agentic systems that issue many requests with shared prefixes (system prompts, tool schemas) waste significant computation.

Addressed later by: SGLang RadixAttention, Mooncake KVCache-centric disaggregation

2. Basic Eviction Policy

vLLM uses an all-or-nothing eviction policy: when memory is exhausted, it evicts all blocks of a sequence. There is no LRU/LFU cache with partial eviction or intelligent retention of high-value prefixes. This limits performance for workloads with temporal locality (e.g., multi-turn agents revisiting contexts).

Addressed later by: CacheBlend, Epic, InfiniGen

3. Single-Node Focus

While vLLM supports tensor-parallel execution across GPUs within a node, the KV cache manager is centralized. There is no cross-node KV cache transfer, disaggregated storage, or distributed caching tier. This limits scalability for cloud-scale agentic deployments.

Addressed later by: Mooncake, MemServe, InfiniStore

4. No Semantic-Aware Scheduling

FCFS scheduling treats all requests equally regardless of their KV cache sharing potential. An agentic scheduler could co-locate requests with shared prefixes, reorder to maximize cache hits, or prioritize high-value continuations.

Addressed later by: SGLang, CachedAttention, MemServe

5. Full-Precision KV Storage Only

All KV blocks are stored at full precision (FP16). There is no compression, quantization, or selective retention. For long-context agentic interactions (100K+ tokens), this becomes prohibitively expensive.

Addressed later by: CacheGen, KIVI, Gear, DMC

6. No Prefill/Decode Disaggregation

Prefill (compute-bound) and decode (memory-bound) phases run on the same GPUs, creating resource contention. Agentic systems with variable-length inputs could benefit from separating these phases onto specialized hardware.

Addressed later by: Splitwise, DistServe, Mooncake

!
Key gap for agentic AI: PagedAttention solves memory waste within a single serving session, but does not address memory reuse across the multi-turn, multi-request workflows that define agentic systems. This is the central problem that every subsequent paper in this survey builds upon.

Impact & Legacy

PagedAttention is arguably the single most influential systems contribution to LLM serving. Its impact extends across three dimensions:

PagedAttention's Influence on Subsequent KV Cache Systems
PagedAttention (vLLM)
Block-level KV cache with virtual memory
Cross-Request Caching
Reuse KV blocks across requests
SGLang / RadixAttention
CachedAttention
ChunkAttention
KV Cache Compression
Reduce per-token storage
CacheGen / CacheBlend
KIVI / Gear / KVCACHE-Q
DMC (Dynamic Memory Compression)
Disaggregated Serving
Separate compute & memory tiers
Mooncake
Splitwise / DistServe
MemServe / InfiniStore
Every major KV cache innovation since 2023 builds on or extends PagedAttention's block-based memory model.

Why PagedAttention is Foundational

Conceptual Breakthrough

The insight that KV cache management is isomorphic to OS virtual memory management unlocked decades of systems research (paging, CoW, swapping, reference counting) for direct application to GPU memory in LLM serving. This analogy continues to guide new research.

Universal Adoption

By March 2026, vLLM has 48K+ GitHub stars and is the default serving engine for most LLM deployments. Its block-based KV cache design has been adopted by TensorRT-LLM, SGLang, and virtually every production serving system.

Abstraction Layer

The block table abstraction cleanly separates the logical view (sequences see contiguous KV) from the physical view (GPU DRAM uses scattered blocks). This separation enables all downstream innovations: caching, compression, disaggregation, and scheduling can all operate at the block level without changing the attention computation.

Enabling the Agentic Survey

This survey exists because PagedAttention created the block-based vocabulary that all subsequent papers use. "KV blocks," "block tables," "physical vs. logical," "reference counting," "copy-on-write"—these concepts, introduced here, are the shared language of every paper analyzed in this survey.

Key Numbers at a Glance

2-4x
Throughput gain
96.3%
Memory utilization
~0%
External frag.
55%
Beam search savings
16
Default block size

Repository & Resources

Source Code

github.com/vllm-project/vllm

8.5K lines of Python + 2K lines of C++/CUDA (original paper). Supports OPT, GPT, LLaMA, and many other model architectures. Built on FastAPI frontend with OpenAI-compatible API.

Paper

arXiv:2309.06180

Published at SOSP '23 (Symposium on Operating Systems Principles), October 23–26, 2023, Koblenz, Germany. ACM ISBN 979-8-4007-0229-7/23/10.

Implementation Summary

Component Language Description
Frontend Python (FastAPI) OpenAI-compatible API; user-configurable sampling params and beam width
Scheduler Python Centralized FCFS scheduler with preemption; manages block tables
Block Manager Python CPU + GPU block allocators; reference counting; CoW tracking
PagedAttention Kernel C++/CUDA Fused reshape-write, block-read-attention, and block-copy kernels
Model Executor Python (PyTorch) Supports GPT, OPT, LLaMA; uses NCCL for tensor parallelism
Distributed Python + NCCL Megatron-LM style SPMD tensor parallelism across GPUs
i
Survey Context: This page analyzes the foundational paper that introduced block-based KV cache management. Every subsequent paper in the agentic AI survey—from SGLang's RadixAttention to Mooncake's disaggregated architecture to CacheGen's compression codecs—builds on, extends, or reacts to the concepts introduced here. Understanding PagedAttention is a prerequisite for understanding the entire field.