KVPP 设计¶
1. 背景¶
MLA/SFA 模型会在张量并行(TP)各 rank 之间复制历史 KV 缓存,从而限制了长上下文和并发请求的容量。KVPP 在每个 TP 组内按层分发持久化缓存,并在计算需要时广播某一层的缓存。
模型执行保留其 TP/EP/PP 配置。调度器保留完整的逻辑缓存规格和块编号。
2. 设计¶
KVPP 结合了层归属、两个可复用的暂存缓冲区,以及提前预取一层:
TP group within one PP stage
Owner rank: persistent main KV + indexer KV for its layers
|
+-- Full-layer broadcast --> Other ranks: scratch buffer
|
+-- Wait --> Compute current layer
Prefetch next layer
2.1 层归属¶
每个流水线并行(PP)阶段按层索引对其本地目标层进行排序,并将连续、均衡的分区分配给其 TP 组中的归属 rank。某一层的主 KV、indexer KV 和量化组件构成一个 bundle,由单一归属者持有。MTP 缓存在每个需要它的 rank 上独立分配,不参与目标层分区。
每个 rank 为其拥有的层分配持久化存储,并为其他层复用两个暂存缓冲区。目标层执行序号交替选择暂存缓冲区,使当前层和下一次预取使用独立的存储。
2.2 物理布局与内存预算¶
bundle 内的各组件是连续的,每个组件包含该层的所有逻辑块。各层不需要全局连续分配;只有每个广播 bundle 必须是连续的。布局遵循实际的缓存规格,包括主 KV、indexer 数据和 scales。
每个 rank 上的物理预算为:
Physical bytes per block
= Sum of owned target-bundle bytes per block
+ Local MTP-cache bytes per block
+ 2 * Largest target-bundle bytes per block
Available blocks = floor(Available KV memory / Physical bytes per block)
worker 将得到的块容量转换为供 planner 使用的逻辑内存预算。物理分配使用最终的块数量,在保留逻辑缓存管理的同时按照 KVPP 布局分配存储。
2.3 全层广播与预取¶
当任何实际请求中存在先前已计算的 token 时,前向传播会从预取第一层开始。每个注意力层在投影之后、首次访问或写入 KV 缓存之前等待其广播,然后开始预取下一层。
每次广播都保持在当前 PP 阶段的 KVPP 组内。归属者一次性广播完整的 bundle,包括所有组件和所有已分配的逻辑块。其他 rank 将其接收到对应的暂存缓冲区中。计算完成后,归属者保留更新后的缓存以供后续前向传播使用。
预取使用独立的传输流。ready 事件在广播前建立数据依赖,completion 事件确保在等待返回之前设备传输已完成。下一层的广播可以与当前层的计算重叠;重叠程度取决于通信和计算开销。无历史的前向传播、dummy 运行和 profiling 路径不会广播历史缓存。
3. 支持与约束¶
| 领域 | 范围 |
|---|---|
| 模型与执行 | eager 模式下的非混合 MLA/SFA 模型;Model Runner V1 和 V2 |
| 支持的组合 | TP、EP、PP、chunked prefill、prefix caching、异步调度、固定步 MTP |
| 缓存布局 | 根据实际规格分配,包括 LI-C8 和 SFA-C8 |
| 不支持 | 图执行、PCP、DCP、分离式 prefill/decode、可变步 MTP |
KVPP 以通信换取持久化缓存容量。每个 rank 仍需要两个按最大目标层大小配置的暂存缓冲区,以及其独立的 MTP 缓存。因此,内存节省取决于层数、TP 大小和每层缓存大小。广播流量随已分配的块数量增长;载荷不会按请求或活动块进行过滤。
4. 使用方法¶
通过模型启动参数启用 KVPP。KVPP 组大小跟随 TP 大小:
vllm serve <model-path> \
--tensor-parallel-size 2 \
--enforce-eager \
--additional-config '{"enable_kvpp": true}'
enable_kvpp 默认为 false。启用 PP 后,每个阶段在其自身的 TP 组内分配归属者并进行广播。