# PR #35919 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[DeepSeek V4] Default FP4 checkpoints to FlashInfer MXFP4 MoE
- 合并时间：2026-08-22 07:48
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/35919

---

# 执行摘要

- 一句话：DSV4 FP4 检查点自动选 FlashInfer MXFP4 MoE runner
- 推荐动作：值得精读。重点学习两点：一是「自动默认值」的 fallback 条件设计——平台、架构白名单、A2A 模式、反量化环境变量、显式用户选择逐层让位，避免默认值吞噬用户意图；二是单元测试的矩阵化写法，用 `patch.object` 逐项钉住每个 fallback 分支。FP4 + DeepEP/A2A 用户应验证自己的启动组合是否仍可用。

# 功能与动机

PR body 明确指出：`DeepSeek V4 FP4 expert checkpoints require the FlashInfer MXFP4 MoE runner on supported NVIDIA GPUs. Select it automatically when the user leaves the MoE runner backend at auto.` 此前仅 SM120 架构通过 `_deepseek_v4_sm120_moe` 后处理 pass 获得该默认值，SM90/SM100 上的 FP4 检查点用户必须手动传参才能走正确的 runner，配置成本高且易错。本 PR 将这一默认行为扩展至全部支持的 NVIDIA 架构，并并入统一的 overrides 注册表，同时保持显式用户选择优先。

# 实现拆解

本 PR 的核心是把 MoE runner 的自动选择逻辑从分散的 post-process pass 收敛进模型 overrides 注册表，并按 4 个步骤落地：

1. **合并 MoE runner 选择逻辑 **（`python/sglang/srt/arg_groups/overrides.py`）：在 `_deepseek_v4_overrides` 中，当 `server_args.moe_runner_backend == "auto"` 时，先保留 NVFP4 混合检查点（`nvfp4_moe_meta is not None`）路由到 `flashinfer_trtllm_routed` 的既有分支；新增 `elif` 分支，在同时满足「`device == "cuda"`、非 HIP、`moe_a2a_backend == "none"`、未开启 `envs.SGLANG_DSV4_FP4_DEQUANT`、`model_config.is_fp4_experts`、SM90/SM100/SM120 之一」时写入 `flashinfer_mxfp4`。同时删除 `_deepseek_v4_sm120_moe` 这个 `register_post_process` 函数，避免两处逻辑分叉。
2. **清理遗留调用 **（`python/sglang/srt/server_args.py`）：删除 `_handle_model_specific_adjustments` 中通过 `run_post_process_pass` 调用 `_deepseek_v4_sm120_moe` 的 legacy 代码与相关 import（净删除 9 行）。这一步是必要的：符号已删除，遗留调用会立即 `ImportError`；同时符合近期 config 重构「默认值统一走 overrides 解析管线」的方向。
3. **重写单元测试 **（`test/registered/unit/test_model_overrides.py`）：`_args` 默认构造现在携带 `is_fp4_experts=True` 的 FP4 检查点，使正向断言能命中新分支；新增 6 类 fallback 断言（显式用户选择、A2A/DeepEP、FP4 反量化 env、FP8 检查点、NPU/HIP、不支持的 NVIDIA 架构），并删除随 `_deepseek_v4_sm120_moe` 一起移除的 `test_deepseek_v4_sm120_moe_pass`。
4. **E2E 测试演练自动选择 **（`test/registered/models_e2e/test_deepseek_v4_flash_fp4_b200.py`）：移除启动命令中显式的 `--moe-runner-backend flashinfer_mxfp4`，让 B200 每提交 CI 真实走一遍自动选择路径，防止默认值与手动参数脱节。PR body 报告该测试 9 项全部通过，启动日志出现 `Use flashinfer_mxfp4 as MoE runner backend for DeepseekV4ForCausalLM`。

关键文件：
- `python/sglang/srt/arg_groups/overrides.py`（模块 默认配置；类别 source；类型 core-logic；符号 _deepseek_v4_overrides, _deepseek_v4_sm120_moe）: 核心变更文件：将 FP4 检查点的 flashinfer_mxfp4 默认选择逻辑并入 `_deepseek_v4_overrides`，删除仅覆盖 SM120 的 `_deepseek_v4_sm120_moe` 后处理 pass，是本次行为变更的主入口。
- `test/registered/unit/test_model_overrides.py`（模块 配置测试；类别 test；类型 test-coverage；符号 test_deepseek_v4_overrides_at_callable_level, test_deepseek_v4_sm120_moe_pass）: 测试主配套：重写 `test_deepseek_v4_overrides_at_callable_level`，用 patch 矩阵覆盖正向路径与全部 fallback 分支，删除随 pass 移除的 `test_deepseek_v4_sm120_moe_pass`，是防止默认值逻辑回归的关键保障。
- `python/sglang/srt/server_args.py`（模块 启动参数；类别 source；类型 dependency-wiring）: 删除 `_handle_model_specific_adjustments` 中对已移除的 `_deepseek_v4_sm120_moe` 的 legacy 调用与 import，避免引用不存在的符号，也是 config 解析管线收敛的一环。
- `test/registered/models_e2e/test_deepseek_v4_flash_fp4_b200.py`（模块 E2E 测试；类别 test；类型 test-coverage）: B200 每提交 CI 测试：移除显式 `--moe-runner-backend flashinfer_mxfp4`，让 E2E 真实演练 auto 自动选择路径，防止默认值与手动参数脱节。

关键符号：_deepseek_v4_overrides, _deepseek_v4_sm120_moe, test_deepseek_v4_overrides_at_callable_level, test_deepseek_v4_sm120_moe_pass

## 关键源码片段

### `python/sglang/srt/arg_groups/overrides.py`

核心变更文件：将 FP4 检查点的 flashinfer_mxfp4 默认选择逻辑并入 `_deepseek_v4_overrides`，删除仅覆盖 SM120 的 `_deepseek_v4_sm120_moe` 后处理 pass，是本次行为变更的主入口。

```python
# python/sglang/srt/arg_groups/overrides.py
# 变更后的完整 _deepseek_v4_overrides：MoE runner 默认值选择集中在这里，
# 原先独立的 _deepseek_v4_sm120_moe post-process pass 已被删除并合并进本函数。

@_register_for("DeepseekV4ForCausalLM")
def _deepseek_v4_overrides(server_args: Any, hf_config: Any) -> dict:
    """DeepSeek V4 attention/page/window/MoE-runner 默认值。

    kv-cache dtype 与 NPU split-backend 写入、max_running_requests 填充
    和校验仍留在 hook 的 legacy slot。
    """
    from sglang.srt.server_args import ServerArgs

    model_arch = hf_config.architectures[0]
    overrides: Dict[str, Any] = {"attention_backend": "dsv4"}

    page_size = 256
    if server_args.device == "npu":
        # NPU 保持设备相关的 dsv4 后端（注册表路由到 Ascend V4 子类），
        # 只有 pool 几何与 dtype 不同，因此 page_size 改为 128。
        page_size = 128
        overrides["prefill_attention_backend"] = "dsv4"
        overrides["decode_attention_backend"] = "dsv4"
    overrides["page_size"] = page_size
    logger.info(
        f"Use dsv4 attention backend for {model_arch}, setting page_size to {page_size}."
    )

    # 用户未显式修改 swa_full_tokens_ratio 时才写入默认值 0.1
    if server_args.swa_full_tokens_ratio == ServerArgs.swa_full_tokens_ratio:
        overrides["swa_full_tokens_ratio"] = 0.1
        logger.info(f"Setting swa_full_tokens_ratio to 0.1 for {model_arch}.")

    # 仅当用户把 MoE runner 留在 auto 时，才做模型级默认选择；
    # 显式传入的 --moe-runner-backend 永远优先，不会被覆盖。
    if server_args.moe_runner_backend == "auto":
        model_config = server_args.get_model_config()
        # nvidia/DeepSeek-V4-Pro-NVFP4 是混合 FP8+NVFP4 检查点，
        # 必须使用 routed TRT-LLM runner，优先级高于 FP4 分支。
        if model_config.nvfp4_moe_meta is not None:
            overrides["moe_runner_backend"] = "flashinfer_trtllm_routed"
            logger.info(
                "Use flashinfer_trtllm_routed as MoE runner backend for "
                f"{model_arch} hybrid FP8+NVFP4 checkpoint."
            )
        elif (
            server_args.device == "cuda"
            and not is_hip()
            and server_args.moe_a2a_backend == "none"
            and not envs.SGLANG_DSV4_FP4_DEQUANT.get()
            and model_config.is_fp4_experts
            and (is_sm90_supported() or is_sm100_supported() or is_sm120_supported())
        ):
            # FP4 expert 检查点在受支持的 NVIDIA 架构上默认走 FlashInfer MXFP4：
            # - MXFP4 只支持标准（非 A2A）dispatcher，所以 DeepEP/A2A 下不选择；
            # - 开启运行时 FP4->FP8 反量化时保留通用 FP8 runner；
            # - 架构白名单之外的 GPU 不选择，避免引用无法启动的 kernel。
            overrides["moe_runner_backend"] = "flashinfer_mxfp4"
            logger.info(
                "Use flashinfer_mxfp4 as MoE runner backend for " f"{model_arch}."
            )
    return overrides

```

# 评论区精华

该 PR 没有留下任何 review 评论（`review_comments_count` 为 0），核心验证集中在 issue 评论里的 CI 重跑记录：

- Fridge003 多次发起 `/rerun-test`，`test_deepseek_v4_flash_fp4_b200.py`（4-gpu-b200）与 `test_deepseek_v4_flash_fp4_h200.py`（8-gpu-h200）首轮均失败，重跑后双双通过；`test_model_overrides.py` 单测也通过。
- 由于缺少独立审查视角，所有风险覆盖依赖作者自测（B200 GSM8K 0.970、bs=1 decode 385.66 token/s）与新增单元测试。

- B200 / H200 E2E 重跑验证自动选择路径 (testing): B200（SM100）与 H200（SM90）E2E 最终通过，验证了移除显式 `--moe-runner-backend` 后自动选择 `flashinfer_mxfp4` 的路径。
- 缺少独立 review 评论 (other): 默认值类变更缺少第二人审查视角，风险主要由单元测试矩阵与 E2E 自测兜底。

# 风险与影响

- 风险：
 1. **默认行为变更**：FP4 检查点 + `auto` 用户在支持的 NVIDIA GPU 上会无提示切换到 `flashinfer_mxfp4`，若其原工作流依赖通用 FP8 runner 或反量化路径，需确认 `SGLANG_DSV4_FP4_DEQUANT` 等环境变量组合仍符合预期。
 2. **FP4 + A2A/DeepEP 组合缺口**：`moe_a2a_backend == "none"` 是硬条件，测试只断言「不覆盖」，未验证 FP4 检查点搭配 `--moe-a2a-backend deepep` 时能否真正启动——该组合可能既不走 MXFP4 也无其他 fallback，存在静默配置缺口。
 3. **SM90 新增覆盖**：白名单从 SM120 扩展到 SM90/SM100，依赖 FlashInfer MXFP4 在 SM90 的 kernel 可用性；H200（SM90）E2E 重跑后通过，但 CI 首轮失败过，环境稳定性有待观察。
 4. **无外部 review**：作者自行合并，缺少第二人审查，默认值类变更尤其依赖测试完备性来兜底。
 - 影响：用户侧：DeepSeek V4 FP4 检查点在 B200/GB300（SM100）、H200（SM90）与 SM120 设备上开箱即用，不再需要手工指定 `--moe-runner-backend flashinfer_mxfp4`，减少配置错误；启动日志会明确输出所选 backend。系统侧：配置解析路径简化，少一个 post-process pass，默认值统一收敛到 `overrides.py` 注册表，与 config 重构系列（PR#35905-35910）方向一致。性能侧：B200 上 GSM8K 0.970 达标，bs=1 decode 385.66 token/s、输出吞吐 2104.59 token/s，均通过测试门槛。
 - 风险标记：默认行为变更 , FP4 + DeepEP 组合缺口 , SM90 新增覆盖 , 缺少外部 review

# 关联脉络

- PR #35910 config: publish before the launcher reads effective configuration: 同属 config 重构系列：本 PR 删除 server_args.py 中遗留的 post-process 调用，与「配置发布时机前移、默认值统一走 overrides 解析管线」的方向一致。
- PR #35906 config: project the config bags from the resolution result: 该 PR 直接改过 `python/sglang/srt/arg_groups/overrides.py`（投影 config bags），本 PR 继续在该文件收敛 DeepSeek V4 默认值，属同一解析管线的演进。
- PR #35405 [NVIDIA] Fix SM107 MXFP8 activation prep: 同为 Blackwell 平台上 MXFP 量化路径的修复与默认值调整，与本 PR 的 flashinfer_mxfp4 选择逻辑共享量化 kernel 兼容性考量。