VerifiedarXiv:2309.0618028 min
LLMs · Systems

Efficient Memory Management for Large Language Model Serving with PagedAttention

Storing the KV cache in fixed-size blocks, like operating-system pages, raises LLM serving throughput 2-4×.

A language-model server can run only as many requests at once as their key-value caches fit in GPU memory. Earlier servers reserved one contiguous region per request, sized for the longest possible output, and real tokens filled 20-38% of it. vLLM hands out 16-token blocks as a request grows and looks them up through a per-request table, and real tokens fill 96%.

Explaining the paperEfficient Memory Management for Large Language Model Serving with PagedAttentionKwon, Li, Zhuang, Sheng, Zheng, Yu, Gonzalez, Zhang, Stoica · UC Berkeley · SOSP 2023 · arXiv:2309.06180 ↗

The SOSP 2023 paper that introduced vLLM: an attention kernel that reads keys and values from scattered memory blocks, and the block manager, copy-on-write sharing and preemption policy built around it.

A language model generates text one token at a time, and every new token attends to all the tokens before it. To avoid recomputing the past at every step, the server keeps each request's attention keys and values in GPU memory, the KV cache. Woosuk Kwon and eight coauthors, most of them at UC Berkeley, measured how existing servers lay that cache out, found that most of the reserved memory held nothing, and rebuilt the allocator on the virtual-memory design operating systems have used since the 1960s. Their server, vLLM, sustains 2-4× the request rate of FasterTransformer and Orca at the same latency, without changing model accuracy.

The page follows the paper's order. It starts with why KV memory, not arithmetic, caps a server's throughput, using the numbers for OPT-13B on one A100. It then covers the three ways the contiguous layout wastes that memory, the paged layout that removes them, and the attention kernel that reads the paged layout, and ends with sharing between sequences, preemption, and the measured results.

Why the KV cache limits LLM serving throughput

In Transformer attention, each position ii projects its hidden state xix_i into a query, a key and a value:

qi=Wq xi,ki=Wk xi,vi=Wv xiq_i = W_q\,x_i, \qquad k_i = W_k\,x_i, \qquad v_i = W_v\,x_i
(2)

The output at position ii is a weighted average of the values at positions 1 through ii, with weights from a softmax over the query-key dot products, each divided by d\sqrt{d}, where dd is the dimension of a key vector:

aij=exp⁡ ⁣(qi⊤kj/d)∑t=1iexp⁡ ⁣(qi⊤kt/d),oi=∑j=1iaij vja_{ij} = \frac{\exp\!\big(q_i^\top k_j / \sqrt{d}\big)}{\sum_{t=1}^{i} \exp\!\big(q_i^\top k_t / \sqrt{d}\big)}, \qquad o_i = \sum_{j=1}^{i} a_{ij}\, v_j
(3)

Both sums run to ii, so producing token i+1i+1 needs every earlier key and value. A server handles a request in two phases. The prompt phase (also called prefill) runs the whole prompt through the model at once, computes the keys and values of all prompt tokens with large matrix-matrix multiplies, and produces the first output token. The generation phase then runs one step per output token: it computes the new token's query, key and value, appends the key and value to the cache, and evaluates (3) against the whole cache. Each step multiplies a single vector by every weight matrix, so the GPU spends most of the step reading weights from memory and little of it computing.

Batching fixes the arithmetic side: one read of the weights serves every request in the batch. The batch size is then limited by memory, because each request brings its own cache. For OPT-13B the cache of one token is

2⏟K, V×5120⏟hidden size×40⏟layers×2 bytes⏟FP16=819,200 bytes≈800 KB\underbrace{2}_{K,\,V} \times \underbrace{5120}_{\text{hidden size}} \times \underbrace{40}_{\text{layers}} \times \underbrace{2\ \text{bytes}}_{\text{FP16}} = 819{,}200\ \text{bytes} \approx 800\ \text{KB}

and OPT generates up to 2048 tokens, so one request's cache can reach 1.6 GB. On an A100 with 40 GB, the 13 billion FP16 weights take 26 GB, and the paper leaves 12 GB for KV caches (Table 1), which it counts as 15.7K token slots. The rest holds activations, the temporary tensors of the current step.

If the server reserves 2048 slots for every request, 15.7K slots hold 7 requests (15,700 / 2048 = 7.7). The paper's Orca (Max) baseline does exactly that, and its measured average batch is 7.00 requests on both of its datasets. ShareGPT conversations average 161 prompt tokens and 338 output tokens, 499 in total. A server that gives each request only the 16-token blocks it fills (499 tokens round up to 32 blocks, 512 slots) fits 15,700 / 512 ≈ 30 such requests. vLLM's measured average batch on ShareGPT is 30.42. The estimate ignores that running requests are partway through their outputs and that the batch also depends on how fast requests arrive, so read the match as rough.

Figure 1 · how many requests fit
499 tok
The 12 GB of KV memory on an A100-40GB serving OPT-13B, drawn as 983 cells of 16 tokens each. Every request gets a run of cells in alternating teal shades; amber cells are reserved by a request but hold no tokens. Drag the request length, or pick a dataset's average, and switch between reserving 2048 tokens per request and allocating blocks as the request grows.

With 2048-token reservations, the count stays at 7 for every request length, and at the ShareGPT average 76% of each reservation is empty. With blocks, the count rises from 7 at 2048 tokens to 30 at the ShareGPT average and 196 at the Alpaca average of 78 tokens.

Another way to fit more requests is to make each token's cache smaller. Multi-query attention gives all query heads one shared key and value, and grouped-query attention, used in Mistral 7B, shares one per group of heads. Those change the model; PagedAttention changes only how the server stores the cache, and works with any of them.

Where contiguous allocation wastes memory

Before vLLM, servers such as FasterTransformer and Orca stored each request's KV cache as one contiguous tensor, because deep-learning frameworks expect a tensor to occupy one contiguous range of memory. The final length of a request is unknown when it arrives, so the server reserves a range sized for the longest it might get, up to the model's limit of 2048 tokens (Sec. 3.1). The paper counts three kinds of waste in that design:

The paper's three Orca baselines differ only in how much they reserve. Orca (Max) reserves the full 2048 tokens. Orca (Pow2) reserves at most twice the true output length (an output of 25 tokens gets 32 slots). Orca (Oracle) knows the true output length in advance and reserves exactly that; no real server can, so it is an upper bound on what length prediction could achieve.

Figure 2 · the four kinds of slot, live
3
8 tok
6 tok
A pool of 64 KV slots shared by two requests. Slots hold token states, reservation (filled later), internal fragmentation (never filled) or allocator waste. Pick a policy, then drag the decode step and the two final output lengths. The readout gives the share of occupied memory that holds token states and how many slots are left for a third request.

Under Max, the two requests take the whole pool even when their outputs are short, and most of it is internal fragmentation that stays unused until they finish. Oracle removes internal fragmentation, but at step 0 nearly all of its reservation is still empty, and the buddy allocator's rounding still costs slots. Under the paged policy, each request holds at most one partly filled block, and the rest of the pool stays free.

The paper measured this breakdown for the three baselines and vLLM during its main experiment (Sec. 6.2, Fig. 2):

Figure 3 · KV memory usage measured in the paper
The paper's Fig. 2, redrawn with its printed values. Each bar is 100% of the KV memory a system had allocated, split into token states, reservation, internal fragmentation, and external fragmentation and other. For vLLM the paper prints only the 96.3%; the remaining 3.7% is a mix it does not break down. Hover or tap a segment for its value.

Orca (Max) stores token states in 20.4% of its KV memory, and 57.3% is internal fragmentation. Pow2 cuts internal fragmentation to 13.6% but loses 41.6% to the allocator. Even Oracle stores token states in only 38.2%: 25.2% is reservation and 36.6% is external fragmentation and other. Perfect length prediction would still leave about 62% of the memory without token states. vLLM stores token states in 96.3%.

Contiguous slabs also cannot share. When a user asks for several samples of one prompt, every sample needs the prompt's keys and values, and they are identical across samples; with one slab per sequence, the server stores the prompt's cache once per sample. The paper later measures up to 55% memory saved by sharing in beam search.

How PagedAttention works: paging the KV cache

Operating systems met the same problem with process memory. A process sees one contiguous range of virtual addresses. The OS splits that range into fixed-size pages, places each page in any free physical page frame of RAM, and keeps a per-process page table that translates one into the other. Frames are allocated when a page is first touched, freed when the process exits, and shared between processes when the contents are identical, as with a shared library. The idea goes back to the Atlas computer's one-level storage system (Kilburn et al., 1962).

vLLM applies the same design to the KV cache, with tokens in the role of bytes, KV blocks in the role of pages, and requests in the role of processes:

For OPT-13B, one 16-token block holds 16 × 800 KB = 12.5 MB of keys and values across all 40 layers, so the 12 GB budget is about 980 blocks. Paging bounds the waste: reservation and internal fragmentation can only occur in a request's last block, at most B−1B - 1 slots (15 at the default size). A 499-token request fills 31 blocks and 3 slots of a 32nd, leaving 13 empty slots, 2.5% of its 512. External fragmentation disappears, because all blocks have the same size and any free block can serve any request.

A process's pages can sit anywhere in RAM because the CPU's memory hardware translates every address. Attention has no such hardware: a standard kernel reads a request's keys as one array, so the paper also needs a kernel that follows the block table, the subject of the next section.

The attention kernel over blocks

PagedAttention, the paper's name for this kernel, regroups equation (3) by block. With block size BB, write KjK_j for the keys of the jj-th block, the BB vectors k(j−1)B+1,…,kjBk_{(j-1)B+1}, \dots, k_{jB}, and VjV_j for its values. Then AijA_{ij}, the length-BB row of weights that query ii puts on block jj, and the output are

Aij=exp⁡ ⁣(qi⊤Kj/d)∑t=1⌈i/B⌉exp⁡ ⁣(qi⊤Kt/d) 1,oi=∑j=1⌈i/B⌉Vj Aij⊤A_{ij} = \frac{\exp\!\big(q_i^\top K_j / \sqrt{d}\big)}{\sum_{t=1}^{\lceil i/B \rceil} \exp\!\big(q_i^\top K_t / \sqrt{d}\big)\,\mathbf{1}}, \qquad o_i = \sum_{j=1}^{\lceil i/B \rceil} V_j\, A_{ij}^\top
(4)

The exponential applies to each of the BB scores in the row qi⊤Ktq_i^\top K_t, and the all-ones vector 1\mathbf{1} sums the BB results, so the denominator is the same sum of exponentials as in (3), grouped into ⌈i/B⌉\lceil i/B \rceil blocks. (The paper prints 1\mathbf{1} inside the exponential; the Provenance panel at the end explains why it belongs outside.) The output sums the same aijvja_{ij} v_j terms as (3), also grouped by block. Nothing is approximated, so the outputs match a contiguous kernel up to the order of floating-point additions, and model accuracy is unchanged.

A contiguous kernel computes (3) over one tensor per request:

# contiguous layout: request r's keys and values are one [len, d] tensor each
a = softmax(K[r] @ q / sqrt(d))          # [len] attention weights, eq (3)
o = a @ V[r]                             # [d] output for this decode step

The paged kernel loops over the block table and reads each block from its physical address. In the CUDA kernel of vLLM's first release, the work for one query and one attention head runs in two passes over the blocks, with one GPU warp (32 threads) reading each block. The first pass computes every score and keeps them in fast on-chip memory; the softmax runs over all of them at once; the second pass multiplies each block's values by its weights and adds them up. In Python-like form:

# paged layout: one decode step for request r (after vLLM v0.1.0's CUDA kernel)
# block_table[r] lists the physical block ids of r's KV cache, in logical order
scores = []
for j, phys in enumerate(block_table[r]):      # pass 1: scores, block by block
    n = min(B, ctx_len - j * B)                # the last block may be part-filled
    K_j = k_cache[phys][:n]                    # [n, d] keys, from wherever phys is
    scores += list(K_j @ q / sqrt(d))          # q.k / sqrt(d) per token
m = max(scores)                                # subtract the max so exp can't overflow
p = [exp(s - m) for s in scores]
a = [x / sum(p) for x in p]                    # the weights a_ij of eq (3), exactly
o = zeros(d)
for j, phys in enumerate(block_table[r]):      # pass 2: weighted sum of values
    n = min(B, ctx_len - j * B)
    V_j = v_cache[phys][:n]                    # [n, d] values
    o += a[j * B : j * B + n] @ V_j            # V_j A_ij^T, one term of eq (4)
return o

Take the paper's running example. The prompt is "Four score and seven years ago our", the model has generated "fathers brought", and the current token is "forth", position i=10i = 10. With B=4B = 4, its keys span ⌈10/4⌉ = 3 blocks: "Four score and seven", "years ago our fathers", and "brought forth" with two empty slots, which the kernel skips. The figure runs both passes on made-up scores and one-dimensional values, and compares the result with the contiguous formula.

Figure 4 · the paged kernel, one decode step
start
Three blocks of one request sit at physical blocks #7, #1 and #3 of the pool. Press Play or drag the step. Pass 1 visits each block, computes its scores and tracks the largest score so far; the softmax then turns all ten scores into weights at once; pass 2 visits the blocks again and adds each block's share of the output. The last frame compares the result with the contiguous softmax over the same ten scores.

Reading through the block table costs time. Section 7.1 measures the paged kernel at 20-26% higher latency than FasterTransformer's attention kernel, which it was adapted from. The paper lists three causes: reading the block table, extra branches, and handling requests of different lengths in one batch. Only the attention operator pays it; the matrix multiplies that dominate a step are untouched, and vLLM runs larger batches, so end-to-end throughput still rises. Section 5.1 lists the kernels written to keep the overhead down: the paged attention kernel itself, which reads blocks and computes attention in one fused kernel; a kernel that reshapes new keys and values and writes them into their blocks in one launch; and one that batches all the block copies of copy-on-write into a single launch.

The prompt phase does not use the paged kernel. It computes the prompt's keys and values with a conventional attention kernel, for which the paper cites FlashAttention as an example, and then stores them into blocks. FlashAttention reduces the memory and memory traffic of one attention call by tiling; PagedAttention manages where the cache lives between calls.

The block table during decoding

The paper walks through one request with B=4B = 4 (Sec. 4.3, Fig. 6). The 7-token prompt needs two logical blocks, and the block manager maps them to physical blocks 7 and 1. Block 0 is full; block 1 holds 3 tokens and has one slot left. The first generated token, "fathers", takes that slot, and the table entry's fill count goes from 3 to 4. The next token, "brought", finds the last block full, so the manager takes physical block 3 from the free list and adds a third entry to the table.

Figure 5 · the block table, step by step
9/16
Left, the request's logical blocks, filled left to right with prompt (teal) and generated (amber) tokens. Right, the physical pool; the curves are the block table, one per logical block. Drag the decode step: when a logical block fills, the next token gets a fresh physical block wherever the pool has one.

Watch three things as you drag. Physical blocks are taken only when a token needs one, so a request that stops early never holds the blocks it would have used. The physical blocks need not be adjacent, because the kernel follows the table. And at most the last logical block has empty slots, between 1 and B−1B - 1. With many requests running, their blocks interleave in the pool, and the scheduler sends each step's block tables to the GPUs together with the input tokens.

The block size sets a trade-off. Small blocks waste fewer slots per request, but each warp then reads only a few tokens per block and the kernel does more lookups. Large blocks keep the GPU busy, but a request's last block can be mostly empty, and two sequences can share only whole blocks, so a shared prefix that ends mid-block leaves that block unshared. Alpaca requests average 78 tokens, so a 256-token block leaves about 70% of each request's memory empty.

Figure 6 · choosing the block size
B = 16
Top, an average-length request of the selected dataset cut into blocks of size BB, with its unfilled slots in amber. Bottom, the paper's Fig. 18b, traced by eye: vLLM's normalized latency at a fixed request rate for each block size. Drag the block size and switch datasets.

On ShareGPT, block sizes from 16 to 128 give the lowest latency. On Alpaca, 16 and 32 work well and larger blocks degrade quickly, because most sequences are shorter than one block. Sizes 1 to 4 are slow on both. The paper picks 16 as the default: large enough to keep the GPU busy, small enough that the last block wastes little on most workloads (Sec. 7.2).

On several GPUs the paper uses Megatron-LM-style tensor parallelism, where each GPU holds a subset of the attention heads. One KV cache manager in the central scheduler keeps the block tables for all of them. Every GPU uses the same physical block ids and stores only its own heads' keys and values in each block (Sec. 4.6).

Sharing blocks between sequences

Because a request reaches its cache only through block ids, two sequences can list the same physical block in their tables. vLLM gives every physical block a reference count, the number of logical blocks that point at it, and frees a block when its count reaches 0. The paper uses this for parallel sampling, beam search and shared prefixes.

Parallel sampling. A user asks for several completions of one prompt. vLLM computes the prompt's cache once and maps every sample's logical prompt blocks to the same physical blocks. The full blocks are only ever read, but the last prompt block usually has free slots, and each sample's first new token goes into it. vLLM uses copy-on-write, as an OS does for a shared page: when a sequence writes to a block with reference count above 1, vLLM allocates a new physical block, copies the contents into it, lowers the original's count by 1, and the write goes to the copy. The next writer finds a count of 1 and writes in place.

Figure 7 · copy-on-write on a shared prompt
1/2
Two samples of one prompt. At step 0 both samples' logical blocks point to physical blocks #7 and #1, reference count 2. At step 1 the samples generate different tokens; sample B writes first, so vLLM allocates #3, copies #1 into it, lowers #1's count to 1, and B writes into #3. Sample A then writes into #1 in place. Block #7 stays shared.

Whichever sample writes first gets the copy. In the paper's Fig. 8 that is sample A1, and A2 writes into the original. Either way at most one block is copied per sample, while the prompt's full blocks stay shared for the life of the request. On Alpaca with OPT-13B, sharing saves 6.1-9.8% of KV memory for 2 to 6 parallel samples (Sec. 6.3, Fig. 15).

Beam search. A beam of width kk keeps the kk most probable candidate sequences at each step, and candidates share long prefixes. In the paper's example with k=4k = 4, all candidates share the prompt block, three of them share the next two blocks, and when two candidates drop out of the top 4, their blocks' counts fall and vLLM frees the blocks that reach 0. Earlier servers copied large parts of one candidate's cache into another's slab when the beam reshuffled; with shared blocks only the block being written is copied. Sharing saves 37.6-55.2% of KV memory for beam widths 2 to 6 on Alpaca, and 44.3-66.3% on ShareGPT.

Shared prefix. Many applications put the same instructions or few-shot examples in front of every prompt. The service provider computes the prefix's cache once and keeps its physical blocks reserved, like a shared library. A new request maps its first logical blocks to them, with the last of those blocks marked copy-on-write since the request's own tokens may continue it, and the prompt phase runs only on the user's part of the input.

The model and the kernel receive only a list of physical block ids per sequence, and the sharing patterns stay inside the block manager, so vLLM can batch requests with different decoding methods (plain sampling, parallel sampling, beam search) in one step.

Preemption: swap or recompute

Allocating on demand means the pool can run out partway through: every running request may need a new block in the same step. vLLM then preempts requests. Scheduling is first-come, first-served: the earliest arrivals are served first, and the latest arrivals are preempted first. Eviction is all-or-nothing per sequence, since a request needs its entire cache to compute the next token. The sequences of one request, such as the candidates of a beam, form a sequence group that is always preempted and rescheduled together, because they share blocks. While any preempted request is waiting, vLLM accepts no new requests.

A preempted request's blocks can be recovered in two ways:

Figure 8 · preemption when the pool runs out
step 0
Five requests, R1 to R5 in order of arrival, share a pool of 12 blocks of 4 tokens. Press Play or drag the step. When a running request needs a block and none is free, the latest-arrived running request loses all its blocks, and no new request is admitted until it resumes. Switch between recomputation (the cache is discarded and rebuilt in one prompt step) and swapping (the blocks move to CPU memory and back).

The two modes trade differently against block size (Sec. 7.3, Fig. 19). Swapping moves one block per PCIe transfer, so small blocks mean many small transfers that use little of the PCIe bandwidth, and swapping is expensive at small block sizes. Recomputation does not touch the blocks, so its cost is the same for every block size. In the paper's microbenchmark, recomputation is never more than 20% slower than swapping, and for block sizes 16 to 64 the two give about the same end-to-end performance. The paper names no default. The v0.1.0 code recomputes requests with one sequence and swaps groups with several, because it had no way to recompute a group whose sequences share blocks.

Results: throughput, batch size and sharing

The evaluation serves OPT-13B on one A100, OPT-66B on four, OPT-175B (the size of GPT-3) on eight A100-80GB GPUs, and LLaMA-13B for the shared-prefix test. Requests come from ShareGPT conversations and the Alpaca instruction dataset; ShareGPT prompts are 8.4× longer and outputs 5.8× longer on average. Arrival times are random with a chosen average rate (a Poisson process). The metric is normalized latency, each request's end-to-end latency divided by its number of output tokens and averaged over requests, in seconds per token. A server that keeps up stays near a flat baseline; past the highest rate it can sustain, requests queue and the curve shoots up.

Figure 9 · latency against request rate
0.50 s/tok
OPT-13B on one A100, traced by eye from the paper's Fig. 12a (ShareGPT) and 12d (Alpaca). Drag the latency target to read off the highest request rate each system sustains under it, and the ratio for vLLM. Switch datasets with the buttons.

On ShareGPT, vLLM sustains 1.7-2.7× the request rate of Orca (Oracle) and 2.7-8× that of Orca (Max) at similar latency, across the three OPT sizes, and up to 22× that of FasterTransformer, which has no iteration-level scheduling (letting requests join and leave the batch at every step) and reserves memory like Orca (Max). The batch sizes behind it are in Fig. 13a, OPT-13B on ShareGPT at 2 requests per second: vLLM averages 30.42 requests in a batch, against 13.62 for Orca (Oracle), 9.81 for Pow2 and 7.00 for Max, so 2.2× Oracle's batch and 4.3× Max's. On Alpaca at 30 requests per second the averages are 132.44, 72.75, 43.24 and 7.00. The Alpaca results follow the same trend, except OPT-175B (Fig. 12f), where 264 GB of KV memory holds enough of Alpaca's short requests that the system is limited by compute, and vLLM's advantage over Oracle and Pow2 is smaller. On OPT-13B the traced Alpaca curves also put vLLM closer to Oracle than on ShareGPT, about 1.3× at 0.5 s/token in Figure 9.

Sharing adds to the gain. With OPT-13B on Alpaca, vLLM's throughput advantage over Orca (Oracle) grows from 1.3× with ordinary sampling to 2.3× with beam width 6. In the shared-prefix test, LLaMA-13B translates English to German (WMT16) behind a prefix of instructions and examples: with one 80-token example, vLLM reaches 1.67× the throughput of Orca (Oracle), and with five examples totalling 341 tokens, 3.58×. The paper does not split that gain between sharing the prefix's blocks and skipping its prompt computation. In a chatbot test, where prompts are cut to the last 1024 tokens of the conversation, vLLM sustains 2× the request rate of all three Orca variants, which all reserve 1024 output slots because of buddy-allocator rounding.

These numbers are for OPT and LLaMA models on A100s, against FasterTransformer and the authors' reimplementation of Orca as they stood in 2023. Iteration-level scheduling comes from Orca, and vLLM uses it too; the paper credits the gain over Orca to fitting more requests in memory. Serving systems built since have widely adopted paged KV caches, which is this page's observation, not a claim of the paper; a 2026 comparison would be against servers that already page.

Provenance Verified against primary literatureHow we verify
Kilburn, Edwards, Lanigan & Sumner (1962)One-level storage system, the Atlas computer: the origin of paged virtual memory, reference [25] in the paper.
Yu et al., Orca (OSDI 2022)Iteration-level scheduling: requests join and leave the batch at every decoding step. vLLM keeps this and adds paged memory; the paper calls the two complementary (Sec. 9). The Orca numbers in the paper come from the authors' own reimplementation, using a buddy allocator.
NVIDIA FasterTransformerThe other baseline, and the source of the attention kernel that vLLM adapts to read from a block table (Sec. 5.1).
Dao et al., FlashAttention (2022)Related work, not a component of the paged kernel: tiling that cuts the memory and I/O of one attention call. The paper cites it as an example of the conventional kernel vLLM uses in the prefill step (Sec. 4.3).
Shazeer, MQA (2019)Background: shrinks the KV cache itself by sharing keys and values across heads. PagedAttention leaves the cache size alone and changes its layout.
OPT-13B on one A100-40GB800 KB per token = 2 x 5120 x 40 x 2 bytes. Table 1: 26 GB of weights, 12 GB for the KV cache, 15.7K token slots. 15.7K / 2048 = 7.7, so a 2048-token reservation fits 7 requests, the Orca (Max) batch size in Fig. 13.
vLLM v0.1.0 sourcecsrc/attention/attention_kernels.cu, lines 141-217 (scores per block, max, softmax) and 238-253 (values per block): the two-pass kernel this page draws. vllm/core/scheduler.py, lines 349-365: the default preemption mode.
Figure dataFig. 2 bars are the paper's printed values. The latency curves (Fig. 12a, 12d) and block-size curves (Fig. 18b) on this page are traced by eye from the paper's plots; read them for shape and ratio, not to the second decimal.
correctionEquation (4) as printed puts the all-ones vector inside the exponential, exp(q_i^T K_t 1 / sqrt(d)), which exponentiates the sum of a block's scores and is not a softmax denominator. The normalizer must sum the elementwise exponentials, exp(q_i^T K_t / sqrt(d)) 1, which is what this page shows and what the kernel computes. The prose around (4) also cites "Eq. 4" where it means Eq. 3. Two popular summaries need correcting too. The 2-4x headline is not a faster kernel: the paged attention kernel is 20-26% slower than FasterTransformer's (Sec. 7.1), and the gain comes from fitting more requests into memory, measured against 2023 baselines. And vLLM does not default to swapping: the paper reports both recovery modes and names no default, while the v0.1.0 code recomputes single-sequence requests and swaps only multi-sequence groups, for which it had no recomputation path. And iteration-level scheduling is Orca's idea, which vLLM reuses (Sec. 9).

Questions you might still have

?

Why does LLM serving throughput depend on memory at all?
A decoding step reads every model weight to produce one token per request, so the GPU only uses its arithmetic well when many requests share that read. Each request in the batch needs its own KV cache in GPU memory. On OPT-13B on one A100-40GB, 12 GB is left for KV caches; reserving 2048 tokens per request fits 7 requests, while allocating only what each request uses fits about 30 average ShareGPT requests.

?

Is the attention output identical to a normal kernel's?
Yes, up to floating-point summation order. Equation (4) regroups the sums of equation (3) by block without approximating anything. The kernel computes every score, takes the full softmax over all of them, then sums the values block by block. The paper states that vLLM does not change model accuracy.

?

What does PagedAttention make slower?
The attention kernel. Section 7.1 measures 20-26% higher attention-kernel latency than FasterTransformer, from reading the block table, extra branches, and handling sequences of different lengths in one batch. Attention is one operator among many (the large matrix multiplies are unchanged), and the larger batch outweighs the cost end to end.

?

Why not predict each request's output length and reserve exactly that?
The paper tests the ideal version, Orca (Oracle), which knows every output length in advance. It still stores token states in only 38.2% of its KV memory: the whole output is reserved from the first step, so slots sit empty until their tokens are generated, and the buddy allocator rounds every reservation up to a power of two.

?

How does this relate to FlashAttention?
They solve different problems and are used together. FlashAttention tiles one attention call so its intermediate matrices stay in on-chip memory. PagedAttention decides where each request's long-lived KV cache sits in GPU memory across many decoding steps. In the paper, vLLM uses a conventional kernel such as FlashAttention for the prompt and a paged kernel adapted from FasterTransformer for decoding.

?

How does this compare to multi-query and grouped-query attention?
Multi-query attention shrinks the KV cache by giving all query heads one shared set of keys and values; grouped-query attention, used in Mistral 7B, shares one set per group of heads. Both change the model and make each token's cache smaller. PagedAttention changes no model weights and makes the cache's layout waste less. A server can do both.

?

Why are blocks 16 tokens long?
Smaller blocks leave less unused space at the end of each request, but the paper finds they cannot use the GPU's parallelism fully when reading and processing the cache; in the paper's sweep, sizes 1 to 4 were several times slower. Larger blocks waste more of the last block and share less, which hurts short requests. On ShareGPT sizes 16 to 128 did best, on Alpaca 16 and 32, so vLLM defaults to 16.

Footnotes & further reading

  1. The paper: Kwon, Li, Zhuang, Sheng, Zheng, Yu, Gonzalez, Zhang, Stoica, Efficient Memory Management for Large Language Model Serving with PagedAttention (SOSP 2023). The vLLM project is at github.com/vllm-project/vllm.
  2. The paged kernel as first released: csrc/attention/attention_kernels.cu at v0.1.0 (scores and softmax at lines 141-217, values at 238-253), and the preemption-mode choice in vllm/core/scheduler.py (lines 349-365).
  3. Orca, the paper's main baseline: Yu, Jeong, Kim, Kim, Chun, Orca: A Distributed Serving System for Transformer-Based Generative Models (OSDI 2022). Iteration-level scheduling is its idea; vLLM keeps it and adds paging.
  4. Shrinking the cache instead of packing it: Multi-Query Attention (Shazeer 2019, arXiv:1911.02150) shares one key and value across all heads; grouped-query attention, the middle ground, is covered in the Mistral 7B explainer.
  5. FlashAttention (Dao et al. 2022, arXiv:2205.14135) tiles one attention call to keep its working set on chip. The PagedAttention paper cites it for the prefill step; the paged decoding kernel is adapted from FasterTransformer.
  6. The virtual-memory lineage: Kilburn, Edwards, Lanigan, Sumner, One-level storage system, IRE Transactions on Electronic Computers, 1962, reference [25] in the paper.