Prhub

#34405 Fix flaky decode cache-hit check in Inkling test

原始 PR 作者 ispobock 合并时间 2026-08-11 21:47 文件变更 2 提交数 2 评论 4 代码增减 +18 / -3

执行摘要

修复 Inkling 测试中 decode cache-hit 检查的 flaky 问题

PR body 指出:test_input_output_logprobs_match_decode_cache_hit 间歇性红色,报 Too few decode cache hits: 16/32,而 avg_kl_div 保持 0.0。命中数并非噪声——decode checkpoint 仅在滑动窗口完整存留时可用,而窗口外的 SWA 槽位按页释放,因此最深的可复用位置是页边界;默认 mamba_track_interval=256 配合 --page-size 128 导致只有一半 prompt 能命中,期望值恰好 16/32,而测试需要 17 个,所以从落地起就一直差一个。

该 PR 值得精读,尤其适合负责测试维护或关注 mamba/SWA 缓存机制的工程师。它展示了一个由配置不对齐导致的 flaky 测试的典型排查过程,以及如何通过将对齐直觉转化为显式断言(0.99)来提升测试的信噪比。但本身不涉及核心推理逻辑,重要性中等。

讨论亮点

本 PR 没有 review 评论,仅有一条 issue 评论由作者触发 /rerun-test,第二次 rerun 通过(4-gpu-b200 上的测试从失败转为成功)。没有公开的设计讨论或争议。

实现拆解

  1. 根因定位:在 test/registered/models_e2e/test_inkling_small_nvfp4.py 中,服务启动参数使用 --page-size 128,但 --mamba-track-interval 未显式设置,沿用默认 256。decode checkpoint 的可复用性取决于页边界,间隔与页大小不对齐导致只有约一半序列长度满足条件。
  2. 修改测试配置:新增常量 KL_TRACK_INTERVAL = 128,并在 setUpClass 的服务启动参数中加入 --mamba-track-interval str(KL_TRACK_INTERVAL),使 checkpoint 间隔与页大小一致,让每个页边界都成为 checkpoint,从而所有 prompt 都能命中 decode 缓存。
  3. 泛化 helper:在 python/sglang/test/kl_test_utils.pytest_input_output_logprobs_match_decode_cache_hit_helper 中新增 min_cache_hit_ratio=0.5 参数,替换原先硬编码的 0.5,保持默认行为不变,同时允许调用方按需收紧断言。
  4. 收紧断言:在 test_input_output_logprobs_match_decode_cache_hit 中传入 min_cache_hit_ratio=0.99,将命中率从“过半数”提升到“几乎全部”,使单次 miss 即视为状态复用回归,测试覆盖范围从原来仅一半请求翻倍。
  5. 配套说明:调整 _run 方法支持透传 **kwargs,并更新注释解释对齐逻辑,避免未来维护者误改配置。
文件 模块 状态 重要度
test/registered/models_e2e/test_inkling_small_nvfp4.py 端到端测试 modified 5.41
python/sglang/test/kl_test_utils.py 测试工具 modified 4.11

关键符号

_run test_input_output_logprobs_match_decode_cache_hit_helper

关键源码片段

test/registered/models_e2e/test_inkling_small_nvfp4.py test-coverage

修复 flaky 的主战场:新增 `KL_TRACK_INTERVAL` 常量、在服务启动参数中显式设置 `--mamba-track-interval 128`,并将 decode cache-hit 测试的命中率阈值提升到 0.99,从根本上消除间歇性失败。

# 与下方 --page-size 128 对齐:SWA 窗口外的槽位按页释放,
# 只有落在页边界上的 checkpoint 才能保留完整滑动窗口数据,
# 默认 256 间隔会使一半的序列长度错过边界、丢掉 decode 前缀。
KL_TRACK_INTERVAL = 128class TestInklingSmallNvfp4(CustomTestCase):
    @classmethod
    def setUpClass(cls):
        cls.model = _MODEL_PATH
        cls.base_url = DEFAULT_URL_FOR_TEST
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
            other_args=[
                "--tp", "4",
                "--trust-remote-code",
                "--quantization", "modelopt_fp4",
                "--attention-backend", "fa4",
                "--page-size", "128",
                "--fp4-gemm-backend", "flashinfer_trtllm",
                "--moe-runner-backend", "flashinfer_trtllm_routed",
                "--mamba-radix-cache-strategy", "extra_buffer",
                "--swa-full-tokens-ratio", "0.1",
                "--mamba-full-memory-ratio", "0.1",
                "--mem-fraction-static", "0.85",
                # 关键修复:checkpoint 间隔与页大小对齐,确保每个页边界都有 checkpoint
                "--mamba-track-interval", str(KL_TRACK_INTERVAL),
                "--enable-deterministic-inference",
            ],
            env={**os.environ, "SGLANG_ENABLE_UNIFIED_RADIX_TREE": "1"},
        )
​
    def _run(self, helper, **kwargs):
        helper(
            self.base_url,
            {self.model: {"kl_div": KL_DIV_THRESHOLD}},
            self.model,
            max_samples=32,
            max_new_tokens=KL_MAX_NEW_TOKENS,
            trust_remote_code=True,
            **kwargs,
        )
​
    def test_input_output_logprobs_match_decode_cache_hit(self):
        # 0.99 意味着要求几乎全部 prompt 命中:间隔与页对齐后复用成为无条件行为,
        # 因此任何一次 miss 都代表状态复用回退,而不再是几何巧合。
        self._run(assert_logprobs_match_decode_cache_hit, min_cache_hit_ratio=0.99)
python/sglang/test/kl_test_utils.py test-coverage

为 decode cache-hit helper 引入 `min_cache_hit_ratio` 参数,替代硬编码的 0.5,使调用方可以按需收紧断言阈值,是支撑本 PR 收紧测试的关键配套改动。

def test_input_output_logprobs_match_decode_cache_hit_helper(
    base_url,
    ACC_THRESHOLDS,
    model_name,
    max_samples=None,
    max_new_tokens=8192,
    trust_remote_code=False,
    min_cache_hit_ratio=0.5,
):
    # ... 第一轮生成与第二轮生成逻辑 ...
    for i, result in enumerate(results):
        if result["meta_info"]["cached_tokens"] <= len(first_turn_input_ids[i]) + 1:
            print(f"Decode cache miss for prompt {i}, skipping")
            continue
        new_input_ids.append(second_turn_input_ids[i] + result["output_ids"])
        output_logprobs.append(_extract_output_logprobs(result))
​
    if not os.environ.get("SGLANG_TEST_SKIP_CACHE_HIT_ASSERT"):
        # 页对齐的 SWA 保留策略决定了哪些 prompt 能命中,因此默认阈值只用于排除“全 miss”的空跑;
        # 调用方若通过配置让每个 prompt 都命中,可提高该阈值来获得更严格的回归检测。
        assert len(new_input_ids) > min_cache_hit_ratio * len(
            second_turn_input_ids
        ), f"Too few decode cache hits: {len(new_input_ids)}/{len(second_turn_input_ids)}"

评论区精华

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

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

风险与影响

风险主要在于测试行为收紧:min_cache_hit_ratio=0.99 会把任何一次非预期的 cache miss 变成失败,若未来 mamba 或 SWA 的内存管理策略发生变化(如页大小调整、释放粒度变化),可能导致误报。另外,--mamba-track-interval 128 是测试专用配置,不会影响生产路径;helper 的参数默认值保持向后兼容,其他调用方不受影响。整体风险较低,但需要注意该测试对几何对齐的敏感性。

影响范围限于 CI 测试:修复了 TestInklingSmallNvfp4 在 B200 4-GPU 上的间歇性红色,提升了测试稳定性;同时通过提高命中率要求,使该测试能更可靠地捕获 decode 阶段的状态复用回归。kl_test_utils.py 的改动对所有使用该 helper 的测试透明,默认行为不变,并提供了更灵活的阈值控制。对用户和运行时无影响。

测试阈值收紧,可能暴露新的缓存回退问题 依赖 mamba-track-interval 与 page-size 的隐式耦合

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论