分层与稀疏KV缓存卸载¶
1. 背景¶
KV缓存是长序列推理中NPU内存的主要开销。Prefill和Decode具有不同的性能特征,因此它们采用不同的卸载策略:
| 阶段 | 特性 | NPU上保留的内容 |
|---|---|---|
| Prefill | 分层KV缓存卸载 | 少量可复用的全层KV缓冲区 |
| Decode | 稀疏KV缓存卸载 | 索引器缓存和热top-k KV缓冲区 |
Prefill有足够的计算量来重叠全层传输。Decode则不然,因此它将完整的KV缓存保留在主机内存中,仅加载稀疏注意力所需的top-k条目。
该设计源自 RFC #48203。其实验表明,该设计有潜力大幅增加序列长度或批处理大小,但实际结果取决于模型、硬件和配置。
2. Prefill的分层KV缓存卸载¶
工作原理¶
分层Prefill KV缓存卸载将许多逻辑层映射到较少数量的物理NPU缓冲区上:
- 将层的缓存KV加载到可复用缓冲区中;
- 运行注意力;
- 将更新后的KV保存到主机内存;以及
- 仅在保存和任何远程读取完成后才复用缓冲区。
多个缓冲区允许传输和计算重叠。当前实现使用带有Memcache后端的AscendStoreConnector。
要求¶
- 使用带有
kv_role: "kv_producer"的专用Prefill节点。 - 使用
backend: "memcache"和use_layerwise: true。 - 使用MLA、SFA或DSA注意力后端并启用eager执行。
- 在Prefill节点上先安装MemFabric Hybrid,然后安装Memcache Hybrid。 Memcache是主机KV池后端;MemFabric是其依赖项。
- 配置
mmc-meta.conf和mmc-local.conf,并在启动Prefill之前启动MetaService。
首先构建并安装MemFabric Hybrid:
git clone -b release/1.2 https://gitcode.com/Ascend/memfabric_hybrid.git
cd memfabric_hybrid
bash script/build_and_pack_run.sh
bash output/memfabric_hybrid-1.2.0_linux_aarch64.run
然后构建并安装Memcache Hybrid。其MemFabric子模块必须使用相同的release/1.2分支:
git clone https://gitcode.com/Ascend/memcache.git
cd memcache
git submodule update --init 3rdparty/
git -c submodule.3rdparty/memfabric_hybrid.branch=release/1.2 \
submodule update --remote 3rdparty/memfabric_hybrid
bash script/build_and_pack_run.sh --build_mode RELEASE
bash output/memcache_hybrid-1.1.0_linux_aarch64.run
在启动Prefill之前准备主机:
echo 200000 > /proc/sys/vm/nr_hugepages
source /usr/local/memcache_hybrid/set_env.sh
source /usr/local/memfabric_hybrid/set_env.sh
export PYTHONHASHSEED=0
使用MetaService和Config Store端点配置mmc-meta.conf:
ock.mmc.meta_service_url = tcp://<META_HOST>:5000
ock.mmc.meta_service.config_store_url = tcp://<CONFIG_STORE_HOST>:6000
ock.mmc.meta.lease_ttl_ms = 30000
ock.mmc.log_level = error
在每个Prefill节点上配置mmc-local.conf:
ock.mmc.meta_service_url = tcp://<META_HOST>:5000
ock.mmc.local_service.config_store_url = tcp://<CONFIG_STORE_HOST>:6000
ock.mmc.log_level = error
ock.mmc.local_service.world_size = 256
ock.mmc.local_service.protocol = device_sdma
ock.mmc.local_service.dram.size = 10GB
这两个文件必须使用相同的MetaService端点,并且LocalService的Config Store端点必须与MetaService的Config Store端点匹配。将world_size设置为部署中LocalService实例的最大数量。在带有HCCS的A3上使用device_sdma,在A2和其他带有设备RoCE的系统上使用device_rdma。将dram.size设置为每个LocalService贡献的主机内存容量。
在启动Prefill之前导出两个配置路径,并在单独的进程中启动MetaService:
export MMC_META_CONFIG_PATH=/usr/local/memcache_hybrid/latest/config/mmc-meta.conf
export MMC_LOCAL_CONFIG_PATH=/usr/local/memcache_hybrid/latest/config/mmc-local.conf
python -c "from memcache_hybrid import MetaService; MetaService.main()"
配置¶
要仅启用分层Prefill KV缓存卸载,请在Prefill启动命令中添加以下选项。此示例将第0层保持独立,并将所有其他层分配给三个可复用缓冲区:
--kv-transfer-config '{
"kv_connector": "AscendStoreConnector",
"kv_role": "kv_producer",
"kv_connector_extra_config": {
"backend": "memcache",
"use_layerwise": true,
"layerwise_num_shared_buffers": 3,
"layerwise_independent_layers": [0]
}
}'
| 参数 | 描述 |
|---|---|
backend |
主机KV池后端。共享缓冲区分层卸载需要"memcache"。 |
use_layerwise |
启用逐层KV传输和可复用的NPU缓冲区。 |
layerwise_num_shared_buffers |
可复用NPU缓冲区的数量。更多缓冲区占用更多内存,但提供更多重叠传输和计算的机会。 |
layerwise_independent_layers |
保留专用缓冲区的层。默认值为[0];"all"禁用跨层复用。 |
缓冲区数量取决于工作负载。从两到四个缓冲区开始,并根据NPU内存和传输带宽进行调整。
当Decode上也启用了稀疏KV缓存卸载时,请将此单连接器配置替换为第4章中的Prefill MultiConnector配置。AscendStoreConnector将每个层缓冲区卸载到Memcache,而SfaRemoteD2HConnector则暴露相同的缓冲区供Decode通过MemFabric拉取。可复用缓冲区仅在两个操作都完成后才释放。
验证与限制¶
以下日志表示缓冲区复用已激活:
- 共享缓冲区卸载目前需要Memcache和eager执行。
- 上下文并行尚未验证。
- AscendStore自身的分层P/D传输不支持TP不匹配。当
SfaRemoteD2HConnector执行P/D传输时,此限制不适用。 - MTP层和可选的SFA索引器缓存会自动参与缓冲区复用和内存核算。
3. Decode的稀疏KV缓存卸载¶
工作原理¶
稀疏KV缓存卸载将完整的KV缓存存储在主机内存中。NPU保留:
- 用于选择重要令牌的索引器缓存;以及
- 包含最近使用的top-k KV条目的热缓冲区。
在每个Decode步骤中,热缓冲区中已有的条目会被复用,仅从主机内存加载缓存未命中的条目。SfaRemoteD2HConnector将Prefill KV缓存直接传输到Decode端存储。
要求¶
- 使用稀疏注意力模型,例如 GLM-5.2 或 DeepSeek-V3.2。
- 使用分离式 P/D 部署,并仅在 Decode 阶段启用该功能。
- 支持张量并行;不支持上下文并行和流水线并行。
- 需要 Model Runner V1。
- 安装 MemFabric Hybrid 1.2 版本。当前版本需要 NPU 驱动
25.5.1或更高版本。 - 如果镜像中尚未提供 Clang 和 OpenMP,请安装它们。
如果镜像中尚未安装 MemFabric Hybrid 1.2:
pip uninstall -y memfabric_hybrid
git clone https://gitcode.com/Ascend/memfabric_hybrid.git -b release/1.2
cd memfabric_hybrid
bash script/build_and_pack_run.sh
bash output/memfabric_hybrid-1.2.0_linux_aarch64.run
export MEMFABRIC_HYBRID_EXTEND_LIB_PATH=/usr/local/memfabric_hybrid/1.2.0/aarch64-linux/lib64
检查 Clang 和 OpenMP 是否可用:
如果缺少任一依赖项,请安装它:
如果镜像已提供特定版本的 Clang 但缺少 OpenMP,
请安装匹配的软件包,例如针对 Clang 17 安装 libomp-17-dev。
配置¶
将以下选项添加到 Decode 启动命令中:
--additional-config '{
"sparse_kv_offload_config": {
"enabled": true,
"topk_buffer_size": 4096,
"dram_size_per_dp_GB": 128
}
}' \
--kv-transfer-config '{
"kv_connector": "SfaRemoteD2HConnector",
"kv_role": "kv_consumer",
"kv_port": 20050,
"kv_connector_extra_config": {
"transfer_backend": "memfabric",
"use_layerwise": true
}
}'
| 参数 | 描述 |
|---|---|
topk_buffer_size |
设备热缓冲区大小。它必须至少为 index_topk,并且能被 block_size 整除。以 index_topk 的两倍作为起点是可行的。 |
dram_size_per_dp_GB |
每个 DP rank 预留的主机内存。它必须能容纳完整的 KV 缓存。TP rank 共享此内存池。 |
keep_device_kv_cache |
仅用于调试的选项,用于保留完整的设备 KV 缓存。在生产环境中请保持其为 false。 |
4. 组合使用¶
组合后的数据流如下:
flowchart LR
PHost["Prefill host KV pool"]
PNPU["Prefill NPU<br/>reusable layer buffers"]
DHost["Decode host<br/>full KV cache"]
DNPU["Decode NPU<br/>indexer + top-k buffer"]
PHost <-->|"Layerwise offload / load"| PNPU
PNPU -->|"Remote D2H pull"| DHost
DHost -->|"Top-k cache misses"| DNPU
Prefill 配置¶
将以下选项添加到 Prefill 启动命令中。MultiConnector 允许
Prefill 将 KV 保存到 Memcache,并将相同的层缓冲区暴露给 Decode:
--kv-transfer-config '{
"kv_connector": "MultiConnector",
"kv_role": "kv_producer",
"kv_connector_extra_config": {
"connectors": [
{
"kv_connector": "SfaRemoteD2HConnector",
"kv_role": "kv_producer",
"kv_port": 20050,
"kv_connector_extra_config": {
"transfer_backend": "memfabric"
}
},
{
"kv_connector": "AscendStoreConnector",
"kv_role": "kv_producer",
"kv_connector_extra_config": {
"backend": "memcache",
"use_layerwise": true,
"layerwise_num_shared_buffers": 3,
"layerwise_independent_layers": [0]
}
}
]
}
}'
不要在 Prefill 上启用 sparse_kv_offload_config。可复用的 Prefill 缓冲区
只有在 AscendStore 保存它且 Decode 完成远程 D2H 读取后才会被释放。
Decode 配置¶
使用第 3 章中的稀疏 KV 缓存卸载配置。Decode 拥有目标端, 并将主 KV 直接拉取到其主机内存池中;索引器 KV 保留在 rank 本地的 NPU 内存中。
代理¶
在 Prefill 和 Decode 就绪后启动逐层代理:
python examples/disaggregated_prefill_v1/load_balance_proxy_layerwise_server_example.py \
--host 127.0.0.1 \
--port 9000 \
--prefiller-hosts 127.0.0.1 \
--prefiller-ports 8100 \
--decoder-hosts 127.0.0.1 \
--decoder-ports 8200
在多节点部署中使用可访问的地址。不要通告
0.0.0.0,因为 Decode 会调用代理的 /v1/metaserver 端点。将
推理请求发送到代理端口(本例中为 9000)。
部署检查清单¶
- 在 Prefill 和 Decode 上都将
transfer_backend设置为memfabric。 - Prefill 的 TP 必须大于或等于 Decode 的 TP,并且能被其整除。
kv_port是 Decode 控制端口的基地址。预留decode_data_parallel_size * decode_tensor_parallel_size个连续端口。 Prefill 不绑定这些端口;Decode 通过请求元数据提供目标地址。