KVPP(实验性)¶
概述¶
KVPP(KV 流水线并行)针对非混合 MLA/SFA 模型,将原本会在 TP rank 之间按层复制的历史 KV 缓存进行分布式存储。这降低了每个 rank 的持久缓存存储占用,使相同的 HBM 容量能够容纳更多上下文 token 或并发请求。
当某一层执行时,负责其缓存的 rank 会将完整缓存广播给组内其他 rank。模型计算保留其 TP/EP/PP 配置。启用 PP 时,每个阶段在各自的缓存副本组内分配缓存并广播。该组跨越 TP rank;当在 Model Runner V2 上启用 PCP 时,跨越 PCP × TP rank;并且从不跨越 DP 副本或 PP 阶段。
KVPP 中的流水线并行指 KV 缓存的存储与通信,而模型计算保持上述并行配置。关于底层的按层 KV 存储概念,请参见 Z.ai 的 大规模编码智能体服务扩展的痛点:从 GLM-5 大规模调试中获得的经验 中的 LayerSplit。
使用场景¶
KVPP 面向长上下文推理和并发服务负载,适用于重复 KV 缓存限制可用缓存容量的场景。对于具有可复用长前缀的负载,它也可以与前缀缓存和 KV 池化结合使用。
在 950DT 产品上,使用 TP8 + EP 的单八设备节点运行 GLM-5.2-W4A8C8 时,实测 KV 缓存容量为 6.25×;使用 TP8 + PP2 + EP(38/40 层)的双八设备节点运行 GLM-5.2-W8A8C8-mxfp8 时,实测 KV 缓存容量为 5.35×。每项比较均使用相同模型、硬件和内存预算,并分别关闭和开启 KVPP。这些容量比率仅适用于性能部分中的配置;它们不是吞吐量倍数,也不保证适用于其他模型。
支持的场景¶
支持的模型¶
KVPP 支持在 Model Runner V1 和 V2 上使用非混合 MLA 和 SFA 模型。将 KVPP 与 PCP 结合使用需要 Model Runner V2。
| 注意力后端 | KVPP 支持 | 模型范围 |
|---|---|---|
| MLA | ✅ 支持 | 使用 MLA 的非混合模型 |
| SFA | ✅ 支持 | 使用 SFA 的非混合模型,例如 GLM-5.2 |
模型还必须满足 vLLM Ascend 的硬件、量化和注意力后端要求。
支持的功能¶
下表列出了与 KVPP 组合的单个功能。这并不意味着所有列出的功能都可以同时启用。
| 功能或组合 | 支持 | 条件和限制 |
|---|---|---|
| Eager 模式 | ✅ 支持 | 使用 --enforce-eager。 |
| Graph 模式 | ❌ 不支持 | KVPP 当前要求使用 Eager 模式。 |
| TP | ✅ 支持 | KV 缓存在每个缓存副本组内按层分配。 |
| EP | ✅ 支持 | 需要支持 EP 的 MoE 模型。 |
| PP | ✅ 支持 | 每个 PP 阶段独立分配并广播其缓存。 |
| 分块预填充 | ✅ 支持 | 可与 KVPP 结合使用。 |
| 前缀缓存 | ✅ 支持 | 可与 KVPP 结合使用。 |
| 异步调度 | ✅ 支持 | 可与 KVPP 结合使用。 |
| LI-C8 和 SFA-C8 缓存布局 | ✅ 支持 | 分配遵循实际的 KV 缓存规格。 |
| 固定步长 MTP | ✅ 支持 | MTP 缓存独立分配,并排除在 KVPP 层划分之外。 |
| 可变步长 MTP 和其他投机解码方法 | ❌ 不支持 | 仅支持固定步长 MTP。 |
| PCP | ✅ 支持 | 需要 Model Runner V2;缓存在 PCP × TP rank 之间共享。 |
| DCP | ❌ 不支持 | 当前不能与 KVPP 结合使用。 |
| P/D 分离 | ✅ 支持 | 使用 MooncakeConnectorV2(实验性);仅在预填充节点上启用 KVPP。 |
| KV 池化 | ✅ 支持 | 使用 AscendStoreConnector、kv_producer 或 kv_both 的 Memcache,以及异步整块加载。 |
| PCP + P/D 分离 + KVPP | ❌ 不支持 | MooncakeConnectorV2 尚不支持 PCP。 |
| PCP + KV 池化 + KVPP | ❌ 不支持 | PCP 和 KV 池化可分别与 KVPP 结合使用,但三者组合不支持。 |
- ✅ 支持:在所述条件下,该功能组合受支持。
- ❌ 不支持:该组合当前不受支持。
使用¶
通过 --additional-config 中的 enable_kvpp 启用 KVPP,并使用 Eager 模式:
vllm serve <model-path> \
--tensor-parallel-size 2 \
--enforce-eager \
--additional-config '{"enable_kvpp": true}'
将 <model-path> 替换为受支持的 MLA/SFA 模型路径,并根据模型大小和可用设备选择 TP。KVPP 组大小遵循 TP × PCP,无需单独设置。禁用 PCP 时,TP=1 无法在 rank 之间提供缓存共享收益。
如果启动命令已包含 --additional-config,请将 enable_kvpp 合并到现有 JSON 对象中。
组合功能¶
KVPP 可与 EP、PP、分块预填充、前缀缓存和异步调度结合使用。以下示例使用支持 EP 的 MoE 模型:
vllm serve <model-path> \
--tensor-parallel-size 2 \
--enable-expert-parallel \
--enable-chunked-prefill \
--enable-prefix-caching \
--async-scheduling \
--max-num-batched-tokens 32768 \
--max-num-seqs 12 \
--enforce-eager \
--additional-config '{"enable_kvpp": true}'
根据设备容量和负载调整 token 预算和最大序列数。
对于 PCP,请设置 VLLM_USE_V2_MODEL_RUNNER=1,并在启动配置中添加 --prefill-context-parallel-size。KVPP 在 PCP × TP rank 之间共享缓存;Model Runner V1 不支持 KVPP 与 PCP 结合使用。
对于 PP,请将 enable_kvpp 添加到现有 PP 启动配置中。每个阶段独立分配其缓存。KVPP 不会改变 PP 层划分。
固定步长 MTP 可与 KVPP 结合使用,但 MTP 缓存仍独立分配,并排除在 KVPP 层划分之外。请按照模型特定的配置要求设置 MTP 启动参数。
配置参数¶
| 参数 | 默认值 | 描述 |
|---|---|---|
additional_config.enable_kvpp |
false |
启用 KVPP;组大小遵循 TP × PCP。 |
--enforce-eager |
未启用 | 启用 KVPP 时必需;当前不支持图执行。 |
KVPP 会完整广播每一层一次。无需配置广播粒度或单独的 KVPP 并行大小。
KV 传输配置¶
PD 分离¶
KVPP 通过 MooncakeConnectorV2 支持 P/D 分离(实验性)。连接器代码已合并,但尚未发布。
在预填充节点上使用 kv_producer,在解码节点上使用 kv_consumer 配合 MooncakeConnectorV2。仅在预填充节点上启用 KVPP。MTP 缓存仍保持复制,并随持久目标缓存一起传输。
# Prefill node: include {"enable_kvpp": true} in --additional-config.
--kv-transfer-config '{"kv_connector":"MooncakeConnectorV2","kv_role":"kv_producer","kv_port":37000}'
# Decode node: leave KVPP disabled.
--kv-transfer-config '{"kv_connector":"MooncakeConnectorV2","kv_role":"kv_consumer","kv_port":37010}'
每个 worker 使用由 kv_port 及其并行 rank 推导出的握手端口。当两个节点运行在同一主机上时,请选择不重叠的端口范围。
Memcache 池化¶
对于 Memcache 池化,KVPP 支持 kv_producer 和 kv_both。kv_consumer 尚未测试,不在已验证的支持范围内。
按照 KV 池 中的说明配置 memcache SDK 和 MetaService,然后添加:
--kv-transfer-config '{"kv_connector":"AscendStoreConnector","kv_role":"kv_producer","kv_connector_extra_config":{"lookup_rpc_port":"0","backend":"memcache","use_layerwise":false,"load_async":true}}'
示例使用 kv_producer;也可以将 kv_role 设置为 kv_both。在此池化配置中,两种角色都会保存和加载前缀。保持 use_layerwise=false、load_async=true 和 discard_partial_chunks=true(默认值)。KVPP 不支持逐层池化、KV 事件和消费者写回。
每个 TP rank 为每个 token 块保存一个完整对象,其中包含其持久目标层及其自身的 MTP 缓存。暂存缓冲区被排除。加载会恢复这些相同的持久缓冲区;当某层执行时,现有的 KVPP 广播为其他 rank 提供数据。池查找需要所有 PP 阶段中每个非空的所有者分片。
性能¶
以下在 950DT 产品上对 GLM-5.2 的测量结果展示了其对缓存容量、首 token 时间(TTFT)和预填充吞吐量的影响。单节点和双节点部署使用不同的量化权重;请在每种部署内比较 KVPP 开启和关闭的情况。
测试配置¶
| 设置 | 单节点 | 双节点 PP |
|---|---|---|
| 硬件 | 一个节点,配备 8 个 950DT 产品 | 两个节点,每个节点配备 8 个 950DT 产品 |
| 模型 | GLM-5.2-W4A8C8 | GLM-5.2-W8A8C8-mxfp8 |
| 并行 | TP8 + EP | TP8 + PP2 + EP,38/40 层划分 |
| 通用设置 | DSA-CP、分块预填充、前缀缓存、异步调度、LI-C8、Model Runner V1、立即执行模式 | 与单节点相同 |
| 调度和内存 | max_num_batched_tokens=32768、max_num_seqs=12、gpu_memory_utilization=0.90 |
与单节点相同 |
未启用 MTP 和 SFA-C8。以下所有测量均使用 32K 调度 token 预算。
KV 缓存容量¶
服务在未覆盖块数的情况下自动计算容量。单节点数值来自池化测试配置;双节点数值来自非池化服务启动日志。
| 部署 | KVPP 禁用(token) | KVPP 启用(token) | 容量倍数 | 相同 token 容量下等效的 KV 内存节省 |
|---|---|---|---|---|
| 单节点 | 235,008 | 1,467,904 | 6.25× | 83.99% |
| 双节点 PP | 555,392 | 2,972,416 | 5.35× | 81.32% |
等效 KV 内存节省是根据容量比估算的,并不代表总 HBM 使用量有相同幅度的减少。每个 rank 仍然需要两个暂存缓冲区和独立的 MTP 缓存,因此容量收益取决于层数、TP 大小、PP 划分以及每层缓存大小。
首 token 时间¶
此测试测量长输入、零前缀命中、低并发的请求。单节点部署使用 GLM-5.2-W4A8C8,配备 8 个 950DT 产品,采用 TP8 + EP。双节点部署使用 GLM-5.2-W8A8C8-mxfp8,每个节点配备 8 个 950DT 产品,采用 TP8 + PP2 + EP(38/40 层)。其他服务设置遵循上述测试配置。
输入长度为 32K、64K 和 128K token。前缀缓存命中率为 0%,输出 1 个 token。每个结果是客户端并发为 1 时 4 个请求的平均 TTFT。正值表示延迟增加。
| 部署 | 输入长度 | KVPP 禁用 | KVPP 启用 | TTFT 变化 |
|---|---|---|---|---|
| 单节点 | 32K | 2.623 s | 2.614 s | -0.33% |
| 单节点 | 64K | 5.295 s | 5.375 s | +1.52% |
| 单节点 | 128K | 10.901 s | 11.268 s | +3.36% |
| 双节点 PP | 32K | 2.900 s | 2.905 s | +0.19% |
| 双节点 PP | 64K | 4.503 s | 4.887 s | +8.53% |
| 双节点 PP | 128K | 7.805 s | 8.665 s | +11.01% |
非池化预填充吞吐量¶
每次运行包含 40 个请求,客户端并发为 12,每个请求输入 128K token,输出 1 个 token。两种配置中均禁用了 KV 池化。
| 部署 | 前缀缓存命中率 | KVPP 禁用(输入 token/s) | KVPP 启用(输入 token/s) | 吞吐量变化 |
|---|---|---|---|---|
| 单节点 | 0% | 12,492.4 | 12,205.7 | -2.30% |
| 单节点 | 90% | 116,516.2 | 110,224.8 | -5.40% |
| 双节点 PP | 0% | 21,395.2 | 18,742.3 | -12.40% |
| 双节点 PP | 90% | 177,375.4 | 154,717.3 | -12.77% |
所有 90% 场景中的实际命中率为 89.9414%。在这些工作负载下,KVPP 增加了缓存容量,但引入了广播开销;非池化吞吐量没有提升。
使用建议¶
首先评估 KVPP 在受 KV 缓存容量限制的工作负载上的表现。使用真实的输入长度、并发和前缀分布,比较 KVPP 开启和关闭时的容量、TTFT 和吞吐量,然后选择满足服务延迟要求的配置。
广播会传输某一层所有已分配的逻辑块,而不会针对活跃请求进行过滤,因此增加缓存容量也会增加广播量。预取可以将部分通信与计算重叠,但其收益仍取决于工作负载。当缓存容量已经足够时,请使用实测的延迟和吞吐量来决定是否启用 KVPP。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 vl/vllm-ascend