Prhub

#54272 [Bugfix][KV Connector] Fix Mooncake physical-block transfer length

原始 PR 作者 zhewenl 合并时间 2026-09-02 04:23 文件变更 3 提交数 5 评论 10 代码增减 +208 / -7

执行摘要

修复 Mooncake 物理块传输长度计算错误,防止 KV 数据重叠损坏。

Mooncake 在传输注意力 KV 数据时,当逻辑块大小(如 Kimi-K3 的 768-token)大于内核物理块大小(64-token)时,会静默损坏数据。原因是 PR #46807 合并了逻辑 ID 扩展,但遗漏了将传输长度按物理块比例缩放,导致描述符重叠。修复确保物理块描述符使用正确的字节长度。

该 PR 值得精读,因为它修复了一个关键的数据损坏 bug,且设计决策清晰:通过除法缩放和运行时检查确保传输长度正确。对于维护 Mooncake 或分布式 KV 传输的工程师,重点关注 register_kv_caches 中的长度计算和测试覆盖模式。

讨论亮点
  • andakai 确认问题根源:在 PR #46807 中遗漏了缩放传输长度,因为测试的 MLA 比例为 1,未暴露问题。对于比例大于 1 的连续注意力缓存,每个逻辑块扩展为多个物理块,传输长度应使用 page_size_bytes / ratio。
  • stmatengss 建议添加更全面的测试,覆盖传输长度、指针重叠和连续块合并,以锁定重叠不变量。PR 已通过 test_build_transfer_params_uses_non_overlapping_physical_pages 部分覆盖,但可能未完全满足建议。

实现拆解

  1. 修改 register_kv_caches 方法:在 vllm/distributed/kv_transfer/kv_connector/v1/mooncake/mooncake_connector.py 中,对于连续注意力块,将 page_size_bytes 除以 _physical_blocks_per_logical_kv_block,得到正确的物理块传输长度 kv_block_len。并添加断言确保整除,以及运行时检查 kv_block_len 不超过 block_len,防止传输重叠。
  2. 新增回归测试:在 tests/v1/kv_connector/unit/test_mooncake_connector_hybrid_mamba.py 中添加两个测试:test_register_kv_caches_scales_attention_len_to_kernel_block 验证注册时传输长度缩放;test_build_transfer_params_uses_non_overlapping_physical_pages 验证传输参数构建时指针和长度正确,无重叠。
  3. 修正现有测试:在 tests/v1/kv_connector/unit/test_mooncake_connector.py 中调整 MLA 缓存张量大小,使其字节步长匹配缓存规范页面大小,确保测试在修改后仍通过。
文件 模块 状态 重要度
vllm/distributed/kv_transfer/kv_connector/v1/mooncake/mooncake_connector.py 连接器 modified 6.92
tests/v1/kv_connector/unit/test_mooncake_connector_hybrid_mamba.py 测试 modified 7.2
tests/v1/kv_connector/unit/test_mooncake_connector.py 测试 modified 4.06

关键符号

register_kv_caches _build_transfer_params

关键源码片段

vllm/distributed/kv_transfer/kv_connector/v1/mooncake/mooncake_connector.py core-logic

核心源码修改,修复传输长度计算逻辑。

# 在 register_kv_caches 方法中,修复连续注意力块的传输长度计算
for region_cache in region_caches:
    base_addr = region_cache.data_ptr()
    block_len = region_cache.stride(0) * region_cache.element_size()
    region_base_addresses.append(base_addr)
​
    if isinstance(layer_spec, AttentionSpec) and block_is_contiguous:
        # 确保 page_size_bytes 能被物理块比例整除
        assert (
            layer_spec.page_size_bytes
            % self._physical_blocks_per_logical_kv_block
            == 0
        )
        # 传输长度应为物理块字节大小,而非逻辑块大小
        kv_block_len = (
            layer_spec.page_size_bytes
            // self._physical_blocks_per_logical_kv_block
        )
    else:
        kv_block_len = block_len
    # 安全检查:传输长度不能超过物理块步长,防止重叠
    if kv_block_len > block_len:
        raise RuntimeError(
            "Mooncake transfer length exceeds physical block stride "
            f"for {layer_name}: kv_block_len={kv_block_len}, "
            f"block_len={block_len}."
        )
    self.block_len_per_layer.append(block_len)
    self.kv_block_len_per_layer.append(kv_block_len)
    self.registered_layer_names.append(layer_name)
    # ... 其余注册逻辑
tests/v1/kv_connector/unit/test_mooncake_connector_hybrid_mamba.py test-coverage

新增关键测试,验证传输长度缩放和非重叠指针。

@pytest.mark.asyncio
async def test_build_transfer_params_uses_non_overlapping_physical_pages():
    # 测试设置:比例 ratio=4,物理块大小 = 页面大小 // ratio
    kv_cache_config = make_hybrid_gdn_kv_cache_config(block_size=16)
    worker = object.__new__(MooncakeConnectorWorker)
    worker.shutdown = noop_shutdown
    worker.use_mla = False
    worker.engine = SimpleNamespace(batch_register_memory=lambda *_: 0)
    worker.is_kv_consumer = True
    worker.tp_rank = 0
    worker.tp_size = 1
    worker.transfer_topo = SimpleNamespace(local_replicates_kv_cache=False)
    worker.kv_cache_config = kv_cache_config
    # 设置层级规范和索引
    worker._layer_specs = {
        layer_name: group.kv_cache_spec
        for group in kv_cache_config.kv_cache_groups
        for layer_name in group.layer_names
    }
    worker._layer_group_indices = {
        layer_name: group_index
        for group_index, group in enumerate(kv_cache_config.kv_cache_groups)
        for layer_name in group.layer_names
    }
    ratio = 4
    worker._physical_blocks_per_logical_kv_block = ratio
    fa_spec = kv_cache_config.kv_cache_groups[0].kv_cache_spec
    physical_page_bytes = fa_spec.page_size_bytes // ratio
    # 创建物理块缓存张量
    fa_cache = torch.empty(
        kv_cache_config.num_blocks * ratio,
        physical_page_bytes,
        dtype=torch.uint8,
    )
    worker.register_kv_caches({"model.layers.0.self_attn": fa_cache})
    # 构建传输区域和元数据
    local_regions = worker._get_transfer_regions(...)
    remote_base_addr = 0x100000
    remote_regions = [TransferRegion(..., block_len=physical_page_bytes, ...)]
    # 模拟发送元数据
    send_meta = SendBlockMeta(..., local_block_ids=[[local_logical_block], []], ...)
    xfer_meta = MooncakeXferMetadata(..., req_blocks={...})
    # 调用 _build_transfer_params 并验证无重叠
    (src_ptrs, dst_ptrs, lengths, err_reqs, err_msg) = await worker._build_transfer_params(...)
    assert lengths == [physical_page_bytes] * ratio # 每个物理块长度正确
    # 验证指针间隔等于物理块大小
    assert all(
        next_ptr - ptr == physical_page_bytes
        for ptr, next_ptr in zip(src_ptrs, src_ptrs[1:])
    )

评论区精华

传输长度缩放缺失根因 正确性

`andakai` 指出在 PR #46807 中遗漏了缩放传输长度,因为测试的 MLA 比例是 1,没有暴露问题。对于比例大于 1 的连续注意力缓存,每个逻辑块扩展为多个物理块,传输长度应使用 `page_size_bytes / ratio`。

结论:修复通过除法缩放传输长度,确保物理块描述符不重叠。 · 已解决

测试覆盖完整性 测试

`stmatengss` 建议添加更全面的测试,覆盖传输长度、指针重叠和连续块合并,以锁定重叠不变量。当前测试部分覆盖了这些场景,但可能未完全满足建议。

结论:PR 已通过关键测试验证核心修复,但可能遗留部分覆盖场景。 · 部分解决

风险与影响

  • 回归风险:修改核心传输长度逻辑可能影响其他使用连续注意力布局的模型,但修复已通过 Kimi-K3 端到端评估验证,准确率匹配基线。
  • 性能风险:除法运算和运行时检查引入微小开销,但可忽略。
  • 兼容性风险:修复仅影响比例大于 1 的场景,比例等于 1 的 MLA 模型行为不变。
  • 用户影响:修复 Mooncake 分布式传输中的数据损坏,确保 KV 缓存一致性,提升推理准确性,尤其在并发高负载下。
  • 系统影响:增强 Mooncake 连接器的鲁棒性,支持更灵活的块大小配置,为后续异构 TP 共享等功能铺路。
  • 团队影响:明确了逻辑块与物理块比例的处理规范,减少类似 bug 复现。
核心路径变更 潜在回归

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论