Prhub

#25419 Port SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN from deepseek_v4_dev

原始 PR 作者 yhyang201 合并时间 2026-05-16 08:39 文件变更 2 提交数 1 评论 2 代码增减 +6 / -1

执行摘要

修复 SWA 逐出边界 env 变量未生效 bug

修复 issue #24857 中报告的高并发 disagg decode 下 SWA tail-only preallocation 导致的 AssertionError: swa_attn_allocator.available_size() <= swa_attn_allocator.size。原因是 deepseek_v4_dev 中已存在的 env 变量 SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN 在 #24857 移植 SWA tail preallocation 优化到 main 时未被包含,导致 main 分支忽略了该 flag,使用了更保守的逐出阈值。

值得精读的场景:关注 SWA cache 逐出策略的开发者;希望了解多分支间 env 变量移植实践的读者。推荐与 #24857 和 swa_radix_cache.py_insert_helper 的 case 3 对照阅读。

讨论亮点

无 review 讨论。该 PR 只有一个 commit,获得了一次 Approved review。

实现拆解

  1. 注册新环境变量:在 python/sglang/srt/environ.pyEnvs 类中新增 SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN = EnvBool(False),默认关闭。
  2. 修改逐出逻辑:在 python/sglang/srt/managers/schedule_batch.py_evict_swa 方法中,根据环境变量值计算逐出阈值:若启用则使用 pre_len - sliding_window_size(不保留边距),否则使用 pre_len - sliding_window_size - page_size(保留一个 page 边距)。
  3. 该改动仅影响 SWA 逐出逻辑,不对其他模块产生副作用。test 覆盖:本次变更未添加新的单元测试,依靠现有 CI 覆盖。
文件 模块 状态 重要度
python/sglang/srt/managers/schedule_batch.py 调度器 modified 5.75
python/sglang/srt/environ.py 配置 modified 4.75

关键符号

_evict_swa

关键源码片段

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

核心逻辑变更:在 `_evict_swa` 方法中根据环境变量动态计算逐出阈值。

def _evict_swa(self, req: Req, pre_len: int):
    assert self.tree_cache.supports_swa(), "prefix cache must support swa"
    sliding_window_size = self.tree_cache.sliding_window_size
​
    # 截断 SWA 逐出边界,避免逐出仍在滑动窗口内的 token
    assert (
        req.cache_protected_len % self.tree_cache.page_size == 0
    ), "cache_protected_len must be page aligned"
    req.swa_evicted_seqlen = max(req.swa_evicted_seqlen, req.cache_protected_len)
​
    # 根据环境变量决定是否保留 page_size 边距
    # 该边距用于保护 radix tree 插入点,防止叶节点被 tombstone
    # 导致多轮对话 SWA cache 泄漏
    if envs.SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN.get():
        # 激进:直接逐出到滑动窗口边界
        evict_threshold = pre_len - sliding_window_size
    else:
        # 保守:保留一个 page 的边距(默认行为)
        evict_threshold = pre_len - sliding_window_size - self.tree_cache.page_size
​
    new_swa_evicted_seqlen = max(
        req.swa_evicted_seqlen,
        evict_threshold,
    )
​
    # 确保逐出长度 page 对齐
    if self.tree_cache.page_size > 1:
        new_swa_evicted_seqlen = (
            new_swa_evicted_seqlen // self.tree_cache.page_size
        ) * self.tree_cache.page_size
​
    if new_swa_evicted_seqlen > req.swa_evicted_seqlen:
        free_slots = self.req_to_token_pool.req_to_token[
            req.req_pool_idx, req.swa_evicted_seqlen : new_swa_evicted_seqlen
        ]
        self.token_to_kv_pool_allocator.free_swa(free_slots)
        req.swa_evicted_seqlen = new_swa_evicted_seqlen

评论区精华

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

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

风险与影响

风险极低。该变更仅新增一个环境变量注册和对应的一条 if-else 分支,默认行为(未设置变量时)与之前完全一致。当显式设置 SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN=1 时,逐出边界更激进,可能影响多轮对话中 SWA cache 的复用,但这本就是预期的优化行为。

影响范围有限。仅影响设置了 SGLANG_OPT_SWA_EVICT_DROP_PAGE_MARGIN=1 的部署(如 InferenceX CI),修复了这些场景下的 assertion 崩溃。对默认部署无影响。

默认行为不变 env 变量开关 无新增测试覆盖

关联 Issue

#24857 Optimize SWA memory preallocation for disaggregated decode

完整报告

参与讨论