Prhub

#23315 Opt-in strip of thinking tokens from radix cache

原始 PR 作者 hnyls2002 合并时间 2026-04-21 13:59 文件变更 4 提交数 6 评论 2 代码增减 +72 / -4

执行摘要

为推理模型添加可选的基数树缓存思考令牌剥离功能,以节省 GPU 内存。

根据PR body和关联Issue #22373,推理模型(如DeepSeek-R1、QwQ等)在请求完成时将所有输出令牌(包括思考令牌)插入基数树缓存,但由于客户端在多轮提示中丢弃思考令牌(遵循DeepSeek/OpenAI约定),这些条目变成死分支,浪费GPU内存(例如每个分支约1.3-1.6 GB)。答案令牌也因RoPE位置偏移而无法安全重用,因此剥离两者可以避免内存浪费和缓存污染。

建议精读此PR,特别关注_cache_commit_len()的设计决策和opt-in策略,它展示了如何在最小化变更下处理推理模型特有的缓存问题,代码改动集中且测试全面,是学习缓存优化和向后兼容性权衡的好例子。

讨论亮点

无review评论,因此无讨论亮点。

实现拆解

  1. 添加配置标志:在python/sglang/srt/server_args.pyServerArgs类中添加strip_thinking_cache: bool字段(默认False)和--strip-thinking-cacheCLI参数,作为功能的入口开关。
  2. 核心逻辑修改:在python/sglang/srt/managers/schedule_batch.pyReq类中新增_cache_commit_len()方法,当strip_thinking_cache启用且reasoning_tokens > 0时,返回min(self.kv_committed_len, len(self.origin_input_ids)),仅将提示前缀视为已提交缓存长度;并修改pop_committed_kv_cache()pop_overallocated_kv_cache()方法调用此辅助函数,使得思考令牌和答案令牌落入过度分配范围。
  3. 断言放宽:在python/sglang/srt/mem_cache/common.pyrelease_kv_cache()函数中,将start_p == end_p的断言条件从spec_algo is None修改为spec_algo is None and not global_server_args.strip_thinking_cache,以允许strip模式下的过度分配,避免崩溃。
  4. 测试配套:在test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py中添加test_cache_finished_req_strips_thinking()测试函数,参数化覆盖不同缓存类型(FULL/SWA/MAMBA)和页面大小,验证strip功能下仅提示前缀被缓存且思考令牌不被匹配。
文件 模块 状态 重要度
python/sglang/srt/managers/schedule_batch.py 请求管理 modified 6.46
python/sglang/srt/server_args.py 配置参数 modified 5.27
python/sglang/srt/mem_cache/common.py 缓存通用 modified 5.07
test/registered/unit/mem_cache/test_unified_radix_cache_unittest.py 单元测试 modified 5.22

关键符号

_cache_commit_len pop_committed_kv_cache pop_overallocated_kv_cache

关键源码片段

python/sglang/srt/managers/schedule_batch.py core-logic

这是核心逻辑文件,新增 `_cache_commit_len()` 方法并修改缓存提交函数,直接控制 strip 功能的行为。

def _cache_commit_len(self) -> int:
    # 报告仅提示前缀,使得思考令牌和答案令牌落入过度分配范围,
    # 并通过 release_kv_cache 回收。这是为了修复 Issue #22373。
    if get_global_server_args().strip_thinking_cache and self.reasoning_tokens > 0:
        # 当 strip 启用且存在推理令牌时,仅返回提示前缀长度与已提交长度的最小值,
        # 确保思考令牌和答案令牌被视为过度分配。
        return min(self.kv_committed_len, len(self.origin_input_ids))
    # 默认情况下返回完整的已提交缓存长度,保持向后兼容性。
    return self.kv_committed_len

评论区精华

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

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

风险与影响

  • 回归风险:修改了pop_committed_kv_cachepop_overallocated_kv_cache的核心路径,若_cache_commit_len()逻辑错误,可能导致缓存释放不当或内存泄漏。
  • 兼容性风险:功能为opt-in且默认关闭,不影响现有用户;但启用后可能改变缓存行为,需用户明确知晓。
  • 性能影响:剥离思考令牌可以减少死分支,提升缓存命中率和内存利用率,但对推理模型的多轮对话性能有正面影响。
  • 断言放宽:在common.py中跳过start_p == end_p断言可能掩盖其他非strip引起的过度分配问题,需依赖测试覆盖确保正确性。
  • 用户影响:提供可选配置,用户可启用以节省GPU内存(尤其是高并发推理场景),但需权衡缓存复用可能的变化。
  • 系统影响:减少基数树缓存中的死分支,降低内存压力,提升缓存效率,可能改善长对话性能。
  • 团队影响:引入新配置参数和缓存逻辑,需文档说明;测试配套完善,便于后续维护。
核心路径变更 缓存逻辑调整 测试覆盖

关联 Issue

#22373 Reasoning model thinking tokens pollute radix cache with unreachable entries
#22617 [Optimization] Strip thinking tokens from radix cache for reasoning models
#22950 [fix] Parser-gated two-phase cache stripping for reasoning radix caches (fixes #22373)

完整报告

参与讨论