AI++
头条推理部署·进阶·10 分钟阅读·AI++ 编辑部

PagedAttention:把操作系统的虚拟内存思想搬进 LLM 推理

vLLM 引入的 PagedAttention 借鉴 OS 分页内存管理,把 KV cache 切成定长 block 按需分配。 它消灭了 KV cache 的内部/外部碎片,让显存利用率从 ~30% 提到 ~90%+,吞吐翻倍以上。 还顺带解锁了「连续批处理」和「前缀共享」两个杀手锏,是开源 LLM serving 的事实标准。

一句话结论

PagedAttention 把 KV cache 当成操作系统管理虚拟内存那样来管——切成固定大小的 block、用 block table 做逻辑到物理的映射、按需分配——于是显存碎片几乎消失,单卡能塞下的并发请求数翻几倍,吞吐量直接 2~4 倍提升。

背景问题

LLM 推理的吞吐瓶颈不在算力,在显存。一个请求的 KV cache 在生成过程中不断增长,传统 serving 框架(如 HuggingFace Transformers + FasterTransformer 早期版本)是这样管理的:

每来一个请求,预先按「最大可能序列长度」预留一段连续显存。

这个朴素策略带来两类灾难:

  1. 内部碎片。请求实际只生成了 200 token,但你按 max=2048 预留了显存——剩下 1848 token 的空间被占着却不用。论文统计这种浪费常占总显存的 60%~80%。
  2. 外部碎片。请求长短不一,频繁分配/释放后显存被切成零散小块,新请求明明总量够却找不到一段连续空间,只能等待或 OOM。

后果:GPU 利用率极低。一张 80GB 的 A100,模型权重占 ~30GB,剩 ~40GB 给 KV cache,但实际能服务到的并发数远低于理论值,因为大量显存被碎片和预留吞掉了。

更糟的是传统 serving 用「静态批处理」:一批请求必须凑齐才一起跑,先到的得等后到的;某个请求生成特别长,整个 batch 都被它拖着。这让 batching 的效率红利大打折扣。

核心思路

PagedAttention 的灵感直接来自操作系统的虚拟内存分页。OS 把进程的虚拟地址空间切成固定大小的 page,物理内存也切成 page frame,用 page table 做映射——进程看到的是连续虚拟地址,物理上却散落在各处,按需分配。

PagedAttention 把这套搬过来:

  1. 把 KV cache 切成固定大小的 block(如每 block 存 16 个 token 的 K、V)。
  2. 逻辑上的「一个序列的 KV」不要求物理连续。一个序列的 KV 可以散落在任意空闲 block 里。
  3. 每个序列维护一个 block table:记录「我的第 $i$ 个逻辑 block 在物理上的哪个 block」。生成时按表查址。
  4. 按需分配 block:序列变长才申请新 block,不预留。

效果立竿见影:

  • 内部碎片只剩最后一个 block 内的少量浪费(最多一个 block,类似 OS page 的内部碎片)。
  • 外部碎片消失——任何空闲 block 都能用,不需要连续。
  • 显存利用率从 ~20%~40% 飙到 ~90%+,能塞下的并发请求数成倍增加。

关键技术点

1. Block table 让「逻辑连续、物理分散」成为可能

每个序列有一个 block table,类似 OS 的页表。attention 计算时,kernel 根据 block table 把分散的 K、V block 拼起来参与计算。这要求重写 attention kernel——vLLM 的 CUDA kernel 会主动处理非连续访存,对上层透明。

2. 连续批处理(Continuous Batching)

PagedAttention 配套的第二个杀手锏是 continuous batching(也叫 iteration-level batching):

  • 不再等一批凑齐才跑,而是每个生成 step 都可以动态加入新请求、踢出已完成的请求
  • 新请求来了,只要显存里有空闲 block 就立刻插入当前 batch。
  • 某个请求生成完,立刻释放它的 block 给后面的请求用。

这让 GPU 几乎永远满载,没有「等齐」和「被长请求拖累」的浪费。配合 PagedAttention 的高显存利用率,吞吐比静态批处理提升 2~4 倍甚至更多。

3. 前缀共享(Prefix Sharing / Automatic Prefix Caching)

很多请求共享相同前缀——system prompt、few-shot 示例、共享的对话历史。PagedAttention 让多个序列的 block table 指向同一组物理 block(前缀部分),写时才复制(copy-on-write 思想)。

  • 多轮对话里同一 system prompt 不必重复存。
  • few-shot 场景里示例的 KV 只算一次。
  • RAG 场景里检索到的同一文档片段被多个请求复用。

这是 PagedAttention 在结构上天然支持的——传统「连续预留」方案想共享前缀非常别扭。

4. 调度策略:先来先服务 + 抢占

显存仍可能不够(请求太多)。vLLM 用两种策略:

  • Preemption:把某些序列的 KV cache 换出(到 CPU 内存或丢弃),需要时重算或换回。类似 OS 的 swap。
  • 优先级调度:短请求优先,长请求可能被换出。

这让系统在过载时优雅降级,而不是直接 OOM 崩溃。

效果与局限

效果:

  • 原论文:相比 HuggingFace Transformers,吞吐提升 2~4 倍(部分场景更高);相比 FasterTransformer 也有明显优势,尤其在长短请求混合的场景。
  • 显存碎片从主要矛盾变成几乎不存在。
  • Continuous batching + prefix sharing 让 vLLM 在真实 serving 负载下(请求长短不齐、有共享前缀)优势尤其大。
  • 开源后迅速成为社区默认 serving 框架,几乎所有主流模型都优先适配 vLLM。

局限:

  • 对 attention kernel 有定制要求。PagedAttention 的非连续访存需要专门 kernel,新架构(如 MLA、GQA 早期、SSM)支持需要时间跟进。
  • block size 是个折中。太小→block table 大、访存间接开销多;太大→最后一个 block 浪费多。典型取 16,但不通用最优。
  • 抢占有代价。换出/重算会引入额外延迟,在极端过载下尾延迟会变差。
  • 不完全适合所有负载。极短请求、批量离线推理(没有并发混合)下,PagedAttention 的红利相对小,传统静态批处理有时更简单高效。

谁该关心

  • LLM serving 工程师:PagedAttention 是当前 serving 的基础设施,理解它的 block table、continuous batching、prefix caching 是调优 vLLM/SGLang 的前提。
  • 自建推理集群的人:vLLM 这套机制能把单卡并发数和吞吐拉到极限,直接关系硬件 ROI。
  • RAG / Agent 应用开发者:prefix sharing 对共享 system prompt、共享检索文档的场景有天然加成,用好能让成本降一个量级。
  • 研究推理系统的人:PagedAttention 是「把成熟 OS 思想搬到 ML」的教科书案例,对设计新的显存管理有启发。

一个判断:PagedAttention 不是一个「炫技」创新,而是一个「早该有人做、做完整个行业受益」的工程基建。它现在已经是默认选项,理解它不是选择题而是必修课。但它不是终点——后续的 SGLang、distserve 等在前缀共享、调度上做了进一步优化,PagedAttention 是地基而非天花板。

推理优化KV Cache显存管理serving