Prhub

#32229 fix(minimax): use routed TRT-LLM for NVFP4 MoE auto on SM100

原始 PR 作者 janbernloehr 合并时间 2026-08-10 11:45 文件变更 2 提交数 2 评论 8 代码增减 +58 / -1

执行摘要

修复 SM10X 上 MiniMax-M2 NVFP4 的 MoE 后端路由

PR body 明确说明:MiniMax-M2 NVFP4 uses routing semantics that are not supported by the plain flashinfer_trtllm MoE path. On SM10X (SM100/SM103), leaving --moe-runner-backend=auto could therefore cause MiniMax-M2.7-NVFP4 startup to fail。review 中 trevor-m 进一步确认 plain flashinfer_trtllm 对 MiniMax 不支持、flashinfer_cutlass 已过时;作者实测三个候选后端后,flashinfer_trtllm_routed 在生产并发下吞吐最高(并发 64 时 5978.6 vs cutlass 4528.1 tokens/s),TTFT 与平均延迟也最低,故最终选定 routed。plain TRT-LLM 的 correctness 问题另跟踪于 #26324,不作为本 PR 的直接目标。

值得阅读,作为“窄范围修复 + 后端决策前移”的样例。重点看两点:一是如何通过模型级 override 而非全局 fallback 控制爆炸半径;二是 review 中关于 A2A 组合与静默损坏验证的讨论,这些是同类改动容易遗漏的坑。若团队后续在 SM100 上推进 MiniMax 系列,建议先补齐 deepep 组合的显式处理与 #26324 的正确性 probe。

讨论亮点

核心交锋集中在“后端决策放哪、放多宽”:

  • trevor-m(首轮 DISMISSED review):We should be using --moe-runner-backend=flashinfer_trtllm_routed for cases like MiniMax where --moe-runner-backend=flashinfer_trtllm is not supported. Flashinfer cutlass is a bit obselete
  • mmangkad(CHANGES_REQUESTED):We shouldn’t make routed trtllm the default for every nvfp4 model, let’s keep the current default and only use routed for models like minimax that actually need it
  • nvpohanh:反对全局 fallback——FLASHINFER_TRTLLM does have some perf benefit so doing this will cause perf regressions,并提示 Will this break Thor (SM110)?
  • BBuf(阻塞意见):新条件在 --moe-a2a-backend=deepep 下同样触发,但 routed runner 只有 a2a=none/a2a=flashinfer 的 fused 注册,(deepep, flashinfer_trtllm_routed) 缺失会导致 MoeRunner.__init__NotImplementedError;同时质疑“无 NotImplementedError”不能证明正确性(#26324 是 HTTP 200 的静默损坏),要求加 temperature-0 probe。
  • mmangkad 与 trevor-m 还确认后端状态应在 server init 时一次解析,避免重复 getattr 惰性解析。

实现拆解

  1. 收缩方案范围:初版在 modelopt_quant.py 中加入全局 _resolve_nvfp4_moe_runner_backend,对所有 NVFP4 模型的 auto 做能力级 fallback;经 nvpohanh(性能回退)与 mmangkad(不应全局默认)反对后,改为只对 MiniMax-M2 生效。
  2. 核心解析逻辑:python/sglang/srt/arg_groups/overrides.py_minimax_m2_overrides 由直接返回改为构造 overrides 字典,在原有 enable_tf32_matmul 之外新增条件分支:is_sm100_supported()moe_runner_backend == "auto"get_model_config().quantization == "modelopt_fp4" 时写入 moe_runner_backend = "flashinfer_trtllm_routed"。解析发生在 server-argument override 阶段,早于模型层与 MoE/quantization runner 初始化,符合 trevor-m 提出的“后端应在 server init 时一次决定”原则。
  3. 范围与保底:只有 auto 被改写;显式指定后端(如 flashinfer_cutlass)原样保留;其他模型、非 SM10X(含 Thor/SM110)以及既有 SM120 CUTLASS fallback 均不受影响。
  4. 测试配套:test_model_overrides.py 新增 test_minimax_m2_sm10x_nvfp4_uses_routed_trtllm,通过 patch.object 控制 is_sm100_supported,用真实 ServerArgs 解析验证显式保留、非 NVFP4 保持 auto、SM10X NVFP4 解析为 routed、非 SM10X 保持 auto,以及 publish 后 get_exec().moe.moe_runner_backend 生效;同时断言 disable_shared_experts_fusion_resolved_overrides 中的来源记录,确保 routed 解析联动既有 _moe_runner_fusion_disable 声明。
文件 模块 状态 重要度
python/sglang/srt/arg_groups/overrides.py 配置覆盖 modified 6.28
test/registered/unit/test_model_overrides.py 配置测试 modified 5.78

关键符号

_minimax_m2_overrides test_minimax_m2_sm10x_nvfp4_uses_routed_trtllm

关键源码片段

python/sglang/srt/arg_groups/overrides.py core-logic

核心实现:`_minimax_m2_overrides` 新增 SM10X + modelopt_fp4 + auto 条件下将 `moe_runner_backend` 解析为 `flashinfer_trtllm_routed` 的逻辑,是本次修复的唯一源码变更点。

@_register_for("MiniMaxM2ForCausalLM")
def _minimax_m2_overrides(server_args: Any, hf_config: Any) -> dict:
    overrides = {"enable_tf32_matmul": True}
    logger.info(
        "Enable TF32 matmul for MiniMaxM2ForCausalLM model to improve gate gemm performance."
    )
    # MiniMax-M2 的 NVFP4 checkpoint 使用 routed 语义,plain flashinfer_trtllm
    # 路径不支持;SM10X 上 auto 会落到不可用实现、启动失败。这里只在
    # 「SM10X + auto + modelopt_fp4」组合下改写为 routed 后端,显式指定
    # 的后端以及其余模型、架构(含 Thor/SM110)保持原有行为。
    if (
        is_sm100_supported()
        and server_args.moe_runner_backend == "auto"
        and server_args.get_model_config().quantization == "modelopt_fp4"
    ):
        overrides["moe_runner_backend"] = "flashinfer_trtllm_routed"
        logger.info(
            "Use flashinfer_trtllm_routed as MoE runner backend on SM10X "
            "for MiniMaxM2ForCausalLM with modelopt_fp4."
        )
    return overrides
test/registered/unit/test_model_overrides.py test-coverage

新增 `test_minimax_m2_sm10x_nvfp4_uses_routed_trtllm`,用真实 `ServerArgs` 解析覆盖所有分支(显式保留、非 NVFP4、SM10X 命中、非 SM10X 不命中、publish 后 exec 生效),把本次修复的语义钉死。

def test_minimax_m2_sm10x_nvfp4_uses_routed_trtllm(self):
    """MiniMax-M2 NVFP4 的 auto 必须避开不支持的 plain TRT-LLM 路径。"""
    with patch.object(overrides_module, "is_sm100_supported", return_value=True):
        # 显式选择的后端必须原样保留
        explicit = self._construct(
            "MiniMaxM2ForCausalLM",
            "llama",
            quantization="modelopt_fp4",
            moe_runner_backend="flashinfer_cutlass",
        )
        # 非 NVFP4 量化不触发改写
        non_nvfp4 = self._construct(
            "MiniMaxM2ForCausalLM", "llama", quantization="fp8"
        )
        # SM10X + modelopt_fp4 + auto -> flashinfer_trtllm_routed
        nvfp4 = self._construct(
            "MiniMaxM2ForCausalLM", "llama", quantization="modelopt_fp4"
        )
​
    self.assertEqual(explicit.moe_runner_backend, "flashinfer_cutlass")
    self.assertEqual(non_nvfp4.moe_runner_backend, "auto")
    self.assertEqual(nvfp4.moe_runner_backend, "flashinfer_trtllm_routed")
    # routed 路径需要关闭共享专家融合,由既有 _moe_runner_fusion_disable 声明提供
    self.assertTrue(nvfp4.disable_shared_experts_fusion)
    # publish 后运行时 exec 读到的是同一解析结果
    self._publish(nvfp4)
    self.assertEqual(get_exec().moe.moe_runner_backend, "flashinfer_trtllm_routed")

评论区精华

是否对全部 NVFP4 模型默认 routed 设计

mmangkad 在 CHANGES_REQUESTED 中指出:`We shouldn’t make routed trtllm the default for every nvfp4 model, let’s keep the current default and only use routed for models like minimax that actually need it`

结论:改为仅 MiniMax-M2 模型生效,其他 NVFP4 模型保持默认 auto 行为。 · 已解决

全局 fallback 会造成性能回退 性能

nvpohanh 反对初版全局 fallback:`FLASHINFER_TRTLLM does have some perf benefit so doing this will cause perf regressions. And also, even if we want to fall back, we should fall back to cutedsl version.`

结论:放弃全局 fallback 方案,改为模型级 override,只对需要 routed 语义的 MiniMax-M2 生效。 · 已解决

Thor (SM110) 兼容性 正确性

nvpohanh 担心改动破坏 Thor:`Will this break Thor (SM110)? My preference would be to use "is_sm100_supported()" and use "is_sm120_supported()" for the elif part`

结论:最终实现只使用 `is_sm100_supported()`,不动 SM120 CUTLASS fallback,Thor/SM110 行为保持不变。 · 已解决

A2A backend 组合缺口 正确性

BBuf 提出阻塞性问题:新条件在 `--moe-a2a-backend=deepep` 下同样触发,但 routed runner 只有 `a2a=none`/`a2a=flashinfer` 的 fused 注册,`(deepep, flashinfer_trtllm_routed)` 缺失会导致 `MoeRunner.__init__` 抛 `NotImplementedError`,建议 A2A-aware 或提前拒绝并补 DeepEP 测试。

结论:BBuf 最终 APPROVED,但代码中未见 A2A 条件处理,该组合缺口未在本 PR 内解决。 · unresolved

#26324 静默损坏的正确性验证 测试

BBuf:`the failure reported in #26324 is silent corrupted generation with HTTP 200, so “benchmark completed without NotImplementedError” does not establish correctness. Could you include the issue's temperature-0 probe comparing patched auto against the known-good flashinfer_cutlass path`

结论:PR 内未补充 temperature-0 一致性 probe,正确性验证留待后续跟踪。 · unresolved

后端状态应一次解析而非惰性 getattr 设计

mmangkad:`Why backend state is repeatedly resolved instead of init once? Also do not use getattr`;trevor-m 补充:`the backend should be decided during server init when we parse server args and run the overrides and shouldn't change after that`

结论:最终方案把后端决策移到 server-argument override 阶段,一次解析后发布到 runtime context。 · 已解决

风险与影响

A2A 组合缺口(中风险):最终实现未把 moe_a2a_backend 纳入条件。SM100 + MiniMax-M2 + NVFP4 + --moe-a2a-backend=deepep + auto 仍会解析到 routed,而 routed runner 没有 deepep 的 fused 注册,初始化可能仍以 NotImplementedError 失败(BBuf 指出,未在本 PR 内处理)。
正确性验证缺口(中风险):BBuf 指出 #26324 的失败形态是静默错误生成,PR 的基准结果只覆盖了“能启动、无异常”,未提供与已知良好 flashinfer_cutlass 路径的 temperature-0 一致性对比;对于此前有静默损坏记录的 TRT-LLM 家族,routed 路径的正确性仍需额外证据。
性能与兼容性(低风险):改动仅命中 MiniMax-M2 + modelopt_fp4 + auto + SM10X,显式后端与其他架构不受影响;routed 在并发 1 下吞吐 198.7 tokens/s 也高于 cutlass 的 142.9,未见回退。get_model_config() 依赖模型的 config 在 override 阶段已可用,若未来 config 装配时机变化需要同步调整。

用户侧:GB200/SM100 上 MiniMax-M2.7-NVFP4 默认启动失败被修复,且生产并发(16/64/128)下输出吞吐提升约 28%~35%,TTFT 与平均延迟最低。
系统侧:解析发生在 overrides.py 的模型级 override,早于 MoE 初始化,对框架其他路径零侵入;get_exec().moe.moe_runner_backend 发布链路被单测钉住。
团队侧:为“模型 × 量化 × 架构 × 后端”组合解析提供了收敛在 server-arg 阶段的范例,与 #34080 系列 config 重构的 _resolved_overrides 机制衔接,后续模型可复用同样模式。

A2A=deepep 组合未覆盖 正确性验证依赖 #26324 配置解析核心路径变更 影响面窄(仅 MiniMax-M2 + NVFP4 + SM10X)

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论