← 关联知识节点:推理优化:KV Cache → Continuous Batching → PagedAttention
交互工具 · 原理

显存碎片与分页

给每个请求预留一段「最大可能长度」的连续显存:预留空着的是内部碎片,旧请求退出后的坑凑不出新请求要的连续段是外部碎片。PagedAttention 把 KV Cache 切成固定小块、逻辑连续物理离散。拧分布与块大小看浪费率漂移,再重放同一场请求流——碎片率是负载的函数,亲手拧出来。

纯前端确定性模拟:请求生成与浪费率公式逐字对齐知识库《推理优化》动手节(CPython random.seed(42) 同语义 MT19937,1000 请求均匀 [50, 2048]、16 token 块 → 预留式 48.7% / 分页式 0.7% 精确复现);显存网格重放的到达节奏与预留声明为演示口径,零 API、零延迟。

1000 个请求的 KV Cache 要住进显存: 预留式按「最大可能长度」圈一整段连续空间——预留空着的是内部碎片, 旧请求退出后的坑凑不出新请求要的连续段是外部碎片; PagedAttention 切成固定小块、逻辑连续物理离散,只剩块内尾浪费。拧分布与块大小,看浪费率漂移; 再重放同一场请求流,看同一显存池谁接得住。
块大小
长度上限 MAX_LEN2048 token
2562048 ★4096
平均长度 1051 token, 均值随上限滑杆平移;分布越「重尾」(双峰 / 长上限),预留式的账越难看。

浪费率总账(文章公式:1 − 实用 ÷ 分配量)

文章口径 seed 42 · 均匀 [50, 2048] · 块 16 ✓
预留式浪费率
48.7%
圈地 2.05M|实用 1.05M
分页式浪费率
0.7%
66160 块 × 16|只剩块内尾浪费
同池并发容量比
1.93×
预留式分配量 ÷ 分页式分配量

分页浪费率的理论期望 ≈ (块大小−1)/2 ÷ 平均请求长 = 16 块时约 0.71%(实测 0.71%)——块拧得越大,块内尾浪费越大,这就是「块粒度压浪费」的代价曲线。 同一块显卡,分页式省出的分配量理论上能再多接约 1.9 倍并发。

同一显存池重放(603K token 池 · 演示口径)

实用(KV Cache) 预留空转 / 块尾浪费 空闲第 48/79 轮 · 在场 348 个请求
预留式 · 每请求圈一整段拒 32|最大连续空闲 2.0K
琥珀 = 预留了没用的内部碎片;空闲被切得七零八落,新请求要一段连续空间时常常凑不出来。
分页式 · 16 token 一块按需领拒 0|空闲 123K
块散在池子各处、块表记着顺序——空闲永远是整块,谁退出立刻就能派给新请求。

同一场请求流、同一个池子:分页式接住 1000/1000, 预留式只接住 968、 32 个被拒—— 被拒的时刻池子里并非没空间(当前空闲 10.2%),是空缺各霸一方、凑不出一段连续的预留上限。 空而不用,这就是外部碎片;文章的静态总账只算了分配层,这一格网格把它补了出来。

两句诚实的校准(文章注记口径)
  • 总账只模拟「分配策略」这一层;真实系统的浪费还含调度保留与更极端的负载——所以 vLLM 论文实测是 60~80%,本页 48.7% 的预留式口径偏保守。
  • 浪费率降几十倍 ≠ 吞吐涨几十倍:计算是另一重瓶颈,论文端到端吞吐提升是 2~4 倍。