Prhub

#27502 Add mixed-prefix gsm8k eval and its CPU unit test

原始 PR 作者 fzyzcjy 合并时间 2026-06-09 20:17 文件变更 4 提交数 5 评论 1 代码增减 +265 / -5

执行摘要

新增混合前缀 GSM8K 评估及 CPU 单元测试

PR body 说明:mixed_prefix_gsm8k builds each question's few-shot prefix from a primary block plus a per-question random sample of a secondary pool, so an evaluation run exercises distinct, partially-shared cached prefixes (instead of one identical few-shot prefix shared by every request). This is the eval used to detect chunked-prefill / prefix-cache corruption: a clean run scores normally, a corrupting bug collapses the score.

值得精读。该 PR 展示了如何通过提取虚方法(_setup_prefix_pool / _build_prefix)在测试框架中实现模板方法模式,使得扩展不同前缀策略变得简单。单元测试也写得清晰全面,可作为测试驱动开发的范例。

讨论亮点

仅有一条来自 gemini-code-assist[bot] 的 review 评论,针对 simple_eval_gsm8k.py 中使用 assert 进行参数验证的风险。机器人建议改用 ValueError 以避免 Python -O 优化绕过。作者在后续 commit(722fd4b)中改为使用切片静默截断(slicing)而非断言,从而修复了该问题。讨论状态:已解决。

实现拆解

  1. 重构 base class GSM8KEvalpython/sglang/test/simple_eval_gsm8k.py
    - 将原本在 __init__ 中直接计算 _few_shot_prompt 的逻辑拆分为两个虚方法:_setup_prefix_pool_build_prefix
    - _setup_prefix_pool 负责从原始数据行中划分训练/测试集并构建基础前缀;_build_prefix 返回特定索引的前缀字符串。
    - 原先的 self._few_shot_prompt 改为通过 self._build_prefix(idx) 调用,默认行为不变。
    - 同时将 num_examples 越界处理从隐式切片改为显式静默截断(切片行为保持不变,但避免硬 assert 被优化掉)。

  2. 新增子类 MixedPrefixGSM8KEvalpython/sglang/test/simple_eval_mixed_prefix_gsm8k.py
    - 继承自 GSM8KEval,重写 _setup_prefix_pool 将数据集分割为 _primary_shots(固定前 N 条)和 _secondary_pool(接下来 secondary_pool_size 条)。
    - 重写 _build_prefix:使用随机种子(种子 = 用户指定 seed + 问题索引)决定主块使用多少条(0~num_shots),并从辅助池随机采样 0~secondary_pool_size 条,拼接后返回。

  3. 集成到评测入口python/sglang/test/run_eval.py
    - 在 run_eval 的 dispatch 中添加 elif args.eval_name == "mixed_prefix_gsm8k" 分支,实例化 MixedPrefixGSM8KEval
    - 添加两个 CLI 参数:--mixed-prefix-gsm8k-secondary-pool-size(默认 15)和 --mixed-prefix-gsm8k-seed(默认 42)。

  4. 添加 CPU 单元测试test/registered/unit/bench/test_mixed_prefix_gsm8k.py
    - 使用 register_cpu_ci 注册到 CI 基础测试套件,不依赖 GPU。
    - 生成合成数据集,验证 _build_prefix 的行为:

    • 主段总是主 shots 的前缀;
    • 剩余问题全部来自辅助池且无重复;
    • 剩余大小在辅助池范围内;
    • 不同查询的主深度和辅助大小具有多样性;
    • 任意两个查询共享至少 0 个主 shots(即主段长度可不同)。
文件 模块 状态 重要度
test/registered/unit/bench/test_mixed_prefix_gsm8k.py 单元测试 added 7.94
python/sglang/test/simple_eval_mixed_prefix_gsm8k.py 评估逻辑 added 7.22
python/sglang/test/simple_eval_gsm8k.py 基类重构 modified 5.9
python/sglang/test/run_eval.py 命令行入口 modified 5.11

关键符号

MixedPrefixGSM8KEval.__init__ MixedPrefixGSM8KEval._setup_prefix_pool MixedPrefixGSM8KEval._build_prefix GSM8KEval._setup_prefix_pool GSM8KEval._build_prefix TestMixedPrefixGSM8KEval._decompose TestMixedPrefixGSM8KEval.test_primary_segment_is_strict_prefix_of_primary_shots

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

使用 assert 验证参数的风险 正确性

gemini-code-assist[bot] 指出在 simple_eval_gsm8k.py 中使用 assert 检查 num_examples 范围,当 Python 以 -O 运行时会被跳过。建议改用 ValueError。

结论:作者在 commit 722fd4b 中改为使用列表切片进行静默截断,避免 assert 被绕过,同时保持与上游调用者(如 test_basic_sanity_eagle3)的兼容。 · 已解决

风险与影响

  1. 基类重构风险GSM8KEval 的前缀构建从内联改为方法调用,虽保持默认行为不变,但任何直接访问 _few_shot_prompt 的外部代码(本项目或其他依赖)会失效。经过代码搜索,_few_shot_prompt 仅在类内部使用,影响可控。
  2. 数据集大小假设MixedPrefixGSM8KEval 要求数据集至少包含 num_shots + secondary_pool_size + 1 条数据,否则抛出 ValueError。用户若使用小数据集需调整参数。
  3. 随机种子确定性:使用固定种子 seed + idx,理论上每次运行结果相同,但若随机库实现变化(如 Python 版本升级)可能影响一致性。测试中使用合成数据,不依赖外部数据集再现性。
  4. 测试覆盖:单元测试覆盖了核心构造逻辑,但不涉及实际模型推理,因此对缓存损坏的检测能力需要通过端到端评估验证。

用户影响:增加一个新的 eval_name mixed_prefix_gsm8k,可通过 CLI 直接使用,为用户提供检测 prefix-cache 完整性的现成工具。
系统影响:不修改推理路径或调度核心,不影响生产服务。
团队影响:为后续 chunked-prefill 相关测试提供可复用的评估框架,降低回归测试编码成本。
影响程度:中等,主要影响测试和调试流程。

基类重构需验证向后兼容 数据集大小假设可能限制使用场景

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论