Prhub

#34341 [npu] [bugfix] Fix HiCache MHA backup for NPU

原始 PR 作者 gjsheu 合并时间 2026-08-11 21:09 文件变更 2 提交数 6 评论 1 代码增减 +17 / -8

执行摘要

修复 NPU 上 HiCache MHA backup 回归

PR body 明确说明:PR #30393 generalized the MHA HiCache backup path to support packed target and draft KV buffers. However, NPUMHATokenToKVPool uses contiguous multi-layer K/V tensors for the NPU transfer kernel and intentionally does not create k_data_ptrs/v_data_ptrs。因此泛化后的代码在 NPU 上会因访问不存在的指针数组而失败,本 PR 旨在修复这一回归。

该 PR 值得快速浏览,改动虽小但揭示了一个重要的设计差异:NPU 池与 CUDA 池在内存布局上的根本不同(连续多层张量 vs. 指针数组)。关注点在于后端差异化的处理方式,以及 _validate_envelope_kv_layout 的校验逻辑。建议阅读 mha.pybackup_from_device_all_layer 的分支处理,理解 HiCache 在不同硬件上的适配模式。

讨论亮点

PR 无实质 review 讨论,仅由 sglang-npu-bot 自动审批通过(两次 APPROVED)。唯一 Issue 评论是 Bot 触发的 /tag-and-rerun-ci 命令,用于重跑 CI,属于流程自动化而非技术讨论。

实现拆解

  1. 修改 python/sglang/srt/mem_cache/pool_host/mha.pybackup_from_device_all_layer:当 io_backend == "kernel_ascend" 时,不再调用 _resolve_device_transfer_buffers,直接将 device_kv_buffers 置为 None,避免访问 NPU 池不存在的 k_data_ptrs/v_data_ptrs。其他后端(如 kernel)保持原有逻辑不变,从 _resolve_device_transfer_buffers 获取指针数组和缓冲区。
  2. 修改 python/sglang/srt/disaggregation/ascend/conn.pysend_kvcache:新增 dst_kv_item_lendst_attn_tp_size 两个可选参数,并在函数开头调用 _validate_envelope_kv_layout 校验目标端 KV 布局,确保 PP 切片或 TP 分片时指针列表与布局参数一致。
  3. 测试与配置:本 PR 未新增或修改测试文件,依赖现有 CI(run-ci 标签)覆盖 NPU 回归场景。
文件 模块 状态 重要度
python/sglang/srt/mem_cache/pool_host/mha.py 缓存池 modified 6.12
python/sglang/srt/disaggregation/ascend/conn.py PD 传输 modified 5.19

关键符号

backup_from_device_all_layer _resolve_device_transfer_buffers send_kvcache _validate_envelope_kv_layout

关键源码片段

python/sglang/srt/mem_cache/pool_host/mha.py core-logic

核心修复文件:在 backup_from_device_all_layer 中针对 kernel_ascend 后端跳过指针数组解析,避免 NPU 池访问不存在的 k_data_ptrs/v_data_ptrs。

def backup_from_device_all_layer(
    self, device_pool, host_indices, device_indices, io_backend
):
    # NPU 传输使用连续多层张量,不构建 CUDA 风格的 ptr 数组,
    # 因此 kernel_ascend 后端直接跳过指针解析,避免访问缺失属性。
    if io_backend == "kernel_ascend":
        device_kv_buffers = None
    else:
        (
            device_k_data_ptrs,
            device_v_data_ptrs,
            device_k_buffers,
            device_v_buffers,
        ) = self._resolve_device_transfer_buffers(device_pool)
        device_kv_buffers = device_k_buffers + device_v_buffers
​
    if io_backend == "kernel":
        if self.layout == "layer_first":
            if self.can_use_jit:
                # JIT 路径:直接按 ptr 数组搬运所有层
                jit_transfer_hicache_all_layer(
                    k_ptr_dst=self.k_data_ptrs,
                    v_ptr_dst=self.v_data_ptrs,
                    indices_dst=host_indices,
                    k_ptr_src=device_k_data_ptrs,
                    v_ptr_src=device_v_data_ptrs,
                    indices_src=device_indices,
                    kv_cache_dst_stride_bytes=self.token_stride_size,
                    kv_cache_src_stride_bytes=self.token_stride_size,
                    element_size=self.element_dim * self.dtype.itemsize,
                )
            else:
                transfer_kv_all_layer(
                    src_k_layers=device_k_data_ptrs,
                    dst_k_layers=self.k_data_ptrs,
                    src_v_layers=device_v_data_ptrs,
                    dst_v_layers=self.v_data_ptrs,
                    src_indices=device_indices,
                    dst_indices=host_indices,
                    item_size=self.token_stride_size,
                    num_layers=self.layer_num,
                )
        elif self.layout == "page_first":
            # ... 其他 layout 的处理(省略)
            pass
python/sglang/srt/disaggregation/ascend/conn.py core-logic

为 Ascend PD 传输增加布局校验:新增参数并调用 _validate_envelope_kv_layout,确保 KV 传输时层数与 TP 大小匹配。

def send_kvcache(
    self,
    mooncake_session_id: str,
    prefill_kv_indices: npt.NDArray[np.int32],
    dst_kv_ptrs: list[int],
    dst_kv_indices: npt.NDArray[np.int32],
    executor: concurrent.futures.ThreadPoolExecutor,
    dst_layer_ids: Optional[List[int]] = None,
    dst_device_kv_indices: Optional[npt.NDArray[np.int32]] = None,
    dst_kv_item_len: Optional[int] = None,
    dst_attn_tp_size: Optional[int] = None,
):
    if dst_device_kv_indices is not None:
        raise NotImplementedError(
            "Ascend PD transfer does not support HiSparse "
            "destination device KV indices"
        )
​
    # 校验目标端 KV 布局:层数与 attention TP 大小必须与指针列表一致,
    # 避免 PP 切片或 TP 分片时错位。
    self._validate_envelope_kv_layout(
        dst_kv_ptrs, dst_kv_item_len, dst_attn_tp_size
    )
    # ... 后续传输逻辑省略

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

  1. 回归风险mha.py 的改动只对 kernel_ascend 后端跳过指针解析,其他后端行为不变,但若 NPU 池未来开始构建 k_data_ptrs,该分支可能隐藏问题;建议后续补充 NPU 单测。
  2. 接口兼容风险conn.pysend_kvcache 新增两个可选参数,若调用方未传递,_validate_envelope_kv_layout 收到 None 时的行为未在片段中展示,需确认其容错逻辑。
  3. 覆盖不足:无直接针对 kernel_ascend 分支的单元测试,回归依赖 CI 硬件环境。

影响范围限定于 NPU(Ascend)平台:修复了 HiCache MHA backup 路径在 NPU 上的潜在崩溃,使启用 HiCache 的 NPU 用户能正常进行 K/V 备份。同时 Ascend PD 传输增加了布局校验,减少 PP/TP 分片错位风险。CUDA 等其他后端不受到影响,改动量小,风险可控。

缺少测试覆盖 核心路径变更

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论