讲清楚 PagedAttention
什么是 PagedAttention(一句话)
把注意力用到的 KV-cache 切成很多固定大小的小块(blocks),再用一张“块表”(像操作系统的页表)把逻辑上的第 i 个 token映射到物理内存里某个小块。这样 KV 不需要一大段连续显存,随用随分配/回收,还能把相同前缀的块共享复用。
⸻
它要解决什么问题?
传统做法把每个请求的 KV-cache 申请成连续大数组。一旦并发里混入“长短不一”的上下文,就会产生碎片、迁移和浪费,并且很难做到“连续批处理(continuous batching)”。PagedAttention 通过分页式管理把这些问题化整为零,让服务器能在高并发下稳定地塞入更多请求、提高吞吐。
⸻
它是怎么做的?(工作原理)
1. 切块(Block)
将每个请求的 KV 拆成固定 token 数量的块;每块存某个 head 的 K/V 片段。块可以非连续分布在显存,只靠索引访问。
2. 块表(Block Table)
每个序列维护一张“块表”,记录“第 j 段 token → 第几块”。注意力 kernel 在计算时,按块表把需要的 K/V **聚合(gather)**到寄存器/共享内存再算,不必真的把 KV 存成连续大段。
3. 前缀共享(Prefix Sharing)
多个请求如果前缀相同,块表都指向同一批物理块(引用计数)。这样不用重复计算/存储前缀 KV,极大节省显存与时间。
小比喻:
传统做法像“把一本书的每一页都装订成一本厚册(连续数组)”;PagedAttention 则把内容裁成索引良好的卡片盒(blocks+表),随取随用、还能多人共用同一盒卡片(前缀共享)。
⸻
它带来的直接收益
• 更少的显存浪费,几乎零碎片 → 同样显存容纳更多并发请求。
• 批处理更灵活 → 便于持续加入/移除请求(continuous batching)。
• 共享前缀 → 提升多轮对话/模板化提示词场景的吞吐。
官方团队报告在典型基线之上显著提升吞吐(场景相关)。
⸻
和参数/实现相关的几个实用点
• block_size(块大小):是“每块容纳的 token 数”,不同设备/后端支持范围不同;例如 CUDA 端常见的支持范围有限(官方 issue 有说明),并非任意数值都行。调大可降低块数量、减轻索引开销;调小可降低浪费、提升共享粒度,取舍要结合模型 head_dim、并发形态做压测。
• 它不改变注意力的数学本质:只是KV 的布局与取数方式变了,注意力计算本身等价;代价是需要高效的 gather/scatter kernel 来把分散的块拼到一起。 
• 与“自动前缀缓存”联动:vLLM 的 Automatic Prefix Caching 正是建立在“块级共享”之上,适合大量相同/相似提示词的场景。
⸻
一句话记忆
PagedAttention = KV-cache 的“分页内存 + 块表 + 前缀块共享”。
它把连续大数组拆成可复用的小块,配合表驱动的高效 kernel,解决碎片与浪费,从而支撑 vLLM 的高吞吐服务。