Prhub

#35030 Add bit-exact guard for extra_buffer_lazy

原始 PR 作者 ispobock 合并时间 2026-08-16 23:34 文件变更 1 提交数 2 评论 2 代码增减 +42 / -3

执行摘要

为 extra_buffer_lazy 新增 bit-exact 缓存守卫

PR body 明确指出 extra_buffer_lazy 此前完全没有 bit-exact 覆盖,且它是「a different code path rather than the same code under a flag」:请求只持有 1 个 ping-pong 槽位而不是 2 个,第二个槽位仅在跨 track 边界的 forward 上分配并释放,checkpoint 因此落在逐 forward 选择的槽位上,选错就会恢复另一个请求的 conv 窗口。关联 issue #34899 确立了「任何非零 KL 都是 KV/状态复用 bug」的契约,并把 mamba extra_buffer_lazy 列为路线图待覆盖项,因此需要为这条独立路径单独建立回归守卫。

值得精读,尤其是关注测试方法论而非实现细节:为什么 extra_buffer_lazy 需要独立测试类而不是在现有类上加一个 flag;revert-then-red 如何把「守卫确实能检测缺陷」变成可验证的硬证据;KL_DIV_THRESHOLDTRACK_INTERVALPAGE_SIZEMAX_NEW_TOKENS 等常量如何与模型特性(SWA 窗口、page 边界、chunked prefill)相互绑定。对于想为 speculative decoding、MTP、DSpark 等路线图项补守卫的开发者,这是一个可直接复制的模板。

讨论亮点

该 PR 没有任何 review 评论(review_comments_count = 0)。PR body 提供了最关键的论证:lazy 路径与常规 extra_buffer 是不同代码路径,不能复用同一测试类加 flag,必须单独起服务实例;灵敏度用 revert-then-red 证明——revert #34184 后 lazy 类读到 5.56e-06,非 lazy 类同会话读到 1.18e-05,因此「两个都触发」,不能说 lazy 守卫更弱;实测耗时 655s(新类仅 129s,因为复用了 warm model),据此把 est_time 从 600 调到 790。issue 评论区只有作者发的 /rerun-test 指令与 GitHub Actions 机器人报告的通过结果,没有技术分歧或未解决问题。

实现拆解

  1. 参数化启动参数_base_args() 增加 mamba_strategy: str = "extra_buffer" 参数,--mamba-radix-cache-strategy 从硬编码字面量改为变量。这让新测试类只需换一个策略名即可复用整套启动参数(--attention-backend fa4--page-size 128--mamba-track-interval 128--enable-deterministic-inference 等),避免复制粘贴带来的配置漂移。
  2. 新增 lazy 测试类TestUnifiedHybridLazyBitExact(TestUnifiedHybridBitExact) 只重写 setUpClass,以 _base_args("extra_buffer_lazy") + ["--chunked-prefill-size", "16384"] 启动服务,其余全部继承基类三个测试方法(test_logprobs_matchtest_prefill_cache_hittest_decode_cache_hit)。--chunked-prefill-size 16384 的作用是保证 decode 过程中跨过 track 边界,让 lazy 槽位的分配与释放路径真正被走到。
  3. 文档与 CI 预算:文件头部 KL 对照表新增 lazy 行(SM100 上 revert #34184 时为 5.56e-06,修复后 0.0),并说明该值与同会话非 lazy 路径的 1.18e-05 应解读为「both fire」;register_cuda_ci(est_time=600 → 790) 反映新增用例的实际耗时。

配套情况:这是纯测试与 CI 配套变更,无配置、schema 或部署改动。测试注册在 1-gpu-large(SM90)的 base-b stage,PR 标签带 run-cirun-ci-extra

文件 模块 状态 重要度
test/registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_hybrid_bitexact.py 缓存测试 modified 6.42

关键符号

_base_args TestUnifiedHybridLazyBitExact.setUpClass

关键源码片段

test/registered/radix_cache/unified_radix_tree/test_unified_radix_cache_kl_hybrid_bitexact.py test-coverage

唯一的变更文件:新增 `TestUnifiedHybridLazyBitExact` 类为 `extra_buffer_lazy` 策略提供 bit-exact KL 守卫,同时把 `_base_args` 参数化并更新 CI 耗时估计,是本次 PR 的全部价值所在。

def _base_args(mamba_strategy: str = "extra_buffer") -> list[str]:
    # 参数化 radix cache 策略,让不同测试类复用同一套启动参数,
    # 只需要切换 --mamba-radix-cache-strategy 这一个配置键。
    return [
        "--trust-remote-code",
        "--attention-backend", "fa4",
        "--page-size", str(PAGE_SIZE),
        "--mamba-radix-cache-strategy", mamba_strategy,
        "--swa-full-tokens-ratio", "0.1",
        "--mamba-full-memory-ratio", "0.1",
        # 0.85 是从 4-GPU B200 测试延续下来的值,在 80 GB 显卡上会 OOM:
        # 静态池只留约 19 GB 给 prefill graph、fa4 workspace 和 chunked
        # prefill 激活,0.6 才是这个配置需要的值。
        "--mem-fraction-static", "0.6",
        "--mamba-track-interval", str(TRACK_INTERVAL),
        "--enable-deterministic-inference",
    ]
​
​
class TestUnifiedHybridLazyBitExact(TestUnifiedHybridBitExact):
    """Same exactness bar on the lazy extra-buffer strategy.    Lazy 是一段独立代码路径,而不是同一个代码换了个 flag:请求只持有
    1 个 ping-pong 槽位而不是 2 个,第二个槽位只在跨 track 边界的 forward
    上临时分配、用完即释放。因此 checkpoint 落在每次 forward 临时选择的
    槽位上,而不是请求整个生命周期拥有的槽位,选错就会恢复另一个请求的
    conv 窗口。    验收方式和它的基类一致(revert-then-red):revert #34184 后,本类的
    test_prefill_cache_hit 会读到 5.56e-06,远高于 1e-9 门槛,所以它守卫的
    是 lazy 路径上同一类状态复用 bug。
    """
​
    @classmethod
    def setUpClass(cls):
        cls.model = _MODEL_PATH
        cls.base_url = DEFAULT_URL_FOR_TEST
        other_args = _base_args("extra_buffer_lazy") + [
            "--chunked-prefill-size", "16384",
        ]
        # 加大 chunked prefill,让 decode 跨过 track 边界,
        # 从而触发 lazy 槽位的分配与释放路径。
        if _MODEL_REVISION:
            other_args += ["--revision", _MODEL_REVISION]
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
            other_args=other_args,
            env={**os.environ, "SGLANG_ENABLE_UNIFIED_RADIX_TREE": "1"},
        )

评论区精华

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

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

风险与影响

风险集中在测试有效性而非生产代码:1)CI 时长与预算——base-b stage 的 est_time 增加约 32%(600 → 790s),实测全文件 655s、新类只占 129s,预算余量约 20%,若同 stage 其他用例继续变慢仍有超时风险;2)架构盲区——lazy 行的 revert 实测只在 SM100(B200)上完成,SM90(CI 实际硬件)标记为 n/a,若两台架构上 lazy 槽位行为不同,该守卫在 CI 上可能「测不到」它声称覆盖的 bug,属于已记录但未验证的盲区;3)模型确定性依赖——守卫前提是 Inkling@test 在 --enable-deterministic-inference 下 prefill 与 decode 逐位一致,checkpoint 或 revision 更新可能破坏该前提并产生假阳性。文件层面风险点集中在 test_unified_radix_cache_kl_hybrid_bitexact.py 内新增类的启动参数组合。

对用户无直接影响:没有任何生产代码或部署配置变更。对系统的影响是每个提交在 1-gpu-large(SM90)的 base-b stage 新增一个约 129s 的 GPU 用例,CI 预算从 600s 上调至 790s。对团队而言,extra_buffer_lazy 这条容易在槽位复用上出错的策略首次获得逐位一致性护栏,防止 #34184 这类状态复用 bug 在 lazy 路径上复发,并补齐 issue #34899 路线图中的一块覆盖空白。

CI 时长预算上调 32% SM90 上 lazy 信号未验证 依赖 Inkling@test 确定性

关联 Issue

#34899 [Feature] Bit-exact correctness coverage for Unified Radix Cache

完整报告

参与讨论