Prhub

#23316 [UnifiedRadixTree]: Support HiCache Framework for UnifiedRadixTree

原始 PR 作者 hzh0425 合并时间 2026-05-03 22:13 文件变更 12 提交数 23 评论 13 代码增减 +2192 / -253

执行摘要

UnifiedRadixTree 集成 HiCache,统一缓存生命周期

根据 PR 描述,该变更为 UnifiedRadixTree 添加 HiCache 支持,以统一 Full、Mamba 及相关混合组件的设备/主机缓存生命周期。它使 UnifiedRadixTree 能够为混合线性模型和 DeepSeek DSA 风格模型启用 HiCache,并提供了组件化的驱逐/加载回逻辑、显式 D-leaf/H-leaf 跟踪、辅助组件的主机端 LRU 管理,以及统一路径中的 HiCache 池/控制器集成。Issue 评论中 linjianyu233 和 icepoint666 验证了精度和多轮对话稳定性。

本 PR 是 UnifiedRadixTree 集成 HiCache 的里程碑变更,涉及缓存核心架构、多组件协同和主机层管理,值得团队精读以理解设计决策,特别是 EvictLayer 枚举和组件化加载回接口的抽象方式。建议重点关注 unified_radix_cache.pyinit_hicache 和驱逐链的调度,以及各组件中 build_hicache_transfers 的实现,这些是未来扩展 L3 的基础。

讨论亮点

核心讨论:

  • Mooncake 后端兼容性:riZZZhik 指出 UnifiedTreeNode 缺少 get_last_hash_value 方法,且 UnifiedRadixCache 缺少预取相关方法(prefetch_from_storagecheck_prefetch_progress),会在 Mooncake HiCache 后端下报错。作者 hzh0425 回应“本 PR 仅支持 L2 HiCache,L3 方法将在后续 PR 中添加”。

  • CI 环境跳过测试:ispobock 在代码审查中建议暂时移除 CUDA13 环境下不兼容的 HiCache 测试,待修复后再恢复。作者同意并在注释中标记 TODO。

  • 精度验证:linjianyu233 提交了 GSM8K 精度测试通过的结果;icepoint666 验证了 Qwen3.5-397B-A17B 模型在 L1+L2 HiCache 下的稳定性和正确性。

实现拆解

  1. LRU 列表扩展与节点属性新增:在 unified_radix_cache.py 中,UnifiedTreeNode 新增 backupedevicted 属性,用于指示 Full KV 是否已备份到主机或已被从设备驱逐。UnifiedLRUList 增加 use_host_ptr 参数,为主机 LRU 分配独立的指针槽位,避免与设备 LRU 指针冲突。

  2. 驱逐层与传输阶段枚举:在 tree_component.py 中定义 EvictLayerIntFlag:DEVICE、HOST、ALL)和 CacheTransferPhase(BACKUP_HOST、LOAD_BACK、BACKUP_STORAGE、PREFETCH),为分层驱逐和缓存迁移提供类型安全的基础。

  3. 组件接口重构:将各组件(FullComponent、MambaComponent、SWAComponent)的 evict_component 签名从 (node, is_leaf) -> int 改为 (node, target: EvictLayer) -> tuple[int, int],返回设备与主机的释放 token 数量。新增 build_hicache_transferscommit_hicache_transferdrive_host_eviction 等抽象方法,各组件按需实现主机 LRU 管理和传输构建。例如 FullComponent 在 evict_component 中同时处理设备层和主机层的释放,并在 redistribute_on_node_split 中克隆 host_value

  4. UnifiedRadixCache 主循环集成:在缓存主类中添加 init_hicacheregister_hicache_anchor_kv_shared_indices_pool_unevict_node_on_insert_for_each_component_lruevict_host 等方法,协调设备与主机间的数据传输。驱逐流程从单设备层扩展为设备层驱逐 + 主机层空间回收的顺序执行。

  5. 调度器与池组装器适配scheduler.pyhybrid_pool_assembler.py 调整导入及调用点,使其支持新的 HiCache 路径(如 Load-back 触发和主机池初始化)。

  6. 测试覆盖test_unified_radix_cache_unittest.py 新增 8 个驱逐链测试(原子释放、级联、LRU 顺序等);test_unified_radix_cache_kl.py 添加精度测试。CI 中 CUDA13 环境暂时跳过部分 HiCache 测试。

文件 模块 状态 重要度
python/sglang/srt/mem_cache/unified_radix_cache.py 缓存层 modified 8.93
python/sglang/srt/mem_cache/unified_cache_components/mamba_component.py Mamba 组件 modified 8.87
python/sglang/srt/mem_cache/unified_cache_components/full_component.py 全量组件 modified 8.85
python/sglang/srt/mem_cache/unified_cache_components/tree_component.py 树组件基类 modified 7.85
test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py 单元测试 modified 7.82
python/sglang/srt/mem_cache/unified_cache_components/swa_component.py SWA 组件 modified 7.15

关键符号

backuped evicted init_hicache register_hicache_anchor_kv_shared_indices_pool _unevict_node_on_insert _for_each_component_lru evict_host evict_component build_hicache_transfers commit_hicache_transfer drive_host_eviction node_has_component_data finalize_match_result EvictLayer CacheTransferPhase redistribute_on_node_split create_match_validator drive_eviction

关键源码片段

python/sglang/srt/mem_cache/unified_radix_cache.py core-logic

主缓存类,新增 backuped/evicted 属性、扩展 LRU 列表为双指针层,并集成 HiCache 初始化与驱逐协调逻辑。

from sglang.srt.mem_cache.unified_cache_components import (
    _NUM_COMPONENT_TYPES,
    ComponentData,
    ComponentType,
)
from sglang.srt.mem_cache.radix_cache import RadixKey
from typing import Optionalclass UnifiedTreeNode:
    counter = 0
​
    def __init__(self, tree_components: tuple[ComponentType, ...]):
        self.parent: Optional['UnifiedTreeNode'] = None
        self.key: Optional[RadixKey] = None
        # 组件数据列表,按 ComponentType 索引
        self.component_data: list[ComponentData] = [
            ComponentData() for _ in range(_NUM_COMPONENT_TYPES)
        ]
        self.last_access_time = get_and_increase_time_counter()
        self.hash_value = None # 替换了旧的 host_value 字段
        self.hit_count = 0
        # LRU 指针:长度扩展为 _NUM_COMPONENT_TYPES * 2,
        # 前半段供设备 LRU 使用,后半段为主机 LRU 保留独立槽位
        self.lru_prev: list[Optional['UnifiedTreeNode']] = [None] * (
            _NUM_COMPONENT_TYPES * 2
        )
        self.lru_next: list[Optional['UnifiedTreeNode']] = [None] * (
            _NUM_COMPONENT_TYPES * 2
        )
        self.id = UnifiedTreeNode.counter
        UnifiedTreeNode.counter += 1
​
    @property
    def backuped(self) -> bool:
        """Full KV 数据已备份到主机(根据 host_value 是否非空判断)"""
        return self.component_data[ComponentType.FULL].host_value is not None
​
    @property
    def evicted(self) -> bool:
        """Full KV 已被从设备驱逐(非根节点且 value 为空)"""
        return (
            self.parent is not None
            and self.component_data[ComponentType.FULL].value is None
        )
​
​
class UnifiedLRUList:
    def __init__(
        self,
        component_type: ComponentType,
        tree_components: tuple[ComponentType, ...],
        use_host_ptr: bool = False,
    ):
        self.component_type = component_type
        # 指针槽位计算:主机 LRU 使用偏移 slot,避免与设备 LRU 指针冲突
        self._pt: int = component_type + (
            _NUM_COMPONENT_TYPES if use_host_ptr else 0
        )
        self.head = UnifiedTreeNode(tree_components)
        self.tail = UnifiedTreeNode(tree_components)
        self.head.lru_next[self._pt] = self.tail
        self.tail.lru_prev[self._pt] = self.head
        self.cache: dict[int, UnifiedTreeNode] = {}
python/sglang/srt/mem_cache/unified_cache_components/mamba_component.py core-logic

Mamba 组件,在匹配验证器中增加主机值检查,在 finalize_match_result 中处理主机命中长度以触发加载回。

from sglang.srt.mem_cache.hicache_storage import PoolName, PoolTransfer
from sglang.srt.mem_cache.unified_cache_components.tree_component import (
    CacheTransferPhase,
    ComponentType,
    EvictLayer,
    TreeComponent,
    get_and_increase_time_counter,
)class MambaComponent(TreeComponent):
    component_type = ComponentType.MAMBA
​
    def __init__(self, cache, params):
        super().__init__(cache, params)
        self.enable_mamba_extra_buffer = params.enable_mamba_extra_buffer
        self._mamba_pool_host = None # HiCache 启用时保存主机 mamba pool
​
    def create_match_validator(self):
        ct = self.component_type
        # HiCache:被驱逐但有主机备份的节点同样作为有效匹配边界
        return lambda node: (
            node.component_data[ct].value is not None
            or node.component_data[ct].host_value is not None
        )
​
    def finalize_match_result(self, result, params, value_chunks, best_value_len):
        # ... 省略 Copy-on-Write 部分 ...
        # HiCache:如果 mamba 值在设备上被驱逐但主机上有备份,
        # 则确保 host_hit_length >= 1 以触发后续加载回
        host_node = result.last_host_node
        cd = host_node.component_data[self.component_type]
        if cd.value is None and cd.host_value is not None:
            result = result._replace(
                host_hit_length=max(result.host_hit_length, 1)
            )
        return result._replace(mamba_branching_seqlen=branching_seqlen)
python/sglang/srt/mem_cache/unified_cache_components/full_component.py core-logic

全量组件,新增主机层驱逐支持,重构 redistribute_on_node_split 以复制 host_value。

import heapq
from sglang.srt.mem_cache.hicache_storage import PoolName, PoolTransfer
from sglang.srt.mem_cache.unified_cache_components.tree_component import (
    CacheTransferPhase,
    ComponentType,
    EvictLayer,
    TreeComponent,
)class FullComponent(TreeComponent):
    component_type = ComponentType.FULL
​
    def __init__(self, cache, params):
        super().__init__(cache, params)
        allocator = cache.token_to_kv_pool_allocator
        # 当存在 SWA 时,只释放 full-attention KV
        if ComponentType.SWA in cache.tree_components:
            self._free_full = allocator.full_attn_allocator.free
        else:
            self._free_full = allocator.free
        self._full_kv_pool_host = None # HiCache 启用时保存主机 KV pool
​
    def evict_component(self, node, target=EvictLayer.DEVICE):
        cd = node.component_data[self.component_type]
        freed = 0
        host_freed = 0
​
        # 设备层驱逐
        if EvictLayer.DEVICE in target and cd.value is not None:
            self._free_full(cd.value)
            freed = len(cd.value)
            self.cache.component_evictable_size_[self.component_type] -= freed
            # 注意:cd.value = None 延迟到 _cascade_evict,
            # 因为 SWA 的 free 仍需读取 Full.value
​
        # 主机层驱逐
        if EvictLayer.HOST in target and cd.host_value is not None:
            host_freed = len(cd.host_value)
            if self._full_kv_pool_host is not None:
                self._full_kv_pool_host.free(cd.host_value)
            cd.host_value = None
        return freed, host_freed
​
    def redistribute_on_node_split(self, new_parent, child):
        ct = self.component_type
        new_parent.component_data[ct].lock_ref = child.component_data[ct].lock_ref
        child_cd = child.component_data[ct]
        split_len = len(new_parent.key)
        # 分割设备值
        if child_cd.value is not None:
            new_parent.component_data[ct].value = child_cd.value[:split_len].clone()
            child_cd.value = child_cd.value[split_len:].clone()
        # 分割主机值(HiCache)
        if child_cd.host_value is not None:
            new_parent.component_data[ct].host_value = child_cd.host_value[
                :split_len
            ].clone()
            child_cd.host_value = child_cd.host_value[split_len:].clone()

评论区精华

Mooncake 后端兼容性(缺少 get_last_hash_value 和 prefetch 方法) 正确性

riZZZhik 在 scheduler.py 第 2046 行评论指出 UnifiedTreeNode 缺少 get_last_hash_value 方法,且 UnifiedRadixCache 缺少 prefetch_from_storage 等预取方法,会在 Mooncake HiCache 后端下报错。

结论:hzh0425 回应本 PR 仅支持 L2 HiCache,L3 方法将在后续 PR 中添加。 · 已解决

CI CUDA13 环境兼容性 测试

ispobock 在 test_unified_radix_cache_kl.py 第 265 行建议暂时移除 HiCache 测试,因为 CI CUDA13 环境不兼容。

结论:作者同意,添加了 TODO 注释并跳过测试,待后续修复。 · 已解决

风险与影响

  1. Mooncake 后端兼容性风险:当前 PR 未实现 get_last_hash_value 和 prefetch 方法,与 Mooncake 后端的完整集成需要等待 L3 支持 PR。若在 L3 环境下运行会直接报错。
  2. CI 环境覆盖不足:CUDA13 环境下的 HiCache 测试被跳过,可能导致回归问题未能及时发现。
  3. SWA 组件功能缺失swa_component.pyevict_componenttarget=HOST 时直接返回 (0, 0),暂不支持主机层缓存,TODO 已标注,但未来可能成为性能瓶颈。
  4. 核心驱逐路径变更:驱逐逻辑从单层扩展为双层,若主机层管理不当可能引入内存泄漏或死锁,新增的单元测试已经覆盖,仍需生产环境验证。
  • 用户:使用 UnifiedRadixTree 的混合模型用户可直接受益,减少设备内存占用,提升缓存命中率和吞吐量。需要启用 --enable-hierarchical-cache 等参数。
  • 系统:缓存子系统新增主机层生命周期管理,需要额外主机内存,但可通过 SGLANG_MM_FEATURE_CACHE_MB 等环境变量控制。调度器需要处理新引入的 Host 缓存状态,对现有调度流程有少量侵入。
  • 团队:后续需完成 SWA HiCache 支持(#23391)和 L3 预取功能。CI 环境需跟进 CUDA13 兼容性修复。
Mooncake 接口缺失 CUDA13 兼容跳过测试 SWA 无主机层 核心驱逐路径变更

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论