AI AI infra · 推理侧
书库 第 1 章
AI infra · 推理侧

第 5 章 分页:把 KV cache 当虚拟内存管

先备检查:一条 8K 请求的缓存是 4 GiB;一条 200 token 的请求大概多少?"按最大值预留"是什么意思?
先修:第 2–4 章 · 深度:L2 · 预计阅读:30 分钟
一句话:缓存不是"分够了就浪费一点",是"分多了根本分不下去"。

五个长合同,十五个短问题

回到那二十条请求:五份 8K 合同,十五个两百字的问题。

最直白的做法,是给每条都按最大长度预留。max_model_len 设 8192,那就是每条预留 4 GiB。

十四条的位置,十四条都按 4 GiB 占着,一共 56 GiB。

实际用了多少?五条长的各 4 GiB,十五条短的各 100 MiB 左右:

5 × 4 + 15 × 0.1 = 21.5 GiB

占了 56,用了 21.5,剩下的三十多 GiB 就在那里空着,谁也拿不走。

浪费是怎么来的

两种。

内部碎片:你给一条请求划了 4 GiB,它只用 100 MiB,尾巴上那 3.9 GiB 别人不能用。请求越短,浪费比例越高。

外部碎片:请求陆续结束、陆续到来,显存里留下大小不一的空洞。总空闲可能还有 8 GiB,但凑不出一块连续的 4 GiB,新请求照样进不来。

两者叠加,实际能跑的并发远低于"显存总量除以单条缓存"。

分页:不再按最大值划地

思路是从操作系统借来的:别给整块地,给页。

把缓存切成固定大小的块,比如一块装 16 个 token。7B 的缓存是每 token 512 KiB,一块就是 8 MiB。

一条 8K 请求 = 512 块。一条 200 token 的请求 = 13 块,最后一块只用了一点。

请求要多少给多少,用完就还。块可以散落在显存的任何地方,靠一张页表把"第几块逻辑块"映射到"哪块物理显存"。

于是内部碎片只剩最后一块的零头,外部碎片基本消失——任何一块 8 MiB 的空位都能用。

vLLM 的 PagedAttention 就是这套东西,论文里报告把缓存浪费压到 4% 以下,吞吐提升 2 到 4 倍。数字是他们在自己的实验里测的,你的机器上不一定一样,方向可以信。

顺带解决的共享问题

二十个同事用同一个系统提示词,五百个 token。

以前每条请求各算一遍、各存一份:二十份,约 4.9 GiB。

有了页表,这件事变得自然:二十条请求的页表,指向同一批物理块。前缀算一次就够,后面的请求直接接上。

这就是前缀缓存(prefix caching)。同一条系统提示、同一份长文档被反复提问,收益最大。不同前缀的请求之间没有便宜可占。

还有个细节:两条请求共享着同一块,其中一条要改,怎么办?复制一份再改。这是写时复制,操作系统里也是这套。

代价

页表要查。每个 token 生成时,注意力要按页表把分散的块找回来,比连续内存多一次间接寻址。

块不能太大,也不能太小。块大,最后一块浪费多;块小,页表变长,查表开销上来。十六个 token 一块是常见折中。

实现复杂度也上去了。内存分配器、块管理、共享与回收,这些在连续分配的方案里都不存在。

什么时候分页没用

batch=1 时,没有并发就没有浪费,分页只增加开销。

请求长度都很接近、上下文都很短时,内部碎片本来就少。

更关键的是:分页解决的是"分配方式",不解决"总量不够"。二十条要 80 GiB,卡只有 72,分页再聪明也放不下。

经验:先看利用率,再谈扩卡

显存报警,先别加卡。看一眼缓存的实际利用率和块的使用数。

利用率低、块却分配不出来,是碎片问题,分页能救。

利用率已经九成以上,是总量问题,只能减负载或者换硬件。

这两个判断差一张卡的钱。

练习

六道题,先闭卷,再翻 ANSWERS.md。

  1. 二十条请求,五条 8K、十五条 200 token,按 8K 预留,浪费多少 GiB?
  2. 一块 16 token,8K 请求要几块?200 token 要几块?
  3. 分页消掉了哪一种碎片?哪一种还留着?
  4. 找错:"用了 PagedAttention,显存就够了。"
  5. 二十条请求共享 500 token 的系统提示,能省多少缓存?
  6. 起一次 vLLM,打开 prefix caching,用同一段长前缀发十次请求,观察 TTFT 的变化。

这一章就记一句

按最大值预留,短请求会浪费掉绝大部分显存,十五个短问题就能空占三十 GiB。分页把缓存切成十六 token 一块,按需分配,浪费降到零头,还能让相同前缀共享同一批块。

真只记一句:显存不够时,先分清是"总量不够"还是"分配太糙";前者加卡,后者分页。

下一章换一个方向:不改分配,改存储——把权重和缓存本身压小。