Prhub

#45836 [NVFP4 MoE/Deepseek V4] Marlin: wire SwiGLU clamp + allow it for clamped models on non-Blackwell

原始 PR 作者 mikekg 合并时间 2026-06-24 03:21 文件变更 2 提交数 1 评论 6 代码增减 +4 / -0

执行摘要

Marlin NVFP4 MoE 支持 SwiGLU clamp

修复 Issue #45859:NVFP4 MoE 模型在设置 swiglu_limit 时,非 Blackwell GPU 上 select_nvfp4_moe_backend()NVFP4_BACKENDS_WITH_CLAMP 仅包含 FLASHINFER_TRTLLM(Blackwell 专用),导致无候选后端而抛出 NotImplementedError。同时,Marlin 后端的内核已支持 SwiGLU clamp(通过 swiglu_limit_func),但未收到 clamp 值,若仅将 Marlin 加入集合而不传参会导致数值错误。

该 PR 值得快速合并,属于关键 bugfix。改动精简、意图明确,经过 review 确认和端到端验证。建议精读 oracle/nvfp4.py 中 backend 选择逻辑和 config.py 中参数传递方式,可了解 NVFP4 MoE 后端的调度机制。

讨论亮点

Review 中主要有以下讨论:

  1. pavanimajety 指出测试中可复用已有的 tests/kernels/quantization/nvfp4_utils.py,但由于后续移除了该测试文件,此建议未采用。
  2. mgoin 认为不需要冗长的单元测试,建议仅用简单的 E2E 模型测试或简短的断言验证参数传递和 backend 选择;作者接受并移除了测试文件。
  3. mgoin 认为在 oracle/nvfp4.py 添加 Marlin 时的注释过于冗长,无需保留;作者接受并删除了注释。
  4. 最终双方 reviewer 均批准(Approved)。

实现拆解

  1. config.pynvfp4_w4a16_moe_quant_config() 增加 gemm1_clamp_limit 参数并传递给 FusedMoEQuantConfig.make():该函数用于构造 NVFP4 W4A16 MoE 量化配置,新增可选参数 gemm1_clamp_limit,以便 Marlin 后端在构造量化配置时能将 SwiGLU clamp 值传给内核。
  2. oracle/nvfp4.pymake_nvfp4_moe_quant_config() 的 MARLIN 分支传入 gemm1_clamp_limit=swiglu_limit:在创建 Marlin 后端的量化配置时,将模型配置中的 swiglu_limit 显式传递给 nvfp4_w4a16_moe_quant_config,确保内核实际应用 clamp。
  3. oracle/nvfp4.py:将 MARLIN 加入 NVFP4_BACKENDS_WITH_CLAMP 集合:在 select_nvfp4_moe_backend() 中,将 NvFp4MoeBackend.MARLIN 添加到 clamp 支持后端集合,使得 clamped 模型在非 Blackwell GPU 上可被 Marlin 后端选中。
  4. 移除多余注释:根据 review 反馈,删除 oracle/nvfp4.py 中添加 Marlin 时的冗长注释。
  5. 移除测试文件:根据 review 反馈,删除本 PR 最初包含的单元测试文件 tests/kernels/moe/test_marlin_nvfp4_swiglu_clamp.py,采用 E2E 模型测试验证。
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/config.py 量化配置 modified 5.79
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py 量化配置 modified 6.08

关键符号

nvfp4_w4a16_moe_quant_config select_nvfp4_moe_backend make_nvfp4_moe_quant_config

关键源码片段

vllm/model_executor/layers/fused_moe/config.py data-contract

在 `nvfp4_w4a16_moe_quant_config()` 中新增 `gemm1_clamp_limit` 参数,并传递给 `FusedMoEQuantConfig.make()`,是 clamp 值传递的数据链路起点。

def nvfp4_w4a16_moe_quant_config(
    g1_alphas: torch.Tensor,
    g2_alphas: torch.Tensor,
    w1_scale: torch.Tensor,
    w2_scale: torch.Tensor,
    # 新增参数,用于传递 SwiGLU clamp 值
    gemm1_clamp_limit: float | None = None,
) -> FusedMoEQuantConfig:
    """
    Construct a quant config for 16-bit activations and nvfp4 weights.
    """
    return FusedMoEQuantConfig.make(
        quant_dtype=None,
        w1_scale=w1_scale,
        w2_scale=w2_scale,
        g1_alphas=g1_alphas,
        g2_alphas=g2_alphas,
        weight_dtype="nvfp4",
        # 将 clamp 值传给 config,内核将据此限制门控激活值的范围
        gemm1_clamp_limit=gemm1_clamp_limit,
    )
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py data-contract

修改 backend 选择逻辑和量化配置构造函数:将 `MARLIN` 加入 `NVFP4_BACKENDS_WITH_CLAMP` 集合,并在 `make_nvfp4_moe_quant_config()` 的 MARLIN 分支中传递 `gemm1_clamp_limit`。

def select_nvfp4_moe_backend(
    config: FusedMoEConfig,
    weight_key: QuantKey | None,
    activation_key: QuantKey | None,
) -> tuple[NvFp4MoeBackend, type[mk.FusedMoEExperts]]:
    # ...
    # 定义了支持 clamp 的后端集合,原只有 FLASHINFER_TRTLLM(Blackwell 专用)
    NVFP4_BACKENDS_WITH_CLAMP = {
        NvFp4MoeBackend.FLASHINFER_TRTLLM,
        # 新增 Marlin 后端,使其可在非 Blackwell GPU 上处理 clamped 模型
        NvFp4MoeBackend.MARLIN,
    }
​
    # 若模型配置了 swiglu_limit,则仅保留 clamp 支持集合中的后端
    if config.swiglu_limit is not None:
        AVAILABLE_BACKENDS = [
            b for b in AVAILABLE_BACKENDS if b in NVFP4_BACKENDS_WITH_CLAMP
        ]
    # ...def make_nvfp4_moe_quant_config(
    backend: NvFp4MoeBackend,
    # ...
    swiglu_limit: float | None = None,
) -> FusedMoEQuantConfig:
    if backend == NvFp4MoeBackend.MARLIN:
        return nvfp4_w4a16_moe_quant_config(
            g1_alphas=w13_scale_2,
            g2_alphas=w2_scale_2,
            w1_scale=w13_scale,
            w2_scale=w2_scale,
            # 将 swiglu_limit 作为 gemm1_clamp_limit 传入,确保内核实际应用 clamp
            gemm1_clamp_limit=swiglu_limit,
        )
    # ... 其他后端分支

评论区精华

测试文件必要性 测试

mgoin 认为不需要冗长的单元测试,建议仅用简单的 E2E 模型测试或简短的断言验证参数传递和 backend 选择。

结论:作者接受并移除了测试文件。 · 已解决

注释冗余 style

mgoin 认为在 oracle/nvfp4.py 中添加 Marlin 时的注释过于冗长。

结论:作者接受并删除了注释。 · 已解决

测试工具复用 style

pavanimajety 指出可复用已有的 nvfp4_utils.py。

结论:由于测试文件被移除,该建议未采纳。 · 已解决

风险与影响

  1. 回归风险(低):改动仅涉及两个文件的各 2 行,且不影响未设置 swiglu_limit 的模型(Marlin 后端的正常选择路径不变)。
  2. 数值正确性风险(低):需确保 gemm1_clamp_limit 参数在 Marlin 内核中正确传递并应用;作者已在 H100 上端到端验证了 DeepSeek-V4-Flash 模型的正确输出。
  3. 兼容性风险(低):仅对 NVFP4 MoE 量化路径有影响,不影响其他量化方案或后端。
  1. 用户影响:使 DeepSeek-V4 等设置 swiglu_limit 的 NVFP4 MoE 模型可在 SM80/SM89/SM90 GPU 上运行,之前这些模型在这些架构上无法加载。
  2. 系统影响:无,改动极小且限定在量化配置构造和 backend 选择逻辑中。
  3. 团队影响:低,代码维护成本极低。
核心路径变更 缺少测试覆盖

关联 Issue

#45859 [Feature]: Wire swiglu_limit from model config into MoE kernel dispatch for DeepSeek V4 and other models

完整报告

参与讨论