Appearance
KV缓存与PagedAttention — 概念
传统 KV Cache 的问题
在标准 Transformer 推理中,每个请求的 KV 缓存需要预分配最大长度的连续显存:
问题:
- 显存浪费:预分配最大长度,实际使用远小于此
- 碎片化:不同请求释放后产生不连续的空闲区域
- 无法共享:相同前缀的请求各自保存独立副本
PagedAttention 原理
PagedAttention 借鉴操作系统虚拟内存的分页机制:
核心概念
| 概念 | 说明 |
|---|---|
| Block(块) | 固定大小的 KV 缓存单元,默认 16 tokens |
| Block Table(块表) | 虚拟块到物理块的映射表 |
| Block Pool(块池) | 所有可用物理块的集合 |
| Reference Counting(引用计数) | 跟踪每个物理块的引用数,支持共享 |
块的生命周期
前缀缓存(Prefix Caching)
Prefix Caching 允许具有相同前缀的请求共享 KV 缓存块:
实现方式:
- 每个 block 计算内容的哈希值
- 新请求的 block 哈希与已有 block 匹配
- 匹配成功则复用已有物理块(引用计数 +1)
- 不匹配则从空闲池分配新块
哈希计算
block_hash = hash(block_content, parent_block_hash)这是链式哈希:每个块的哈希依赖其内容和父块的哈希,确保相同序列位置上的相同内容产生相同哈希。
KV 缓存卸载(KV Cache Offloading)
当 GPU 显存不足时,vLLM 支持将 KV 缓存卸载到其他存储层级:
分层策略
| 层级 | 延迟 | 容量 | 适用场景 |
|---|---|---|---|
| GPU HBM | ~ns | 有限 | 活跃请求的 KV 缓存 |
| CPU RAM | ~us | 较大 | 被抢占的请求 |
| SSD/Disk | ~ms | 很大 | 长期不活跃的请求 |
文件系统分层(FileSystem Tiering)
新增的纯 Python 磁盘后端二级缓存层:
关键特性:
- DualQueueThreadPool:分离读写队列,读操作优先处理
- 原子写入:先写临时文件,再
rename确保一致性 - O_DIRECT I/O:绕过页缓存,减少内存拷贝
- 哈希子目录:将 KV 块按哈希映射到子目录,避免单目录文件过多
MooncakeStore 多组支持
MooncakeStore 连接器现在支持混合注意力模型的多组 KV 缓存:
- MooncakeStoreCoordinator:计算每个 KV 缓存组的 load/store mask
- 多 TokenDatabase:每组 KV cache 维护独立的
ChunkedTokenDatabase - LCM 块对齐:跨异构 KV 缓存组的块对齐(使用最小公倍数)
- 指标子系统:
MooncakeStoreConnectorStats跟踪每次操作的延迟、字节数、p90 延迟和错误率
Block 大小选择
block_size 的选择影响性能:
| block_size | 优点 | 缺点 |
|---|---|---|
| 小(8) | 更精细的显存管理,浪费更少 | 更多的元数据开销 |
| 中(16) | 平衡选择(默认) | — |
| 大(32) | 更少的元数据开销 | 更多显存浪费 |
相关概念
- Paged Attention — 分页注意力的核心算法
- KV Cache — KV 缓存的基本概念
- Continuous Batching — 调度器如何与 KV 缓存管理交互
- Prefix Caching — 前缀缓存的详细原理
- KV Cache Offloading — KV 缓存卸载到 CPU/磁盘