Prhub

#35081 Using unified radix tree by default for all case

原始 PR 作者 hzh0425 合并时间 2026-08-21 10:45 文件变更 10 提交数 4 评论 7 代码增减 +140 / -80

执行摘要

统一 radix 树设为默认,弃用 SGLANG_ENABLE_UNIFIED_RADIX_TREE

PR body 明确指出:'Unified RadixTree is set as the default tree, which has previously cover SWA/Mamba/HiCache. Now officially includes all cases including Full Only. Deprecate SGLANG_ENABLE_UNIFIED_RADIX_TREE env.' 即统一树已经过多个场景验证,希望收敛默认行为,降低用户配置成本,并为后续缓存能力(如 HiCache、存储后端)提供统一入口。

值得精读,尤其关注 registry.py 中工厂选择链的收敛以及 unified_radix_cache.py 新增的兼容接口。该 PR 展示了如何将实验性能力转正为默认路径,并保持特例旁路,适合作为 SGLang 缓存架构的入门文档。建议合入后关注纯 full-attention 模型的性能回归数据。

讨论亮点

PR 没有 review 评论;issue 中作者通过 /rerun-test 触发了几次 CI 重跑,包括 test_disaggregation_decode_radix_cache.pytest_hicache_storage_3fs_backend.pytest_scripted_runtime_core.py 等,最终均通过。其中一次失败的测试被作者判定为与本 PR 无关。主要关注点是确保默认切换后现有关键路径仍然绿。

实现拆解

  1. 精简缓存工厂选择链(python/sglang/srt/mem_cache/registry.py):在 default_radix_cache_factory 中删除 SGLANG_ENABLE_UNIFIED_RADIX_TREE 判断、hybrid SWA/SSM 分支、enable_hierarchical_cache 下的 HiRadixCache 分支以及最终的 RadixCache 兜底,所有常规路径统一收敛到 _create_unified_radix_cache;仅保留纯 SWA 模型、LMCache、FlexKV 等显式特例。这样减少了多套实现并存带来的行为漂移。
  2. 补齐 UnifiedRadixCache 兼容接口(python/sglang/srt/mem_cache/unified_radix_cache.py):新增 query_storage_hit_length 用于同步探测 L3 存储可复用前缀长度,内部对 TP 组执行 all-reduce MIN 并做 page 对齐;新增 is_load_back_event_done 镜像 HiRadixCache 的加载事件状态,供 disagg decode 恢复状态机(DecodeHiCacheTransferMixin)门控使用。
  3. 扩展 HostKVCache 聚合转发(python/sglang/srt/mem_cache/memory_pool_host.py):为多池聚合类增加 get_size_per_tokenget_split_heads_page_buffer_meta,转发到底层 anchor pool,保证 UnifiedRadixCache 对 host pool 接口的调用可用。
  4. 清理启动校验与环境变量(python/sglang/srt/server_args.py、python/sglang/srt/environ.py):移除 MiMoV2 对 SGLANG_ENABLE_UNIFIED_RADIX_TREE 的强制要求;environ 中相应配置键标记为弃用,避免误导。
  5. 同步测试与文档test_registry.py 将“设置 env 才用 unified”改为“默认就是 unified”,新增 hierarchical + full attention、hybrid 等场景测试,删除 fallback_to_radix_cache 测试;scripted_runtime 增加 resolve_node / to_node_handle 以兼容统一树的 NodeId 句柄;components/README.md 更新说明。
文件 模块 状态 重要度
python/sglang/srt/mem_cache/registry.py 缓存工厂 modified 6.75
python/sglang/srt/mem_cache/unified_radix_cache.py 统一树 modified 7.71
python/sglang/srt/mem_cache/memory_pool_host.py 内存池 modified 6.19
python/sglang/srt/server_args.py 启动参数 modified 6.09
python/sglang/srt/environ.py 环境配置 modified 5.39
test/registered/unit/mem_cache/test_registry.py 测试 modified 7.02
python/sglang/test/scripted_runtime/context/radix.py 脚本运行时 modified 5.97
python/sglang/test/scripted_runtime/req_handle.py 脚本运行时 modified 4.27
python/sglang/test/scripted_runtime/context/lock_ref_exhauster.py 脚本运行时 modified 4.19
python/sglang/srt/mem_cache/unified_cache/components/README.md 文档 modified 1.54

关键符号

default_radix_cache_factory _create_unified_radix_cache query_storage_hit_length is_load_back_event_done get_size_per_token get_split_heads_page_buffer_meta resolve_node to_node_handle

关键源码片段

python/sglang/srt/mem_cache/registry.py dependency-wiring

缓存工厂选择链的核心修改,决定了所有模型的默认缓存实现。

def default_radix_cache_factory(ctx: TreeCacheBuildContext) -> BasePrefixCache:
    """Built-in Radix Cache selection chain.    本 PR 后,常规路径默认统一走 UnifiedRadixCache,仅保留少量显式特例。
    """
    server_args = ctx.server_args
    params = ctx.params
​
    # 禁用 radix cache 但启用了 host_pool 回退时,仍需 unified 树来支撑
    if (
        ctx.disable_radix_cache
        and get_disagg().disaggregation_decode_retraction_backup == "host_pool"
    ):
        return _create_unified_radix_cache(ctx, server_args, params)
​
    # chunked prefill + 禁用 radix cache 时使用 ChunkCache(含 SWA 变体)
    if ctx.effective_chunked_prefill_size is not None and ctx.disable_radix_cache:
        if not ctx.is_hybrid_swa:
            from sglang.srt.mem_cache.chunk_cache import ChunkCache
            return ChunkCache(params)
        if ctx.full_tokens_per_layer == 0:
            from sglang.srt.mem_cache.chunk_cache import PureSWAChunkCache
            return PureSWAChunkCache(params)
        from sglang.srt.mem_cache.chunk_cache import SWAChunkCache
        return SWAChunkCache(params)
​
    # 实验性的 C++ radix tree 仍然保留开关
    if envs.SGLANG_EXPERIMENTAL_CPP_RADIX_TREE.get():
        # lazy import 避免 JIT 开销
        from sglang.srt.mem_cache.radix_cache_cpp import RadixCacheCpp
        logger.info("Using experimental C++ radix tree implementation.")
        return RadixCacheCpp(params=params, server_args=server_args)
​
    # 纯 SWA 模型(无 full attention 层)继续使用专用缓存
    if ctx.is_hybrid_swa and ctx.full_tokens_per_layer == 0:
        from sglang.srt.mem_cache.pure_swa_radix_cache import PureSWARadixCache
        return PureSWARadixCache(params=params)
​
    # LMCache / FlexKV 等外部后端仍然优先
    if get_memory().enable_lmcache:
        from sglang.srt.mem_cache.storage.lmcache.lmc_radix_cache import LMCRadixCache
        return LMCRadixCache(
            params=params,
            model_config=ctx.model_config,
            tp_size=ctx.tp_size,
            rank=ctx.tp_rank,
            tp_group=ctx.tp_group,
        )
​
    if get_memory().enable_flexkv:
        import os
        from sglang.srt.mem_cache.storage.flexkv import _flexkv_factory
        # 将 CLI 配置转发给 FlexKV 实际读取的环境变量
        if get_memory().flexkv_config_file and not os.environ.get("FLEXKV_CONFIG_PATH"):
            os.environ["FLEXKV_CONFIG_PATH"] = get_memory().flexkv_config_file
        return _flexkv_factory(ctx)
​
    # 其余所有情况默认使用 UnifiedRadixCache
    return _create_unified_radix_cache(ctx, server_args, params)
python/sglang/srt/mem_cache/unified_radix_cache.py core-logic

新增 query_storage_hit_length 和 is_load_back_event_done,补齐统一树对外接口。

def query_storage_hit_length(
    self,
    last_host_node_id: NodeId,
    new_input_tokens: list[int],
    last_hash: Optional[str] = None,
    prefix_keys: Optional[list[str]] = None,
) -> int:
    """Synchronously probe L3 storage for the reusable prefix length.    该接口用于替代异步 prefetch 的同步查询,返回可直接复用的前缀长度;
    若未启用存储、速率受限或 key 过短,则返回 0。
    """
    # 未启用存储或 cache controller 缺失时直接返回
    if (
        not self.enable_storage
        or self.cache_controller is None
        or self.cache_controller.prefetch_rate_limited()
    ):
        return 0
​
    # 构造与 prefetch 相同的 RadixKey,确保查询口径一致
    extra_key, cache_salt = self.tree_core.prefetch_anchor_info(last_host_node_id)
    prefetch_key = RadixKey(
        new_input_tokens,
        extra_key=extra_key,
        is_bigram=self.tree_core.is_eagle,
        cache_salt=cache_salt,
    ).page_aligned(self.page_size)
    if len(prefetch_key) < self.prefetch_threshold:
        return 0
​
    # 通过 controller 的存储命中查询接口获取命中数
    from sglang.srt.mem_cache.hybrid_cache.hybrid_cache_controller import (
        PrefetchOperation,
    )
​
    operation = PrefetchOperation(
        "__storage_hit_query__",
        prefetch_key,
        last_hash,
        prefix_keys,
    )
    _, storage_hit_count = self.cache_controller._storage_hit_query(operation)
​
    # 多卡环境下取所有 attention 组的最小值,保证各 rank 口径一致
    storage_hit_count_tensor = torch.tensor(storage_hit_count, dtype=torch.int)
    self._all_reduce_attn_groups(
        storage_hit_count_tensor, torch.distributed.ReduceOp.MIN
    )
    storage_hit_count = storage_hit_count_tensor.item()
​
    # 对齐到 page_size 边界,避免跨页引用不完整数据
    storage_hit_count -= storage_hit_count % self.page_size
    return storage_hit_count

评论区精华

默认切换后的 CI 回归测试 测试

作者在合并前对 disaggregation 和 hicache 相关测试发起多次 /rerun-test,以确保默认切换不影响现有关键路径。

结论:重跑全部通过,其中一个失败被判定为与本 PR 无关。 · 已解决

风险与影响

  1. 默认路径变更:所有非特例模型的缓存实现从 RadixCache / HiRadixCache 切换到 UnifiedRadixCache,纯 full-attention 模型此前未经过大规模验证,可能存在前缀命中率或内存分配行为差异。
  2. 新增分布式调用query_storage_hit_length 中执行 _all_reduce_attn_groups(..., ReduceOp.MIN),每次调用都会引入一次 TP 组集合通信,若在高频查询路径上使用可能带来延迟开销。
  3. 事件索引风险is_load_back_event_done 直接访问 layer_done_counter.events[consumer_index],若 consumer_index 越界或事件未初始化会抛异常,需要调用方保证索引有效。
  4. 环境变量弃用:依赖 SGLANG_ENABLE_UNIFIED_RADIX_TREE 的部署脚本不再生效,需迁移到默认配置或显式 --radix-cache-backend 指定。

用户影响:无需再设置环境变量即可获得统一树能力;但旧环境变量被弃用,设置后可能产生告警或未来版本移除。系统影响:默认缓存层变更会影响 prefill 命中率、显存管理以及 disagg 加载流程;统一树支持 Host 侧存储和 HiCache,为后续功能演进(如跨层复用、存储扩展)铺平道路。团队影响:缓存实现分支大幅减少,降低维护成本,但需要保证回归测试覆盖,尤其是 full-attention、纯 SWA、LMCache/FlexKV 等特例。

默认行为变更 核心缓存路径切换 弃用环境变量 集合通信开销

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论