Prhub

#40082 Integrate flashinfer b12x MoE and FP4 GEMM kernels for SM120/121

原始 PR 作者 meena-at-work 合并时间 2026-05-21 01:21 文件变更 10 提交数 15 评论 66 代码增减 +595 / -4

执行摘要

为 SM12x GPU 集成 FlashInfer b12x MoE 和 FP4 GEMM 后端

在 SM120/SM121 架构上,NVFP4 推理工作负载需要高性能的 MoE 和 GEMM 内核。FlashInfer 社区开发了 b12x CuTe DSL 内核(参见 FlashInfer PR #3051、#3066、#3080),在 decode-bound 小 M 场景下相比 CUTLASS 可带来 1.3-1.6x 加速。本 PR 将这些内核集成到 vLLM 中,使 DGX Spark 和 RTX Pro 6000 用户获得更好的 NVFP4 性能。

值得精读:该 PR 展示了如何将第三方自定义 kernel 后端集成到 vLLM 现有的量化 MoE 和 Linear 架构中,特别是 process_weights_after_loading 中的 scale 融合技巧。review 讨论中关于设计权衡 — 独立 backend 与复用 — 的对话也值得参考。

讨论亮点
  • 单独 backend 的必要性:pavanimajety 质疑为何不重用现有 CuTe DSL 路径,meena-at-work 解释 b12x 内核来自独立社区项目且差异显著,FlashInfer 团队也将其视为独立后端,因此保留单独入口更清晰。
  • a2_gscale 原位修改风险:gemini-code-assist 指出对 layer.a2_gscale.fill_(1.0) 会破坏模型参数,影响后端切换。meena-at-work 在后续 commit 中改为在 apply 时传入 torch.ones_like(a2_gscale),保留原始值。
  • MMA 布局重复计算:gemini-code-assist 指出在 apply 中每次前向都调用 convert_sf_to_mma_layout 增加开销。meena-at-work 将转换提前到 process_weights_after_loading 并缓存 w1_sf_mma/w2_sf_mma
  • CUTLASS SM121 MMA op guard 不兼容:eugr 报告在 DGX Spark 启动时因 Warp MMA 架构检查失败(sm_121a 不被接受)导致报错。meena-at-work 将 b12x 后端从自动选择中移除,并等待上游 CUTLASS 修复(NVIDIA/cutlass#3082)。

实现拆解

  1. 创建 FlashInferB12xExperts(MoE 专家类):在 vllm/model_executor/layers/fused_moe/experts/flashinfer_b12x_moe.py 新增 FlashInferB12xExperts(继承 FusedMoEExpertsModular)。其 process_weights_after_loading 方法将 modelopt 校准的全局尺度融合进块尺度(避免中间激活饱和),并预计算 w1_sf_mma / w2_sf_mma 到 MMA 布局;apply 方法调用 flashinfer_b12x_fused_moe 一次完成路由、W1 GEMM、SwiGLU、W2 GEMM 与归约。

  2. 添加 FlashInferB12xNvFp4LinearKernel(稠密 GEMM 类):在 vllm/model_executor/kernels/linear/nvfp4/flashinfer.py 新增 FlashInferB12xNvFp4LinearKernel,其 is_supported 要求 SM120+ 并有 Sm120BlockScaledDenseGemmKernelapply_weights 使用 scaled_fp4_quant(backend="b12x")flashinfer_scaled_fp4_mm(backend="b12x")

  3. 注册到后端选择架构:在 vllm/model_executor/layers/fused_moe/oracle/nvfp4.pyNvFp4MoeBackend 枚举中添加 FLASHINFER_B12X,并在 select_nvfp4_moe_backend 中将其从 AVAILABLE_BACKENDS 排除(避免自动误选)。在 vllm/model_executor/kernels/linear/__init__.py 中将 FlashInferB12xNvFp4LinearKernel 注册到 _NVFP4_BACKEND_TO_KERNEL 映射(键 flashinfer-b12x)。

  4. 添加功能检测与惰性导入:在 vllm/utils/flashinfer.py 中新增 flashinfer_b12x_fused_moe 惰性导入,以及 has_flashinfer_b12x_gemm()has_flashinfer_b12x_moe() 缓存检测函数,确保仅在 FlashInfer 版本满足时启用。

  5. 更新配置与测试:在 vllm/config/kernel.pyMoEBackend Literal 中增加 "flashinfer_b12x"。新增 tests/kernels/moe/test_flashinfer_b12x_moe.py(24 项参数化测试,在非 SM12x 硬件上跳过)并扩展 tests/kernels/quantization/test_flashinfer_nvfp4_scaled_mm.py 以覆盖 b12x 路径。

文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/experts/flashinfer_b12x_moe.py 融合 MoE added 9.36
vllm/model_executor/kernels/linear/nvfp4/flashinfer.py 线性层 modified 8.63
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py MoE oracle modified 6.66
vllm/utils/flashinfer.py FlashInfer utils modified 7.27
vllm/model_executor/kernels/linear/__init__.py 线性层注册 modified 5.8
tests/kernels/moe/test_flashinfer_b12x_moe.py MoE 测试 added 6.99

关键符号

FlashInferB12xExperts.__init__ FlashInferB12xExperts.process_weights_after_loading FlashInferB12xExperts.apply FlashInferB12xNvFp4LinearKernel.is_supported FlashInferB12xNvFp4LinearKernel.process_weights_after_loading FlashInferB12xNvFp4LinearKernel.apply_weights has_flashinfer_b12x_gemm has_flashinfer_b12x_moe select_nvfp4_moe_backend map_nvfp4_backend

关键源码片段

vllm/model_executor/layers/fused_moe/experts/flashinfer_b12x_moe.py core-logic

核心新增:FlashInferB12xExperts MoE 专家类,实现与 SM12x fused MoE kernel 的集成。包括 weight normalization 和 MMA scale 转换。

# flashinfer_b12x_moe.py — FlashInferB12xExperts(核心 MoE 专家类)class FlashInferB12xExperts(mk.FusedMoEExpertsModular):
    """FlashInfer CuteDSL fused MoE 专家类, 目标 SM12x (SM120/SM121)。
    使用 ``b12x_fused_moe`` 从 FlashInfer PR #3080,
    将 token 分发、W1 GEMM、SwiGLU、W2 GEMM 融合为一个 kernel 调用。
    BF16 隐藏状态直接传入, kernel 内执行量化。
    """
​
    def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
        # 融合权重全局尺度到块尺度
        # vLLM 约定 : block_scale = max_abs * w_gs / fp4_max, g1_alphas = 1/w_gs
        # SM12x kernel 将 w1_alpha 作为权重反量化因子 , 与激活尺度分离
        # 我们将 w_gs 烘焙进块尺度 , 使 w1_alpha = 1.0
        layer.w13_weight_scale.data = (
            layer.w13_weight_scale.float() * layer.w13_weight_scale_2.view(-1, 1, 1)
        ).to(layer.w13_weight_scale.dtype)
        layer.w13_weight_scale_2.data.fill_(1.0)
        # 同理处理 FC2 weight
        layer.w2_weight_scale.data = (
            layer.w2_weight_scale.float() * layer.w2_weight_scale_2.view(-1, 1, 1)
        ).to(layer.w2_weight_scale.dtype)
        layer.w2_weight_scale_2.data.fill_(1.0)
​
        # 将激活间尺度置为 1.0:kernel 使用动态 per-block 量化
        # modelopt 校准的 a2_gscale 会导致饱和
        if self.a2_gscale is not None:
            self.a2_gscale.fill_(1.0)
​
        # 预计算 MMA-layout 的 scale 数据 , 避免每次前向重复转换
        assert self.w1_scale is not None
        num_experts_w1, m1, k1_sf = self.w1_scale.shape
        k1 = k1_sf * 16
        self.w1_sf_mma = flashinfer_convert_sf_to_mma_layout(
            self.w1_scale.reshape(num_experts_w1 * m1, k1_sf),
            m=m1, k=k1, num_groups=num_experts_w1,
        )
        # 同理 w2_sf_mma...

评论区精华

是否需要独立 backend? 设计

pavanimajety 建议重用现有 CuTe DSL 路径;meena-at-work 解释 b12x 内核由社区独立开发,FlashInfer 团队将其作为独立后端,保留独立入口更清晰。

结论:保留独立 backend;将用户可见名称统一为 `flashinfer_b12x`。 · 已解决

a2_gscale 原位修改破坏参数 正确性

gemini-code-assist 指出 `layer.a2_gscale.fill_(1.0)` 永久改变模型中值,影响后端切换;建议在 apply 时传递 ones。

结论:修复为在 apply 时传入 `torch.ones_like(a2_gscale)` 而非原地修改。 · 已解决

CUTLASS SM121 MMA 架构不兼容 正确性

eugr 在 DGX Spark 上报 `MmaSM120BlockScaledOp` 拒绝 `sm_121a`;需手动 patch nvidia-cutlass-dsl 的 warp/mma.py。

结论:暂时从自动选择中排除 b12x 后端,等待上游 CUTLASS 修复(NVIDIA/cutlass#3082);用户可通过显式参数手动启用。 · 已解决

EP 专家数不匹配 (global vs local) 正确性

idonta 发现 `b12x_fused_moe` JIT 使用全局专家数创建 faked tensor,但运行时传递的是 local 维度的权重,导致形状错误。

结论:未在本 PR 修复;建议使用 TP 而非 EP 以避免该问题,FlashInfer 侧需进一步更新。 · unresolved

风险与影响

  1. 依赖版本严格:需要 FlashInfer >=0.6.9 (推荐 0.6.11+)及 nvidia-cutlass-dsl==4.4.2(4.5.0 会产生坏 PTX)。用户如未按文档配置特定版本将无法使用。
  2. CUTLASS SM121 兼容性MmaSM120BlockScaledOp 默认不支持 sm_121a,需用户手动 patch warp/mma.py 或依赖上游新版 cutlass-dsl。b12x 已排除自动选择以规避此问题。
  3. EP 专家数不匹配:idonta 报告在 Expert Parallel 下 JIT 内核使用全局专家数而运行时使用本地专家数,导致形状错误。该问题不在本 PR 范围内(建议先使用 TP)。
  4. 对无 act_and_mul MLP 模型支持不足:Nemotron 等模型使用 ReLU2 激活,当前 b12x MoE 后端会拒绝加载,需等待 askliar 的 WIP 分支。
  5. FP8 调度器拒绝 b12x 标签:混合精度 checkpoint 中 FP8 部分的 moe_backend 设置会被 b12x 值干扰,需额外映射处理(暂未纳入本 PR)。
  • 用户影响:SM120/SM121 GPU(DGX Spark、RTX 5090、RTX Pro 6000)用户可通过设置环境变量手动激活 b12x 后端,获得 decode 场景 6-60% 的吞吐提升。非 SM12x 硬件无任何变化。
  • 系统影响:需要相应升级 FlashInfer 和 nvidia-cutlass-dsl 依赖;vLLM 项目增加两个新后端类,但默认不启用,无侵入性。
  • 团队影响:需维护额外后端变体;后续 CUTLASS 升级后需重新启用自动选择。
依赖特定 flashinfer 与 cutlass-dsl 版本 CUTLASS SM121 兼容性问题 EP 专家数不匹配 nemotron 无 act_and_mul 不支持 FP8 调度器标签冲突 排除自动选择,需用户显式启用

关联 Issue

#3051 feat: Add backend="b12x" for mm_fp4 on SM120
#3066 feat: Add b12x CuTe DSL fused MoE for SM120
#3080 feat: Add b12x_fused_moe / B12xMoEWrapper SM120 APIs with micro kernel and ReLU2

完整报告

参与讨论