Paged Cache策略
Paged Cache(分页缓存)
如果说 Dynamic Cache 是最容易想到的实现方式,那么 Paged Cache 就是目前业界最主流的实现方式之一。
它最早由 vLLM 提出,其核心思想借鉴了操作系统的分页内存(Paging)。
Dynamic Cache 存在什么问题?
假设有一个用户正在聊天,随着模型不断生成 Token:
KV Cache
□□□□□□□□□□□□□□□□□□□□□□□□□□□□新的 KV 会不断追加到末尾,这看起来没有问题。但是服务器里同时服务很多用户:
用户 A
□□□□□□□□□□□□□□□□□□□□
用户 B
□□□□□□□□□□□□
用户 C
□□□□□□□□□□□□□□□□□□□□□□□□□□由于每个人的上下文长度不同,有人很快结束聊天,有人还在继续生成。于是:
用户 A
□□□□□□□□□□□□□□□□□□□□
用户B(释放)
□□□□□□□□□□□□□□
用户 C
□□□□□□□□□□□□□□□□□□□□□□GPU 显存就会出现用户B释放的一大块可能用不上的内存碎片(Memory Fragmentation),随着时间推移,碎片会越来越多。即使还有足够显存,也可能找不到需要的连续的大块空间。
Paged Cache 如何解决?
Paged Cache 不再要求一整段 KV 连续存放,而是把 KV 分成很多固定大小的小块。
例如每页:
16 Token那么原本:
□□□□□□□□□□□□□□□□□□□□□□□□□□□□用户 A:
A1 A2 A3用户 B:
B1 B2 B3 B4用户 C:
C1 C2GPU 实际存储:
┌────────────────────────────────────────────┐
│A1│C1│B1│A2│B2│C2│A3│B3│B4│....│....│....│..│
└────────────────────────────────────────────┘看起来已经完全打散。但是系统维护了一张映射表:
用户 A
A1 → A2 → A3
用户 B
B1 → B2 → B3 → B4
用户 C
C1 → C2所以模型看到的仍然是 A1→A2→A3,它不知道底层实际上已经分散在 GPU 的不同位置。这和现代操作系统的虚拟内存分页几乎是同一个思想。
为什么分页之后效率更高?
假设用户聊天结束,开始释放显存中的空间。
如果使用 Dynamic Cache,需要释放整块连续空间,这样操作可能导致中间留下大量碎片。
而 Paged Cache 只需要根据指针释放对应位置的内存即可,即便有新加入的空间需求,也只需要见缝插针就好。
Paged Cache 对 Batch 推理更加友好
现代推理服务器通常不会一次只服务一个用户,而是:
用户 A
用户 B
用户 C
...
一起推理如果采用连续内存,每个用户需要申请不同大小的缓存,Batch 调度会非常困难。
而分页之后,每个请求都只是:
Page
Page
PageGPU 可以自由组合这些 Page。
因此,Paged Cache 天然适合:
- 大规模 Batch
- 多用户推理
- 高吞吐服务器
这也是为什么 vLLM 的吞吐量相比很多传统推理框架提升明显。
Paged Cache 的优点
相比传统的 Dynamic Cache:
- 减少 GPU 显存碎片。
- 无需频繁申请连续大块内存。
- 更容易支持高并发 Batch 推理。
- 显著提升 GPU 显存利用率。
- 更适合超长上下文。
因此,目前很多高性能推理框架都采用了类似思想:
- vLLM
- TensorRT-LLM(部分实现采用类似 Block 管理思想)
- SGLang
虽然具体实现细节有所不同,但核心目标都是让 KV Cache 像操作系统管理内存一样,按固定大小进行分页管理,而不是作为一整块连续内存。
它和 Dynamic Cache 的区别
| 对比项 | Dynamic Cache | Paged Cache |
|---|---|---|
| 存储方式 | 连续追加(Append) | 固定大小分页(Page/Block) |
| 内存布局 | 连续 | 逻辑连续,物理可不连续 |
| 显存碎片 | 容易产生 | 极少 |
| 高并发 | 一般 | 非常优秀 |
| Batch 推理 | 管理复杂 | 更容易调度 |
| 工程复杂度 | 简单 | 较高 |
RoLingG | 博客
评论(0)