文章

PagedAttention 生产实战:用分页 KV Cache 提升 LLM Serving 吞吐

系统讲解 PagedAttention 如何通过分页 KV Cache 管理缓解大模型推理中的显存碎片与过度预留问题,帮助工程团队提升吞吐、降低延迟波动,并给出生产上线检查清单与监控策略。

背景问题:为什么 KV Cache 会成为推理系统的隐性瓶颈

Transformer 自回归生成时,每生成一个 token 都需要访问前面所有 token 的 Key 和 Value。为了避免每一步重新计算历史上下文,推理系统会将中间结果缓存下来,这就是 KV Cache。

KV Cache 有三个典型特征:

  1. 大:随层数、hidden size、head 数、上下文长度和 batch size 线性增长。
  2. 动态:请求长度不同,输出长度不可预知,显存需求持续变化。
  3. 强占显存:KV Cache 通常驻留在 GPU 显存中,直接挤压可并发请求数量。

在没有分页管理时,系统往往需要为每个请求预留一段连续显存,或按最大长度估算空间。这会带来两类浪费:

  • 内部碎片:序列最后一段没有填满,但对应显存已经分配。
  • 外部碎片:显存还有总量,但不一定有足够大的连续空间给新请求。

当请求长度分布很不均匀时——如同一个服务同时处理短问答、长文档总结、RAG 检索问答和多轮 Agent 任务——这个问题会进一步放大。即使 GPU 看起来还有显存,调度器也可能无法安全地塞入更多请求。

核心原理:把 KV Cache 当成”分页内存”管理

PagedAttention 的设计可以用三个对象理解:

  • Logical blocks:一个请求在逻辑上连续的 token 块。
  • Physical blocks:GPU 显存中真实分配的 KV Cache 块。
  • Block table:把逻辑块映射到物理块的表。

普通 KV Cache 管理更像”为一个请求分配一整段连续数组”。PagedAttention 则更像操作系统的虚拟内存:一个请求看到的是连续 token 序列,但底层显存块可以不连续。

Request tokens: [ t0 t1 t2 t3 ][ t4 t5 t6 t7 ][ t8 t9 ... ]
Logical block id: [ block 0 ][ block 1 ][ block 2 ]
Block table:
  block 0 -> physical block 17
  block 1 -> physical block 03
  block 2 -> physical block 42

当请求继续生成 token,系统按需申请新 block;当请求结束,block 释放回池中;当多个请求共享同一段前缀,block 也有机会复用。

这带来的好处不是”attention 算法复杂度变成常数”,而是显存管理效率提升。它让系统能更稳定地把 GPU 显存转化为有效 batch,而不是浪费在碎片和保守预留上。

与连续批处理的关系:PagedAttention 不是单点优化

PagedAttention 常和 continuous batching 一起出现,但二者不是同一件事。

技术解决的问题
Continuous batching调度问题:请求完成时新请求可立刻进入 batch,不必等整个 batch 结束
PagedAttention显存组织问题:请求进入、退出、增长时,KV Cache 按块动态分配和回收

二者结合后,服务端可以更大胆地做动态调度:

Incoming requests
  ↓
Scheduler groups active sequences
  ↓
Paged KV blocks allocated on demand
  ↓
Attention kernel reads blocks by block table
  ↓
Completed requests release blocks
  ↓
New requests fill freed capacity

如果只有 continuous batching 而没有高效 KV Cache 管理,系统仍可能被显存碎片卡住。如果只有 PagedAttention 而没有合理调度,显存效率也不一定能转化为吞吐收益。

工程落地:适合哪些生产场景

PagedAttention 最适合以下类型的服务:

1. 请求长度差异大的在线推理

例如同一个 LLM API 同时处理:

  • 短指令问答
  • 长文档总结
  • RAG 检索增强问答
  • 多轮会话
  • Agent 工具调用链

这类负载的上下文长度分布不稳定,传统连续显存分配很容易浪费空间。

2. 多并发、吞吐优先的模型服务

如果目标是提高单卡或多卡吞吐,PagedAttention 可以帮助系统容纳更多 active sequences。它不保证每个请求都更快,但通常能改善整体容量与稳定性。

3. 长上下文和 prefix reuse 场景

长系统提示词、固定模板、多轮会话和 RAG 管线都可能出现共享前缀。分页 KV Cache 为块级共享、复用和缓存淘汰策略提供了基础。

4. 多输出采样

并行采样时,多个候选输出可能共享同一段 prompt 的 KV Cache。块级共享可以减少重复存储,提高并行生成效率。

上线架构:从”能跑”到”可控”

生产环境不要只把 vLLM、TGI 或 TensorRT-LLM 启动起来就结束。更稳妥的方式,是把 PagedAttention 放入整体推理架构中治理。

推理服务层

推理服务需要明确:

  • 模型上下文长度上限
  • 最大 batch token 数
  • 最大并发序列数
  • block size
  • prefill 和 decode 阶段的调度策略
  • 是否启用 prefix caching 或 KV cache reuse

路由层

如果有多个推理实例,应避免完全随机路由。对于共享系统提示词、多轮会话或相同知识库任务,可以考虑 KV-aware routing,让更可能命中缓存的请求进入同一个实例或同一组实例。

限流层

KV Cache 不是无限资源。限流不应只按 QPS 计算,还应考虑:

  • prompt token 数
  • max output token 数
  • 当前显存压力
  • 请求是否为长上下文
  • 是否允许流式输出

更合理的做法是建立 token budget,而不是只限制请求数。

常见误区

误区一:PagedAttention 等于降低所有请求延迟

PagedAttention 的主要收益是显存效率和吞吐容量。单个短请求在低并发情况下,未必能明显变快。甚至因为多了一层 block table 访问和 kernel 适配,在某些场景下还可能有额外开销。

判断效果时,不要只看单请求 benchmark,要看生产负载下的:

  • TTFT(Time to First Token)
  • TPOT(Time per Output Token)
  • tokens/s
  • GPU utilization
  • OOM 次数
  • p95 / p99 延迟

误区二:block size 越小越好

block 越小,显存浪费越少,复用粒度越细;但 block 太小可能增加管理开销,也可能影响 kernel 访问效率。block size 是典型权衡项:

block size可能收益可能代价
较小更细粒度分配,更少尾部浪费,更容易局部复用元数据更多,调度和访问开销更高
较大kernel 访问可能更友好,管理开销更低尾部浪费更高,复用命中条件更粗

实际选择应结合模型、上下文长度分布、并发模式和框架默认值测试。

误区三:只要启用 PagedAttention,就不需要容量规划

PagedAttention 能减少浪费,但不能改变 KV Cache 线性增长的事实。长上下文、长输出、高并发仍会让显存成为瓶颈。上线前仍然要做容量模型。

可以用以下简化公式做初步估算:

KV cache memory ≈ layers × 2 × hidden_size × tokens × bytes_per_element × batch_factor

这里的 2 对应 Key 和 Value。真实实现还会受到 GQA/MQA、量化、张量并行、padding、block size 和框架内存池影响,所以该公式只能用于方向性判断。

上线检查清单

1. 负载画像

上线前先统计真实请求,而不是只用平均值:

  • prompt token p50 / p95 / p99
  • output token p50 / p95 / p99
  • 多轮会话比例
  • RAG 请求比例
  • 共享 system prompt 比例
  • 流式输出比例

2. 压测矩阵

至少覆盖以下组合:

场景观察重点
短 prompt + 短输出调度开销是否可接受
长 prompt + 短输出TTFT 和 prefill 压力
短 prompt + 长输出decode 阶段 KV 增长
长 prompt + 长输出OOM、p99 延迟、吞吐上限
多轮会话prefix reuse 和缓存命中
RAG 模板系统提示词和检索内容的复用机会

3. 监控指标

建议至少监控:

  • GPU 显存使用率
  • active sequences
  • waiting queue length
  • prefill tokens/s
  • decode tokens/s
  • TTFT p50 / p95 / p99
  • TPOT p50 / p95 / p99
  • cache hit ratio
  • block allocation failure
  • eviction count
  • OOM count
  • request cancellation rate

4. 回滚策略

推理系统变更要保留回滚路径:

  • 保留旧版本 serving 配置
  • 控制单实例灰度比例
  • 将长上下文请求单独路由
  • 对 max tokens 设置保守上限
  • 出现 OOM 或 p99 激增时自动降级

适用边界

PagedAttention 对以下场景收益可能有限:

  1. 低并发、短上下文服务:显存碎片不是主要问题。
  2. 严格追求单请求最低延迟:需要同时优化 kernel、batch、模型量化和调度。
  3. 缓存复用很少的随机请求:block sharing 价值有限,但按需分配仍可能有用。
  4. 框架或模型 attention 类型特殊:需要确认当前 serving 框架是否支持对应模型结构。

对于新模型,尤其是混合 attention、sliding window attention、Mamba-like 结构或特殊位置编码模型,需要检查框架文档中的兼容性说明,不要简单假设所有模型都能获得同样收益。

参考资料

常见问题

PagedAttention 和普通 attention 的区别是什么?
普通实现通常要求 KV Cache 在显存中连续存放,或需要按最大长度做较保守的预留。PagedAttention 将 KV Cache 拆成固定大小的 block,通过 block table 把逻辑序列映射到非连续的物理显存块,从而降低碎片和预留浪费。
PagedAttention 一定能降低单个请求延迟吗?
不一定。它更常见的收益是提升吞吐、降低显存浪费、减少 OOM、提升高并发下的稳定性。单请求延迟还取决于模型大小、上下文长度、batch 策略、kernel 实现、GPU 型号和是否启用 prefix caching。
生产环境上线 PagedAttention 最应该监控什么?
重点监控 GPU 显存占用、KV Cache 使用、block 分配失败、cache hit ratio、TTFT、TPOT、tokens/s、OOM 次数和 p95/p99 延迟。只看平均延迟很容易掩盖长上下文请求带来的尾部风险。