Prhub

#34186 [CI] Key scheduled CUDA suites by runner_config instead of hand-written jobs

原始 PR 作者 hnyls2002 合并时间 2026-08-10 07:44 文件变更 94 提交数 7 评论 2 代码增减 +531 / -869

执行摘要

CUDA 夜测迁移至 runner_config 注册式调度,统一走 `_pr-test-stage.yml`

PR body 明确指出:"Replaces the hand-written nightly/weekly job list with registry-keyed stages, so scheduled suites run through the same _pr-test-stage.yml path as per-commit CI." 此前 nightly 工作流中每个 job 手写 runs-on、安装步骤、分区矩阵、超时参数,与 per-commit CI 的 runner_configs 体系割裂,测试若要上线一台新机型必须同时修改 workflow 与 suite 注册两处。改为 registry-keyed 后,测试通过 register_cuda_ci 声明 runner_config 即可自动落入对应机器的夜间套件,workflow 文件不再需要按 job 维护。

值得 CI 基础设施负责人精读:该 PR 展示了如何通过 registry-keyed 注册把分散的手写调度收敛为单一执行路径,scheduled 输入作为 per-commit 与夜间行为差异的开关设计干净,token 隔离与超时推导的注释含实证依据。对一般功能开发者,只需了解测试注册格式变更即可,不影响业务代码。关注点建议放在迁移完整性和超时预算变化上,可在合并后观察一个周期的 nightly 结果确认无静默丢失。

讨论亮点

该 PR 没有实质 review 评论(0 条 review comments;2 条 issue comments 分别为 Mintlify 预览机器人通知和作者触发的 /tag-and-rerun-ci)。有价值的讨论内嵌在代码注释与提交演进中:

1) ci_utils.py 注释解释了超时公式的动机——"Slow-run variance is largely additive (cold HF cache, slow server launch), so the multiplier alone under-provisions at both ends",并给出 test_encoder_dp 曾以 1.5x 预算(765s)实际耗时超过 1185s 的实证,因此采用比例加绝对松弛的 max(est*1.5, est+1800) 组合;
2) precision_baseline_store.py_store_token docstring 说明 token 隔离理由——"Deliberately not HF_TOKEN: that name already carries the runner's gated-model read token, so writing the store token there would shadow it and turn every gated model on the job into a 401";
3) 提交 revert gpu idle wait; fix stale suite name and --nightly helprestore IS_H200 and HF_HUB timeouts; widen derived timeout; fix metrics needs 显示作者在自测中对策略做了多轮收敛(如放宽推导超时、恢复 H200 启动上限与 HF_HUB 超时)。

实现拆解

  1. 注册契约迁移:批量改写测试文件中的 register_cuda_ci 调用,从 suite="nightly-8-gpu-b200", nightly=True 形式改为 stage="nightly", runner_config="8-gpu-b200"。涉及 test/registered/ 下数十个文件(如 test_dsa_glm52_cache_layer_split.pytest_custom_all_reduce.py8-gpu-models/test_glm52_fp8.py 等),nightly 标识不再由 flag 表达,而是由 stage 名称承载。
  2. suite 命名与校验逻辑调整test/run_suite.py):NIGHTLY_SUITES 中 CUDA 分支从 19 个手写 suite 名缩减为 7 个 nightly-test-{runner_config} 形状的条目;filter_tests 的合法性校验从只查 NIGHTLY_SUITESPER_COMMIT_SUITES 之一,改为查三个字典的并集(_valid_suites_by_backend()),因为 CUDA 夜测 suite 仅凭名称选择、不再依赖 --nightly
  3. workflow 重构.github/workflows/nightly-test-nvidia.ymlweekly-test-nvidia.yml_pr-test-stage.yml):夜间工作流删除了约 550 行手写 job,改为按 runner_config 动态调用 _pr-test-stage.yml_pr-test-stage.yml 新增 scheduled 输入,用于切换夜间行为:--timeout-from-est-time 逐文件超时、完整 jit kernel 测试网格(SGLANG_JIT_KERNEL_RUN_FULL_TESTS=1)、IS_H200 服务器启动上限放宽、metrics 收集上传、以及默认串行 shard(除非 full_parallel=true)。precision baseline 所需环境变量也在此步骤导出。
  4. 超时推导python/sglang/test/ci/ci_utils.py):新增 derive_timeout_per_file(est_time) = max(est * 1.5, est + 1800)run_unittest_filestimeout_per_file 参数改为 Optional[float],为 None 时按每个文件的 est_time 推导预算;run_suite.py 新增 --timeout-from-est-time 开关。
  5. 配套清理scripts/ci/utils/slash_command_handler.py 删除 _LEGACY_SUITE_TO_RUNNER_CONFIG 映射,旧式单字符串 suite= 注册不再可通过 /rerun-test 调度,错误消息引导重新注册;scripts/ci/stage_models_overrides.json 相应删除 12 个 legacy suite 的 label 映射;docs/docs/references/nightly_precision_regression.mdx 同步更新 token 变量名与 workflow 说明。
文件 模块 状态 重要度
.github/workflows/nightly-test-nvidia.yml 夜测编排 modified 6.43
test/run_suite.py 套件调度 modified 5.9
python/sglang/test/ci/ci_utils.py CI 工具 modified 5.9
python/sglang/test/precision_baseline_store.py 基线存储 modified 5.26
scripts/ci/utils/slash_command_handler.py CI 命令 modified 5.02
.github/workflows/_pr-test-stage.yml 测试管线 modified 5.39
.github/workflows/weekly-test-nvidia.yml 周测编排 modified 4.61

关键符号

derive_timeout_per_file filter_tests run_unittest_files _store_token detect_suite _extract_legacy_suites

关键源码片段

test/run_suite.py test-coverage

suite 注册契约的核心载体:`NIGHTLY_SUITES` 改为 `nightly-test-{runner_config}` 形状,`filter_tests` 校验逻辑从二分改为三字典并集,并新增 `--timeout-from-est-time` 开关。

# test/run_suite.py
# CUDA 夜间套件不再按功能手写名字,而是按 runner_config 组织:
# 测试通过 register_cuda_ci(stage="nightly", runner_config=...) 声明归属,
# stage 名称承载节奏,不再需要 nightly=True flag。
def filter_tests(
    ci_tests: List[CIRegistry], hw: HWBackend, suite: str, nightly: bool = False
) -> List[CIRegistry]:
    ci_tests = [
        t
        for t in ci_tests
        if t.backend == hw and t.effective_suite == suite and t.nightly == nightly
    ]
​
    # 合法性校验需要三个字典的并集,而不是只看 per-commit 或 nightly 半边:
    # CUDA nightly suite 直接按名称选取,不依赖 --nightly flag。
    if suite not in _valid_suites_by_backend().get(hw, set()):
        print(f"Warning: Unknown suite {suite} for backend {hw.name}")
​
    enabled_tests = [t for t in ci_tests if t.disabled is None]
    skipped_tests = [t for t in ci_tests if t.disabled is not None]
    return enabled_tests, skipped_tests
python/sglang/test/ci/ci_utils.py test-coverage

新增 `derive_timeout_per_file` 与 `run_unittest_files` 的 per-file 超时推导能力,是夜间 suite 混合快慢测试时避免一刀切超时预算的关键支撑。

# python/sglang/test/ci/ci_utils.py
# 慢速方差在很大程度上是加性的(冷 HF cache、慢的 server 启动),
# 单纯乘系数会在两端都欠配:test_encoder_dp 实际跑 200-426s,
# 却曾以 1.5x 预算(765s)超时到 1185s;test_lora_deepseek_v3_base_logprob_diff
# (est 1800)恰好落在 1.5 * est 上。因此每个文件在比例预算之上
# 再叠加同一份绝对松弛。
DERIVED_TIMEOUT_SLACK = 1800.0
DERIVED_TIMEOUT_FACTOR = 1.5
​
​
def derive_timeout_per_file(est_time: float) -> float:
    """根据单个文件的 est_time 推导超时上限,取比例与绝对两者的较大值。"""
    est = float(est_time)
    return max(est * DERIVED_TIMEOUT_FACTOR, est + DERIVED_TIMEOUT_SLACK)

评论区精华

超时推导公式的设计动机 设计

代码注释记录了推导公式的实证背景:慢速方差以加性为主(冷 HF cache、慢服务器启动),纯乘系数在两端都欠配;`test_encoder_dp` 跑 200-426s 却曾以 1.5x 预算(765s)超时到 1185s。

结论:采用 `max(est * 1.5, est + 1800)` 组合,并让 `run_unittest_files` 在 `timeout_per_file=None` 时按每个文件自己的 `est_time` 推导预算;后续提交 `widen derived timeout` 进一步放宽。 · 已解决

precision 基线 token 与 gated-model 读 token 的隔离 安全

`_store_token` 注释说明:若把基线写 token 写入 `HF_TOKEN`,会遮蔽 runner 读取 gated model 的 token,导致任务里所有 gated model 变成 401。提交历史显示这是独立 commit(read precision store token from its own env var)。

结论:新增独立环境变量 `SGLANG_PRECISION_HF_TOKEN`,workflow 导出步骤、`HfStoreConfig.from_env` 报错信息与文档全部同步切换。 · 已解决

legacy suite 的 /rerun-test 可调度性回退 设计

删除 `_LEGACY_SUITE_TO_RUNNER_CONFIG` 后,旧式单字符串 suite 注册不再可被 slash 命令单独重跑;代码故意将不可调度的原因写入错误消息,引导重新注册。

结论:接受该回退:所有可调度的 CUDA suite(per-commit 与 scheduled)统一走 runner_configs.yml 单一来源,遗留注册项降级为非可调度并提示迁移。 · 已解决

nightly 与 per-commit 行为差异的开关设计 设计

复用 `_pr-test-stage.yml` 后,夜间特有的行为(完整 jit 网格、IS_H200 上限、metrics 收集、串行 shard)通过新增 `scheduled` 输入切换;`full_parallel` 输入允许显式开启并行分片。

结论:以 `scheduled: true` 作为统一开关,配套 `job_timeout_minutes`、`run_timeout_minutes` 区分 job 级与 run 级超时,避免在 per-commit 路径上引入夜间副作用。 · 已解决

风险与影响

1) 测试静默丢失风险:94 个文件的批量迁移中,若某个 register_cuda_cistage/runner_configrun_suite.pyNIGHTLY_SUITES 条目不匹配,测试将得不到合法 suite 校验保护(CUDA 之外的 backend 仍被 _SUITE_CHECKED_BACKENDS 排除在校验外),可能静默不跑;
2) --nightly 语义变化:CUDA 夜测 suite 改为按名称选取,--nightly flag 仅对 AMD/CPU/NPU 生效,旧脚本若沿用 --nightly + 新 suite 名组合可能选不到测试;
3) /rerun-test 能力回退:删除 _LEGACY_SUITE_TO_RUNNER_CONFIG 后,尚未迁移的 legacy suite(如 GB300 系列、部分 AMD)无法再通过 slash 命令单独重跑,排障效率下降;
4) 超时策略变化derive_timeout_per_file 对短测试(est < 3600s)会给到明显更大的预算(est+1800),可能延长失败反馈时间,而对 est=0 的测试(如 test_custom_all_reduce 夜测注册 est_time=110)则相对安全;长测试(如 test_glm52_fp8est_time=2880)所得预算低于旧版 --timeout-per-file 18000,存在超时风险;
5) 令牌环境变量切换:precision 基线从 HF_TOKEN_PRECISION_STORE/HF_TOKEN 切换到 SGLANG_PRECISION_HF_TOKEN,若 _pr-test-stage.yml 的导出步骤与 _store_token 读取不一致(如 secrets 未注入),基线读写会立即失败。

影响范围集中在 CI 基础设施:所有 CUDA nightly/weekly 测试的调度路径(约 40+ 个测试文件、7 个 runner_config 机器池)从手写 workflow 迁移到统一 stage 管线,新增测试只需一次 register_cuda_ci 注册即可自动进入夜间队列,新增机器池只需在 runner_configs.yml 声明。对普通开发者,影响主要体现为测试注册 API 的格式变化(suite+nightlystage+runner_config);对 CI 维护者,消除了 nightly 与 per-commit 两套实现长期漂移的维护负担,并统一了分区计算、rust-ext 复用、metrics 上报等行为。团队协作上,后续调整夜间测试的机器选择、超时、并行度都只需改注册项或 _pr-test-stage.yml,不再需要编辑大型 workflow 文件。

涉及 CI 核心调度路径 94 文件批量迁移存在静默丢失风险 legacy suite 失去 /rerun-test 能力 超时预算策略变化 token 环境变量切换

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论