跳转至

分层与稀疏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缓冲区上:

  1. 将层的缓存KV加载到可复用缓冲区中;
  2. 运行注意力;
  3. 将更新后的KV保存到主机内存;以及
  4. 仅在保存和任何远程读取完成后才复用缓冲区。

多个缓冲区允许传输和计算重叠。当前实现使用带有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.confmmc-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拉取。可复用缓冲区仅在两个操作都完成后才释放。

验证与限制

以下日志表示缓冲区复用已激活:

Layerwise KV cache reuse merged ... descriptors into ... descriptors using ... buffer assignments.
  • 共享缓冲区卸载目前需要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.2DeepSeek-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 --version
ls "$(clang --print-resource-dir)/include/omp.h"

如果缺少任一依赖项,请安装它:

apt-get update
apt-get install -y clang libomp-dev

如果镜像已提供特定版本的 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 通过请求元数据提供目标地址。