Prhub

#43727 [MoE] Remove inplace fused experts mechanism

原始 PR 作者 zyongye 合并时间 2026-05-28 11:00 文件变更 44 提交数 5 评论 4 代码增减 +11 / -237

执行摘要

移除已失效的 inplace MoE 融合专家机制

在 torch 2.9+ 中,torch custom op 无法安全表示输出别名输入,导致 inplace_fused_expertsdisable_inplace() 强制禁用(参见 issue #26378)。自 vLLM 将 torch 最低版本提升至 2.11.0 后,inplace 路径始终为死代码。PR 执行已在 fused_moe.py:1585 遗留的 TODO:“Can get rid of inplace/outplace torch ops.”,彻底清理该技术债务。

建议阅读此 PR,以了解如何在大型代码库中安全、系统地删除被版本约束废弃的死代码。特别值得关注的是 torch custom op 输出别名输入的约束如何驱动架构决策,以及如何通过测试和 CI 验证大规模接口变更的正确性。

讨论亮点

在 code review 中,bnellnm 首先建议将 dispatch_fused_experts_func 分支直接替换为 torch.ops.vllm.fused_experts 调用,并将残留的 TODO 注释清理干净;提交者 zyongye 确认已更新。此外,PR 合并后 fxmarty-amd 在 issue 评论中指出该 PR 破坏了 benchmarks/kernels/benchmark_moe.py(因 disable_inplace 被删除),随后在 #44041 中修复。

实现拆解

  1. 删除 inplace_fused_experts 及 inplace_fused_experts_fake 的 custom op 注册:在 vllm/model_executor/layers/fused_moe/fused_moe.py 中移除两个函数及 direct_register_custom_op 调用。
  2. 重命名 outplace_fused_experts 为 fused_experts_op:在 fused_moe.py 中将原 outplace 函数更名为 fused_experts_op,其 fake 实现更名为 fused_experts_op_fake,并更新 dispatch_fused_experts_func 选择器为直接调用 torch.ops.vllm.fused_experts
  3. 移除所有调用站点的 inplace 参数:从 fused_expertsfused_experts_implfused_marlin_moebatched_fused_marlin_moeFusedMoEKernelFusedMoEKernelModularImpl 等函数/方法的签名中删除 inplace 形参,并移除对应的 if self.inplace: 分支逻辑。
  4. 删除 disable_inplace 配置:移除 vllm/model_executor/layers/fused_moe/utils.py 中的 disable_inplace() 函数,移除 vllm/model_executor/layers/fused_moe/config.pyFusedMoEConfig.disable_inplace 字段,并清理所有引用该配置的导入和条件语句。
  5. 更新所有调用者和测试文件:涵盖所有 oracle 后端(fp8, mxfp4, nvfp4 等)、量化集成(quark, bitsandbytes, compressed-tensors)、模型特定代码(arctic, minicpm)以及 15 个 MoE 测试文件。
文件 模块 状态 重要度
vllm/model_executor/layers/fused_moe/fused_moe.py MoE 内核 modified 8.74
vllm/model_executor/layers/fused_moe/modular_kernel.py MoE 内核 modified 7.03
vllm/model_executor/layers/fused_moe/utils.py 工具函数 modified 6.49
vllm/model_executor/layers/fused_moe/config.py 配置层 modified 5.6
vllm/model_executor/layers/fused_moe/experts/marlin_moe.py MarLin 专家 modified 6.23
vllm/model_executor/layers/fused_moe/layer.py MoE 层 modified 5.51

关键符号

inplace_fused_experts inplace_fused_experts_fake outplace_fused_experts fused_experts_op disable_inplace FusedMoEKernelModularImpl.__init__ FusedMoEKernelModularImpl.apply FusedMoEKernel.inplace fused_marlin_moe batched_fused_marlin_moe fused_experts

关键源码片段

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

核心文件:删除了 inplace_fused_experts 及其 fake 实现,重命名 outplace_fused_experts 为 fused_experts_op,移除 inplace 参数传递。

# fused_experts_op —— 替代原 inplace_fused_experts 和 outplace_fused_experts
# 移除了 inplace 参数,所有调用统一走 outplace 路径
def fused_experts_op(
    hidden_states: torch.Tensor,
    w1: torch.Tensor,
    w2: torch.Tensor,
    topk_weights: torch.Tensor,
    topk_ids: torch.Tensor,
    activation: str = "silu",
    apply_router_weight_on_input: bool = False,
    use_fp8_w8a8: bool = False,
    use_int8_w8a8: bool = False,
    use_int8_w8a16: bool = False,
    use_int4_w4a16: bool = False,
    ocp_mx_scheme: str | None = None,
    per_channel_quant: bool = False,
    global_num_experts: int = -1,
    expert_map: torch.Tensor | None = None,
    w1_scale: torch.Tensor | None = None,
    w2_scale: torch.Tensor | None = None,
    w1_zp: torch.Tensor | None = None,
    w2_zp: torch.Tensor | None = None,
    a1_scale: torch.Tensor | None = None,
    a2_scale: torch.Tensor | None = None,
    block_shape: list[int] | None = None,
    w1_bias: torch.Tensor | None = None,
    w2_bias: torch.Tensor | None = None,
) -> torch.Tensor:
    # 原 inplace=True 分支已删除,inplace 参数不复存在
    # 直接调用 fused_experts_impl,内部 inplace 参数固定为 False
    return fused_experts_impl(
        hidden_states,
        w1,
        w2,
        topk_weights,
        topk_ids,
        activation,
        apply_router_weight_on_input,
        use_fp8_w8a8,
        use_int8_w8a8,
        use_int8_w8a16,
        use_int4_w4a16,
        ocp_mx_scheme,
        per_channel_quant,
        global_num_experts,
        expert_map,
        w1_scale,
        w2_scale,
        w1_zp,
        w2_zp,
        a1_scale,
        a2_scale,
        block_shape,
        w1_bias,
        w2_bias,
    )
vllm/model_executor/layers/fused_moe/modular_kernel.py core-logic

删除 FusedMoEKernelModularImpl 中的 inplace 属性和相关分支,简化 apply 逻辑。

@final
class FusedMoEKernelModularImpl:
    def __init__(
        self,
        prepare_finalize: FusedMoEPrepareAndFinalizeModular,
        fused_experts: FusedMoEExpertsModular,
        # inplace: bool = False, # 已删除:不再支持 inplace
    ):
        self.prepare_finalize = prepare_finalize
        self.fused_experts = fused_experts
        # self.inplace = inplace # 已删除
        moe_parallel_config = fused_experts.moe_config.moe_parallel_config
        self.moe_parallel_config = moe_parallel_config
        self.is_dp_ep = (
            moe_parallel_config is not None
            and moe_parallel_config.dp_size > 1
            and moe_parallel_config.use_ep
        )
​
    # 在 apply 方法中,原先 if self.inplace: output = hidden_states 分支被移除,
    # 直接统一 output = torch.empty_like(hidden_states)
    def apply(self, ...) -> torch.Tensor:
        # if self.inplace: # 已删除
        # assert shared_experts is None
        # assert not disable_inplace()
        # output = hidden_states
        # else:
        output = torch.empty_like(hidden_states)
        # ... 后续统一处理

评论区精华

删除剩余 TODO 设计

bnellnm 指出 fused_moe.py 中的 TODO 注释已不再指向有效任务,建议一并删除。

结论:提交者 zyongye 移除该 TODO。 · 已解决

重命名 outplace_fused_experts 为 fused_experts 设计

bnellnm 建议将 `torch.ops.vllm.outplace_fused_experts` 重命名为 `torch.ops.vllm.fused_experts`,因为 inplace 变体已删除。

结论:提交者 zyongye 采纳并执行重命名,最终 op 名为 `fused_experts`。 · 已解决

破坏 benchmarks/kernels/benchmark_moe.py 正确性

PR 合并后,fxmarty-amd 报告 `benchmarks/kernels/benchmark_moe.py` 因 `disable_inplace` 被删除而抛出 NameError,该文件直接调用了该函数。

结论:该问题在 #44041 中修复,通过适配 benchmark 代码。 · 已解决

风险与影响

主要风险来自 disable_inplace() 函数的删除:任何直接调用该函数的外部代码(如 benchmarks/kernels/benchmark_moe.py)都会引入 NameError,该问题已在 #44041 修复。此外,若外部项目直接通过 inplace 参数调用 fused_experts 等公开 API,也会因签名变更而报错,但 vLLM 内部所有调用点已全部更新。鉴于测试套件通过且改动均为机械性删除,回归风险较低。

对最终用户无功能或性能影响(inplace 路径从未生效)。对开发者而言,MoE 内核的调用接口简化,减少了因 inplace 参数带来的认知负担和维护成本。系统层面,减少了注册的 custom op 数量,降低因 Torch 升级导致的潜在兼容性问题。

向后兼容风险 核心路径变更 大量文件修改 工具链影响

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论