Prhub

#22913 test(4-gpu-b200): split test_qwen35_models.py + bump partitions 5→6

sgl-project/sglang · 作者 alisonshao · 合并时间 2026-04-17 09:51

分析状态 已生成
文件变更 4提交数 3 · 评论 3
代码增减 +184 / -247
test run-ci refactor

执行摘要

拆分 Qwen3.5 FP4 模型测试文件并增加 CI 分区,避免超时失败。

PR body 指出,原文件合并运行在 B200 主机上经常超过 30 分钟步超时(示例失败链接:https://github.com/sgl-project/sglang/actions/runs/24454292569/job/71542805109),因此拆分文件以便 CI 自动分区器分散负载。此外,根据 reviewer 反馈移除了 v1 MTP 测试,因为 v2 MTP 将成为默认。

该 PR 是基础设施优化,值得技术管理者关注 CI 配置变更以调整测试策略;工程师可参考测试分割策略,在类似场景下优化测试套件执行时间。

讨论亮点

review 中仅显示批准,无详细评论;但 PR body 提及“per reviewer feedback — v2 MTP will be the default”,表明有隐含的设计决策:移除 v1 MTP 测试以避免冗余,聚焦于 v2 版本。

实现拆解

  1. 拆分原测试文件:删除 test/registered/4-gpu-models/test_qwen35_models.py,创建两个新文件:
    • test_qwen35_fp4_triton.py:包含 TestQwen35FP4 类,专注于 Triton 后端准确性测试,设置 est_time=720s
    • test_qwen35_fp4_mtp_v2.py:包含 TestQwen35FP4MTPV2 类,用于测试 v2 MTP 推测解码,设置 est_time=540s,并通过 envs.SGLANG_ENABLE_SPEC_V2.set(True) 启用 v2 特性。
  2. 移除冗余测试:根据 reviewer 反馈,删除 TestQwen35FP4MTP(v1 MTP 测试),因为 v2 MTP 将默认启用,避免测试冗余。
  3. 更新 CI 配置:修改 .github/workflows/pr-test.yml,将 stage-c-test-4-gpu-b200 的分区数量从 5 增加到 6,并更新 auto-partition-size 参数,确保测试负载均匀分布,防止超时。
  4. 测试配套验证:保持准确性阈值(如 gsm8k ≥ 0.95)和性能指标(如 avg_spec_accept_length > 3.3)不变,通过 CI 运行验证所有分区在超时内完成。
文件 模块 状态 重要度
test/registered/4-gpu-models/test_qwen35_models.py 测试套件 removed 7.46
test/registered/4-gpu-models/test_qwen35_fp4_triton.py 测试套件 added 6.43
test/registered/4-gpu-models/test_qwen35_fp4_mtp_v2.py 测试套件 added 7.38
.github/workflows/pr-test.yml CI 配置 modified 3.13
test/registered/4-gpu-models/test_qwen35_fp4_triton.py test-coverage

新增文件,包含 TestQwen35FP4 类,专注于 Qwen3.5 FP4 模型的 Triton 后端准确性测试,是拆分后的核心测试文件之一。

import unittest
from sglang.test.accuracy_test_runner import AccuracyTestParams
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.run_combined_tests import run_combined_tests
from sglang.test.test_utils import CustomTestCase, ModelLaunchSettings# 注册 CI 测试,设置估计时间为 720 秒,以匹配拆分后的负载
register_cuda_ci(est_time=720, suite="stage-c-test-4-gpu-b200")QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}} # 定义准确性阈值class TestQwen35FP4(CustomTestCase):
    def test_gsm8k(self):
        # 基础服务器启动参数,针对 4-GPU B200 配置,包括 TP 大小、Mamba 调度等
        base_args = [
            "--tp-size", "4",
            "--chunked-prefill-size", "2048",
            "--mamba-scheduler-strategy", "extra_buffer",
            "--mamba-track-interval", "128",
            "--mamba-ssm-dtype", "bfloat16",
            "--max-running-requests", "128",
            "--reasoning-parser", "qwen3",
            "--attention-backend", "trtllm_mha",
            "--quantization", "modelopt_fp4",
            "--model-loader-extra-config", '{"enable_multithread_load": true,"num_threads": 64}',
        ]
​
        # 定义测试变体,目前只启用 Triton 后端,FlashInfer 后端待修复
        variants = [
            ModelLaunchSettings(
                QWEN35_FP4_MODEL,
                extra_args=base_args,
                variant="Triton",
            ),
            # TODO: 修复 FlashInfer 后端后重新启用
            # ModelLaunchSettings(
            #     QWEN35_FP4_MODEL,
            #     extra_args=base_args + ["--linear-attn-decode-backend", "flashinfer"],
            #     variant="FlashInfer",
            # ),
        ]
​
        # 运行组合测试,验证 gsm8k 数据集的准确性,确保不低于阈值
        run_combined_tests(
            models=variants,
            test_name="Qwen3.5-397B-A17B-NVFP4",
            accuracy_params=AccuracyTestParams(
                dataset="gsm8k",
                baseline_accuracy=ACC_THRESHOLDS[QWEN35_FP4_MODEL]["gsm8k"],
                num_examples=200,
                num_threads=128,
                max_tokens=16000,
                thinking_mode="qwen3",
                temperature=0.6,
                top_p=0.95,
                top_k=20,
            ),
        )
test/registered/4-gpu-models/test_qwen35_fp4_mtp_v2.py test-coverage

新增文件,包含 TestQwen35FP4MTPV2 类,用于测试 Qwen3.5 FP4 模型的 v2 MTP 推测解码功能,是拆分后的另一核心测试文件。

import unittest
from types import SimpleNamespace
import requests
from sglang.srt.environ import envs
from sglang.srt.utils import kill_process_tree
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.kits.reasoning_kit import ReasoningTokenUsageMixin
from sglang.test.run_eval import run_eval
from sglang.test.test_utils import (
    DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
    DEFAULT_URL_FOR_TEST,
    CustomTestCase,
    popen_launch_server,
)# 注册 CI 测试,设置估计时间为 540 秒,以适配拆分后的分区负载
register_cuda_ci(est_time=540, suite="stage-c-test-4-gpu-b200")QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}} # 准确性阈值定义class TestQwen35FP4MTPV2(ReasoningTokenUsageMixin, CustomTestCase):
    reasoning_parser_name = "qwen3" # 设置推理解析器
​
    @classmethod
    def setUpClass(cls):
        cls.model = QWEN35_FP4_MODEL
        cls.base_url = DEFAULT_URL_FOR_TEST
        cls.init_reasoning_token_verifier() # 初始化推理 token 验证器
        envs.SGLANG_ENABLE_SPEC_V2.set(True) # 启用 v2 推测解码特性
        # 启动服务器,使用 4-GPU 配置和推测解码参数
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
            other_args=[
                "--tp-size", "4",
                "--chunked-prefill-size", "2048",
                "--mamba-scheduler-strategy", "extra_buffer",
                "--mamba-track-interval", "128",
                "--mamba-ssm-dtype", "bfloat16",
                "--max-running-requests", "128",
                "--reasoning-parser", "qwen3",
                "--attention-backend", "trtllm_mha",
                "--quantization", "modelopt_fp4",
                "--speculative-algorithm", "NEXTN", # v2 MTP 相关参数
                "--speculative-num-steps", "3",
                "--speculative-eagle-topk", "1",
                "--speculative-num-draft-tokens", "4",
                "--mem-fraction-static", "0.8",
                "--model-loader-extra-config", '{"enable_multithread_load": true,"num_threads": 64}',
            ],
        )
​
    @classmethod
    def tearDownClass(cls):
        envs.SGLANG_ENABLE_SPEC_V2.set(False) # 测试结束后禁用 v2 特性
        kill_process_tree(cls.process.pid) # 清理服务器进程
​
    def test_gsm8k(self):
        # 设置评估参数,运行 gsm8k 数据集测试
        args = SimpleNamespace(
            model=self.model,
            eval_name="gsm8k",
            num_shots=5,
            num_examples=200,
            max_tokens=16000,
            num_threads=128,
            repeat=1,
            temperature=0.6,
            top_p=0.95,
            top_k=20,
            base_url=self.base_url,
            host="http://127.0.0.1",
            port=int(self.base_url.split(":")[-1]),
        )
        metrics = run_eval(args) # 执行评估
        print(f"{metrics=}")
        self.assertGreaterEqual(metrics["score"], ACC_THRESHOLDS[self.model]["gsm8k"]) # 验证准确性
​
        # 获取服务器信息,检查推测解码性能指标
        server_info = requests.get(self.base_url + "/server_info")
        avg_spec_accept_length = server_info.json()["internal_states"][0]["avg_spec_accept_length"]
        print(f"{avg_spec_accept_length=}")
        self.assertGreater(avg_spec_accept_length, 3.3) # 确保推测解码性能达标

关键符号

TestQwen35FP4.test_gsm8k TestQwen35FP4MTPV2.setUpClass TestQwen35FP4MTPV2.tearDownClass TestQwen35FP4MTPV2.test_gsm8k

评论区精华

移除 v1 MTP 测试的决策 设计

根据 reviewer 反馈,移除了 TestQwen35FP4MTP 类(v1 MTP 测试),因为 v2 MTP 将默认启用,避免测试冗余。

结论:决定删除 v1 测试以简化测试套件,聚焦于 v2 版本。 · 已解决

风险与影响

风险较低:文件分割可能略微增加维护复杂性,但测试逻辑未变,回归风险小;CI 分区调整需验证不会引入新的失败或资源冲突,例如分区数量增加可能导致资源分配不均,但 PR body 已确认所有分区在超时内完成。

对用户无直接影响;系统测试执行更可靠,减少超时失败,提升 CI 稳定性;团队测试代码更模块化,便于未来扩展和维护,但需注意文件分割后的依赖管理。

CI 配置变更风险 测试维护负担

关联 Issue

未识别关联 Issue

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

完整报告

执行摘要

  • 一句话:拆分 Qwen3.5 FP4 模型测试文件并增加 CI 分区,避免超时失败。
  • 推荐动作:该 PR 是基础设施优化,值得技术管理者关注 CI 配置变更以调整测试策略;工程师可参考测试分割策略,在类似场景下优化测试套件执行时间。

功能与动机

PR body 指出,原文件合并运行在 B200 主机上经常超过 30 分钟步超时(示例失败链接:https://github.com/sgl-project/sglang/actions/runs/24454292569/job/71542805109),因此拆分文件以便 CI 自动分区器分散负载。此外,根据 reviewer 反馈移除了 v1 MTP 测试,因为 v2 MTP 将成为默认。

实现拆解

  1. 拆分原测试文件:删除 test/registered/4-gpu-models/test_qwen35_models.py,创建两个新文件:
    • test_qwen35_fp4_triton.py:包含 TestQwen35FP4 类,专注于 Triton 后端准确性测试,设置 est_time=720s
    • test_qwen35_fp4_mtp_v2.py:包含 TestQwen35FP4MTPV2 类,用于测试 v2 MTP 推测解码,设置 est_time=540s,并通过 envs.SGLANG_ENABLE_SPEC_V2.set(True) 启用 v2 特性。
  2. 移除冗余测试:根据 reviewer 反馈,删除 TestQwen35FP4MTP(v1 MTP 测试),因为 v2 MTP 将默认启用,避免测试冗余。
  3. 更新 CI 配置:修改 .github/workflows/pr-test.yml,将 stage-c-test-4-gpu-b200 的分区数量从 5 增加到 6,并更新 auto-partition-size 参数,确保测试负载均匀分布,防止超时。
  4. 测试配套验证:保持准确性阈值(如 gsm8k ≥ 0.95)和性能指标(如 avg_spec_accept_length > 3.3)不变,通过 CI 运行验证所有分区在超时内完成。

关键文件:

  • test/registered/4-gpu-models/test_qwen35_models.py(模块 测试套件;类别 test;类型 deletion;符号 TestQwen35FP4, test_gsm8k, TestQwen35FP4MTP, setUpClass): 原测试文件,包含 TestQwen35FP4、TestQwen35FP4MTP 和 TestQwen35FP4MTPV2 三个类,因拆分而被删除。
  • test/registered/4-gpu-models/test_qwen35_fp4_triton.py(模块 测试套件;类别 test;类型 test-coverage;符号 TestQwen35FP4, test_gsm8k): 新增文件,包含 TestQwen35FP4 类,专注于 Qwen3.5 FP4 模型的 Triton 后端准确性测试,是拆分后的核心测试文件之一。
  • test/registered/4-gpu-models/test_qwen35_fp4_mtp_v2.py(模块 测试套件;类别 test;类型 test-coverage;符号 TestQwen35FP4MTPV2, setUpClass, tearDownClass, test_gsm8k): 新增文件,包含 TestQwen35FP4MTPV2 类,用于测试 Qwen3.5 FP4 模型的 v2 MTP 推测解码功能,是拆分后的另一核心测试文件。
  • .github/workflows/pr-test.yml(模块 CI配置;类别 infra;类型 infrastructure): 修改 CI 工作流配置,将 stage-c-test-4-gpu-b200 的分区数量从 5 增加到 6,以适配测试文件拆分后的负载分布。

关键符号:TestQwen35FP4.test_gsm8k, TestQwen35FP4MTPV2.setUpClass, TestQwen35FP4MTPV2.tearDownClass, TestQwen35FP4MTPV2.test_gsm8k

关键源码片段

test/registered/4-gpu-models/test_qwen35_fp4_triton.py

新增文件,包含 TestQwen35FP4 类,专注于 Qwen3.5 FP4 模型的 Triton 后端准确性测试,是拆分后的核心测试文件之一。

import unittest
from sglang.test.accuracy_test_runner import AccuracyTestParams
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.run_combined_tests import run_combined_tests
from sglang.test.test_utils import CustomTestCase, ModelLaunchSettings# 注册 CI 测试,设置估计时间为 720 秒,以匹配拆分后的负载
register_cuda_ci(est_time=720, suite="stage-c-test-4-gpu-b200")QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}} # 定义准确性阈值class TestQwen35FP4(CustomTestCase):
    def test_gsm8k(self):
        # 基础服务器启动参数,针对 4-GPU B200 配置,包括 TP 大小、Mamba 调度等
        base_args = [
            "--tp-size", "4",
            "--chunked-prefill-size", "2048",
            "--mamba-scheduler-strategy", "extra_buffer",
            "--mamba-track-interval", "128",
            "--mamba-ssm-dtype", "bfloat16",
            "--max-running-requests", "128",
            "--reasoning-parser", "qwen3",
            "--attention-backend", "trtllm_mha",
            "--quantization", "modelopt_fp4",
            "--model-loader-extra-config", '{"enable_multithread_load": true,"num_threads": 64}',
        ]
​
        # 定义测试变体,目前只启用 Triton 后端,FlashInfer 后端待修复
        variants = [
            ModelLaunchSettings(
                QWEN35_FP4_MODEL,
                extra_args=base_args,
                variant="Triton",
            ),
            # TODO: 修复 FlashInfer 后端后重新启用
            # ModelLaunchSettings(
            #     QWEN35_FP4_MODEL,
            #     extra_args=base_args + ["--linear-attn-decode-backend", "flashinfer"],
            #     variant="FlashInfer",
            # ),
        ]
​
        # 运行组合测试,验证 gsm8k 数据集的准确性,确保不低于阈值
        run_combined_tests(
            models=variants,
            test_name="Qwen3.5-397B-A17B-NVFP4",
            accuracy_params=AccuracyTestParams(
                dataset="gsm8k",
                baseline_accuracy=ACC_THRESHOLDS[QWEN35_FP4_MODEL]["gsm8k"],
                num_examples=200,
                num_threads=128,
                max_tokens=16000,
                thinking_mode="qwen3",
                temperature=0.6,
                top_p=0.95,
                top_k=20,
            ),
        )

test/registered/4-gpu-models/test_qwen35_fp4_mtp_v2.py

新增文件,包含 TestQwen35FP4MTPV2 类,用于测试 Qwen3.5 FP4 模型的 v2 MTP 推测解码功能,是拆分后的另一核心测试文件。

import unittest
from types import SimpleNamespace
import requests
from sglang.srt.environ import envs
from sglang.srt.utils import kill_process_tree
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.kits.reasoning_kit import ReasoningTokenUsageMixin
from sglang.test.run_eval import run_eval
from sglang.test.test_utils import (
    DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
    DEFAULT_URL_FOR_TEST,
    CustomTestCase,
    popen_launch_server,
)# 注册 CI 测试,设置估计时间为 540 秒,以适配拆分后的分区负载
register_cuda_ci(est_time=540, suite="stage-c-test-4-gpu-b200")QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}} # 准确性阈值定义class TestQwen35FP4MTPV2(ReasoningTokenUsageMixin, CustomTestCase):
    reasoning_parser_name = "qwen3" # 设置推理解析器
​
    @classmethod
    def setUpClass(cls):
        cls.model = QWEN35_FP4_MODEL
        cls.base_url = DEFAULT_URL_FOR_TEST
        cls.init_reasoning_token_verifier() # 初始化推理 token 验证器
        envs.SGLANG_ENABLE_SPEC_V2.set(True) # 启用 v2 推测解码特性
        # 启动服务器,使用 4-GPU 配置和推测解码参数
        cls.process = popen_launch_server(
            cls.model,
            cls.base_url,
            timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
            other_args=[
                "--tp-size", "4",
                "--chunked-prefill-size", "2048",
                "--mamba-scheduler-strategy", "extra_buffer",
                "--mamba-track-interval", "128",
                "--mamba-ssm-dtype", "bfloat16",
                "--max-running-requests", "128",
                "--reasoning-parser", "qwen3",
                "--attention-backend", "trtllm_mha",
                "--quantization", "modelopt_fp4",
                "--speculative-algorithm", "NEXTN", # v2 MTP 相关参数
                "--speculative-num-steps", "3",
                "--speculative-eagle-topk", "1",
                "--speculative-num-draft-tokens", "4",
                "--mem-fraction-static", "0.8",
                "--model-loader-extra-config", '{"enable_multithread_load": true,"num_threads": 64}',
            ],
        )
​
    @classmethod
    def tearDownClass(cls):
        envs.SGLANG_ENABLE_SPEC_V2.set(False) # 测试结束后禁用 v2 特性
        kill_process_tree(cls.process.pid) # 清理服务器进程
​
    def test_gsm8k(self):
        # 设置评估参数,运行 gsm8k 数据集测试
        args = SimpleNamespace(
            model=self.model,
            eval_name="gsm8k",
            num_shots=5,
            num_examples=200,
            max_tokens=16000,
            num_threads=128,
            repeat=1,
            temperature=0.6,
            top_p=0.95,
            top_k=20,
            base_url=self.base_url,
            host="http://127.0.0.1",
            port=int(self.base_url.split(":")[-1]),
        )
        metrics = run_eval(args) # 执行评估
        print(f"{metrics=}")
        self.assertGreaterEqual(metrics["score"], ACC_THRESHOLDS[self.model]["gsm8k"]) # 验证准确性
​
        # 获取服务器信息,检查推测解码性能指标
        server_info = requests.get(self.base_url + "/server_info")
        avg_spec_accept_length = server_info.json()["internal_states"][0]["avg_spec_accept_length"]
        print(f"{avg_spec_accept_length=}")
        self.assertGreater(avg_spec_accept_length, 3.3) # 确保推测解码性能达标

评论区精华

review 中仅显示批准,无详细评论;但 PR body 提及“per reviewer feedback — v2 MTP will be the default”,表明有隐含的设计决策:移除 v1 MTP 测试以避免冗余,聚焦于 v2 版本。

  • 移除 v1 MTP 测试的决策 (design): 决定删除 v1 测试以简化测试套件,聚焦于 v2 版本。

风险与影响

  • 风险:风险较低:文件分割可能略微增加维护复杂性,但测试逻辑未变,回归风险小;CI 分区调整需验证不会引入新的失败或资源冲突,例如分区数量增加可能导致资源分配不均,但 PR body 已确认所有分区在超时内完成。
  • 影响:对用户无直接影响;系统测试执行更可靠,减少超时失败,提升 CI 稳定性;团队测试代码更模块化,便于未来扩展和维护,但需注意文件分割后的依赖管理。
  • 风险标记:CI 配置变更风险, 测试维护负担

关联脉络

  • 暂无明显关联 PR

参与讨论