SFA 远程 D2H 连接器设计¶
目的与范围¶
SfaRemoteD2HConnector 将 SFA KV 缓存从远程 Prefill 工作节点传输到由 SparseKVOffloadManager 拥有的 Decode 侧布局中。它是用于带有稀疏 Decode 卸载的 Prefill-Decode 分离架构的 KV 连接器 V1 实现。
RD2H 表示远程设备到本地主机。该连接器采用拉取模型:
- Prefill 注册并暴露其 NPU KV 缓存。
- Decode 拥有目标端并通过 MemFabric 拉取数据。
- Prefill 通过 ZMQ 发布源元数据和逐层就绪状态。
- Decode 在每次读取成功或失败后确认每一层。
该连接器提供传输和完成信号机制。Decode 的 top-k 常驻缓存算法保留在 SparseKVOffloadManager 和 SFA 卸载注意力后端中。AscendStore 分层 Prefill KV 缓存卸载是一个可选且独立配置的功能。
有关面向用户的配置和部署约束,请参阅 分层与稀疏 KV 缓存卸载。
数据所有权¶
Decode 目标端包含主 KV 和索引器 KV,二者具有不同的所有权规则。
| 组件 | Prefill 源端 | Decode 目标端 |
|---|---|---|
| 主 K/V | 常规分页 NPU 缓存,在 Prefill TP 各 rank 间复制 | 由 SparseKVOffloadManager 拥有的 TP 共享固定 CPU 池 |
| 索引器 | Rank 本地 NPU 缓存 | 每个 Decode TP rank 上的完整 rank 本地 NPU 副本 |
| LIC8 缩放 | 可选张量,遵循索引器布局 | 可选的 rank 本地 NPU 张量 |
每个 Decode TP rank 将主 KV 的不相交部分传输到共享 CPU 池中。主块 ID 和索引器块 ID 在调度器元数据和线协议消息中保持分离。统一的 KV 缓存组可能将它们映射到同一个 vLLM 块组,但连接器不依赖于组顺序。
请求生命周期¶
Decode 分配与会合¶
对于 do_remote_prefill=true 的请求,Decode 将剩余的提示词 token 报告为异步匹配,并让 vLLM 分配主目标和索引器目标。它在本地记录这些块 ID,通过代理元服务器通告其主机、基础端口、并行拓扑和缓存 token 数量,然后清除 do_remote_prefill。
Decode 块 ID 永远不会离开 Decode。Prefill 仅接收发布其源端所需的端点和缓存 token 信息。
Prefill 调度¶
代理将请求以 do_remote_decode=true 分发给 Prefill。Prefill 跟踪其源块 ID、Decode 端点、已计算和已传输的 token 数量以及分块完成状态。
对于分块 Prefill,元数据更新涵盖新完成的块。最后一个分块还包含其部分块。OpenAI 提示词列表在 Decode 上展开为子请求;代理收集所有子请求的会合元数据,并将原始列表一次性分发给 Prefill。
层传输¶
在一层的 KV 分散完成后,Prefill 记录一个 NPU 事件并排队一个发送任务。后台发送器等待该事件,每个连接发布一次 MemFabric 元数据,并发送就绪消息。
Decode 验证源端和目标端布局,跨就绪请求构建描述符,并执行一次同步批量的 MemFabric 读取。然后它针对该层回复成功或失败。
控制协议¶
Prefill 使用 ZMQ DEALER 套接字。每个 Decode TP rank 拥有一个 ZMQ ROUTER。线协议消息是位置化的 msgpack 元组。
MemFabric 元数据¶
Prefill 在每个连接上、在任何就绪消息之前发送一次此消息:
层元数据包含张量组 ID、基地址、块字节长度、块大小缩放、主张量数量以及索引器是否存在。Decode 按 ZMQ 身份存储该元数据并回复 ACK。
层就绪¶
Prefill 为每一层和每个 Decode 端点发送一条消息:
每个 read_reqs 条目包含外部请求 ID、Prefill 主块 ID 和索引器块 ID,以及主目标和索引器目标起始偏移量。
done_ext_ids 标识最终分块已到达最后一个承载缓存的层的请求。
完成回复¶
协议扩展会追加元组字段。接收方使用元组长度保护,因此缺失的不等 TP 字段保留旧版 ratio=1 行为。
正确性不变量¶
物理存储完成¶
分层 Prefill 可能为多个逻辑层重用同一个物理缓冲区。在缓冲区被覆盖之前,每个收到前一个所有者就绪状态的 Decode 端点必须确认其读取完成。
因此,Prefill 为每个物理主存储槽或索引器存储槽维护一个完成事件,该事件根据存储地址而非层名称推断。主槽和索引器槽独立跟踪。每条就绪消息都会收到回复,包括当 Decode rank 拥有零个块时,因此缓冲区重用不会因缺少空操作确认而死锁。
请求完成¶
Decode 仅在以下条件满足后才能调度请求:
- 映射到该 Decode rank 的每个 Prefill 贡献者报告最终层完成;并且
- 每个 Decode TP rank 达到终态成功或失败状态。
贡献者状态以外部请求ID为键。工作进程通过TP CPU组收集终止状态,并将外部ID映射回内部vLLM请求ID。
物理存储完成与请求完成保持独立。前者保护Prefill内存复用;后者控制Decode调度。
布局验证¶
在读取之前,Decode验证主张量数量和字节长度、索引器的存在性和字节长度、LIC8缩放因子的存在性和布局,以及目标块范围。主张量、索引器或缩放因子的不匹配会导致该层失败,而不是留下部分更新的目标数据。
不等的Prefill和Decode TP¶
支持的拓扑结构为:
对于Prefill秩 p_rank:
映射到一个Decode秩的ratio个Prefill秩构成一个贡献者组:
- 主KV在Prefill上复制。只有贡献者成员
0拉取该Decode秩的主份额,防止重复写入和多余带宽。 - 每个Decode秩需要完整的索引器。贡献者传输不相交的索引器范围,其并集为完整副本。LIC8缩放因子遵循相同范围。
- 没有块的贡献者仍然确认该层并参与请求完成。
- 来自同一贡献者的重复最终层信号不会推进完成。
AscendMultiConnector集成¶
当RD2H与AscendStore逐层Prefill卸载组合时,RD2H完成提供者首先运行于:
wait_for_layer_load;save_kv_layer;以及on_kv_cache_written。
此顺序在AscendStore发布或复用同一物理缓冲区之前关闭RD2H存储复用门。RD2H将wait_for_layer_send暴露为逐层Prefill路径使用的等待器。
在调度器侧,即使选择了另一个连接器进行查找,RD2H也必须观察到完整的块组。requires_full_blocks_on_update_after_alloc标志保留了该行为。
端口和端点标识¶
只有Decode绑定连接器控制套接字:
全局DP秩(而非主机本地秩)防止跨引擎的端口复用。Decode通过元服务器通告基础端口;Prefill添加映射的Decode TP秩。最高Decode TP端口和最终映射的远程端口均被验证。
故障与兼容性¶
- 元服务器传输错误会重试。HTTP错误响应被视为已送达,以兼容旧版代理行为。
- 非零的MemFabric读取结果变为
READ_FAILED。Decode将请求标记为失败,并将其目标块标记为无效。 - Prefill记录层错误并释放受影响的存储事件,使计算路径抛出异常而不是死锁。
- 连接器层不重试数据读取。
- Prefill和Decode必须使用兼容的协议和缓存布局版本。混合使用旧版和不平等TP贡献者构建可能导致重复写入。
- 进程级MemFabric引擎绑定到一个角色、设备和主机名。
实现与测试¶
实现位于
vllm_ascend/distributed/kv_transfer/kv_p2p/sfa_pd_rd2h/。其主要测试为
tests/ut/kv_offload/下的test_sfa_pd_rd2h_connector.py、test_memfabric_transfer_engine.py和
test_ascend_multi_connector.py。
使用tools/test_memfabric_pd_read.py独立于vLLM调度验证真实双端点MemFabric注册和读取。
当前限制¶
- MemFabric是唯一支持的传输后端。
- Decode稀疏卸载支持TP,但不支持CP或PP。
- Prefill TP必须大于或等于Decode TP,且能被其整除。
- 未实现连接器级数据读取重试。