Prhub

#33639 [Hicache][2/2]Support Mamba branching in Unified Radix Cache with HiCache

原始 PR 作者 Chen-0210 合并时间 2026-08-10 17:16 文件变更 7 提交数 25 评论 3 代码增减 +88 / -25

执行摘要

支持 Mamba 组件增量备份到 Host,避免重复拷贝 Full KV

PR body 明确指出:'Enable component-only incremental HiCache backups for Host-backed nodes without copying Full KV again. this pr initially limits incremental backup to the write-through path and skipped when the node already has a pending backup transfer to avoid concurrent backups on the same node. Write back support is not implemented yet.' 这是对 PR #31181 的 follow-up,目标是解决 Host 已有 Full KV 时,Mamba 分支状态新增后无法持久化导致缓存命中率低的问题。作者给出的 benchmark 显示 6.4K 共享前缀命中从 1/10 提升到 10/10,Token 命中率从 15.74% 提升到 64%。

建议精读。该 PR 展示了如何在不改变整体备份框架的前提下,通过组件委托(needs_incremental_backup)实现细粒度的增量持久化,是理解 HiCache 组件化备份扩展点的关键样例。值得关注的设计决策:备份规格生成与执行解耦、用零长度 anchor 表达 component-only 备份、以及并发备份的 pending 防护。若你正在做多模态/状态模型的缓存持久化,可参考此模式。

讨论亮点

本 PR 无实质性的 review 评论(review_comments 为 0),仅有两位维护者 ispobock 与 hzh0425 的 APPROVED。hzh0425 留言 'Looks good, thanks'。PR 内的 3 条 Issue 评论均为作者触发的 CI 命令(/tag-run-ci-label 与两次 /rerun-failed-ci),不涉及技术讨论。

实现拆解

  1. 组件接口扩展:在 python/sglang/srt/mem_cache/unified_cache/components/tree_component.pyTreeComponent 基类新增 needs_incremental_backup(node) 默认返回 False 的方法;SWAComponent 覆写为始终 False(SWA 走 tombstone 语义,不需要增量备份);MambaComponent 覆写为 data.value is not None and data.host_value is None,即设备上有、Host 上没有时才需要增量备份。

  2. 插入路径决策重构:在 python/sglang/srt/mem_cache/unified_cache/unified_tree_core.py 中新增 _needs_incremental_component_backup()(聚合各组件判断)和 _should_backup_after_insert()(统一决策)。原 _insert_tail_step 只在新建叶子时触发备份,现改为:新叶子沿用原命中计数判断;已存在节点需满足 enable_hicache && !is_write_back && node.backuped && write_through_pending_id is None && 存在组件需要增量备份 才追加 BackupKV 动作,从而避免对同一节点并发备份。

  3. 后备规格生成改造_build_backup_spec() 在节点已 backuped 时将 device_value 置为空张量(表示不需要 Full KV 传输),并跳过 host_value 已存在的组件,只收集缺失的组件传输,形成 component-only 备份规格。commit_backup() 也改为仅当 host_indices.numel() > 0 时才提交 KV anchor 传输。

  4. 执行层与主机池适配python/sglang/srt/mem_cache/unified_radix_cache.py_execute_and_commit_kv_backup() 从“已备份节点直接跳过”改为“没有任何剩余传输(device_value.numel() == 0 and not comp_xfers)才跳过”,让组件传输可被执行;python/sglang/srt/mem_cache/memory_pool_host.pybackup_from_device_all_layer() 将 anchor 备份改为条件执行,零长度 anchor 表示组件-only 备份。

  5. 测试配套test/registered/unit/mem_cache/test_unified_radix_cache_unittest.pytest_mamba_branching_from_host_full_is_reusable_after_insert 中新增断言:写入后 Full Host 池可用空间不变、仅 Mamba Host 池少一个 slot,并验证再次匹配时能从 Host 恢复 Mamba 状态。

文件 模块 状态 重要度
python/sglang/srt/mem_cache/unified_cache/unified_tree_core.py 缓存核心 modified 7.87
python/sglang/srt/mem_cache/unified_cache/components/mamba_component.py Mamba 组件 modified 6.07
python/sglang/srt/mem_cache/memory_pool_host.py 主机池 modified 5.97
python/sglang/srt/mem_cache/unified_cache/components/tree_component.py 组件基类 modified 5.29
python/sglang/srt/mem_cache/unified_radix_cache.py 统一缓存 modified 5.23
python/sglang/srt/mem_cache/unified_cache/components/swa_component.py SWA 组件 modified 5.22
test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py 单元测试 modified 4.88

关键符号

_needs_incremental_component_backup _should_backup_after_insert needs_incremental_backup _build_backup_spec commit_backup backup_from_device_all_layer _execute_and_commit_kv_backup

关键源码片段

python/sglang/srt/mem_cache/unified_cache/unified_tree_core.py core-logic

核心决策逻辑所在:新增 _needs_incremental_component_backup 与 _should_backup_after_insert,重构 _build_backup_spec 支持 component-only 备份规格,修改 commit_backup 支持零长度 anchor。

# unified_tree_core.py 中的关键决策逻辑def _needs_incremental_component_backup(self, node: UnifiedTreeNode) -> bool:
    # 聚合所有非 BASE 组件的增量备份需求,任一组件需要即为 True
    return any(
        component.needs_incremental_backup(node)
        for component in self.components
        if component.component_type != BASE_COMPONENT_TYPE
    )def _should_backup_after_insert(self, state: _InsertWalkState) -> bool:
    """判断 insert 目标是否需要进行 Host 备份。"""
    # 新叶子仍走原命中计数逻辑
    if state.is_new_leaf:
        return self._inc_hit_count_and_check(
            state.target_node, state.params.chunked
        )
​
    # 已存在节点(非新叶子):只有在 HiCache 开启、write-through 模式、
    # 节点已备份过、无并发 pending 备份、且存在组件需要增量备份时才触发。
    node = state.target_node
    return (
        self.enable_hicache
        and not self.is_write_back
        and node.backuped
        and node.write_through_pending_id is None
        and self._needs_incremental_component_backup(node)
    )def _insert_tail_step(self, state: _InsertWalkState) -> None:
    """刷新 LRU 并追加终末备份动作。"""
    if state.target_node is not self.root_node:
        for component in self.components:
            if component.component_type == BASE_COMPONENT_TYPE:
                continue
            component.refresh_lru(
                LRURefreshPhase.INSERT_END, state.target_node, self.root_node
            )
​
    # 由统一决策函数决定是否追加 BackupKV 动作
    if self._should_backup_after_insert(state):
        state.pending_actions.append(
            self._build_backup_kv_action(state.target_node)
        )
python/sglang/srt/mem_cache/unified_cache/components/mamba_component.py core-logic

MambaComponent 实现 needs_incremental_backup,决定 Mamba 状态是否需要在 Host 有缺失时进行增量备份,是整个功能的触发源头。

# mamba_component.py 中的增量备份判断class MambaComponent(TreeComponent):
    component_type = ComponentType.MAMBA
​
    # ... 初始化逻辑省略 ...
​
    def needs_incremental_backup(self, node: UnifiedTreeNode) -> bool:
        # 设备上有 Mamba 状态(value 非 None)但 Host 还没有(host_value 为 None)时,
        # 说明该状态是设备上新建的,需要增量持久化到 Host。
        data = node.component_data[self.component_type]
        return data.value is not None and data.host_value is None
python/sglang/srt/mem_cache/memory_pool_host.py core-logic

backup_from_device_all_layer 支持零长度 anchor,使 component-only 备份在主机池层面跳过 Full KV 传输、只做组件池传输。

# memory_pool_host.py 中的备份入口适配def backup_from_device_all_layer(
    self,
    device_pool,
    host_indices,
    device_indices,
    io_backend,
    pool_transfers: Optional[list] = None,
) -> None:
    # 1. Anchor (KV) 备份:零长度 anchor 表示仅组件备份
    if host_indices.numel() > 0:
        anchor_host_indices, anchor_device_indices = self._normalize_backup_indices(
            self.anchor_entry, host_indices, device_indices, io_backend
        )
        self.anchor_entry.host_pool.backup_from_device_all_layer(
            self.anchor_entry.device_pool,
            anchor_host_indices,
            anchor_device_indices,
            io_backend,
        )
​
    # 2. Extra pool 备份:组件传输照常执行
    for transfer in pool_transfers or []:
        entry = self.entry_map.get(transfer.name)
        if entry is None or transfer.host_indices is None:
            continue
        transfer_host_indices, transfer_device_indices = (
            self._normalize_backup_indices(
                entry,
                transfer.host_indices,
                transfer.device_indices,
                io_backend,
            )
        )
        entry.host_pool.backup_from_device_all_layer(
            entry.device_pool,
            transfer_host_indices,
            transfer_device_indices,
            io_backend,
        )

评论区精华

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

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

风险与影响

  1. write-back 路径未支持_should_backup_after_insert 明确限定 not self.is_write_back,在 write-back 策略下设备上新增的 Mamba 状态不会触发增量备份,可能被后续 eviction 丢弃,导致 Host 上的 Mamba 状态过期(代码注释与 PR body 均已声明此限制)。
  2. 并发备份防护依赖 pending 标记:通过 write_through_pending_id is None 防止同一节点并发备份,但若该标记清理不及时或异常中断,可能漏掉必要的组件备份。
  3. 零长度 anchor 语义memory_pool_host.pycommit_backup 依赖 host_indices.numel() > 0 判断是否执行 Full KV 传输,若调用方传入非 tensor 或维度不一致的对象会出错;当前所有调用方均传 tensor,风险可控。
  4. 组件备份范围有限:目前仅 Mamba 组件实现增量备份,SWA 显式返回 False,未来若新增其他组件需各自实现语义,容易遗漏。

主要影响使用 Mamba 架构 + HiCache(--enable-hierarchical-cache + write_through)的用户:共享前缀场景下,从 Host Full KV 重建 Mamba 分支状态后,新状态能自动增量备份,避免下次 evict 后丢失,缓存命中率显著提升(benchmark 中 Token 命中率从 15.74% 提升到 64%)。对非 Mamba 用户无行为变化;对 write-back 策略无影响(当前未启用)。代码改动集中在 mem_cache 模块,涉及约 7 个文件,核心逻辑在 unified_tree_core.py,对团队后续扩展其他组件的增量备份提供了接口范式。

write-back 路径未支持 并发备份依赖 pending 标记 零长度 anchor 语义依赖 numel 判断 组件备份范围仅限 Mamba

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论