From 5097f9ac95b0916b7070e3d2c98225badbd964fb Mon Sep 17 00:00:00 2001 From: "Po-Han Huang (NVIDIA)" <53919306+nvpohanh@users.noreply.github.com> Date: Tue, 8 Sep 2026 21:16:36 +0800 Subject: [PATCH] Disable Hopper GLM shared-expert fusion for modelopt_fp4 Marlin (#37325) --- python/sglang/srt/models/deepseek_v2.py | 12 +++++ .../test_shared_experts_fusion_gates.py | 49 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/python/sglang/srt/models/deepseek_v2.py b/python/sglang/srt/models/deepseek_v2.py index 3d9438c91..c72bd3d0a 100644 --- a/python/sglang/srt/models/deepseek_v2.py +++ b/python/sglang/srt/models/deepseek_v2.py @@ -200,6 +200,7 @@ from sglang.srt.utils import ( LazyValue, add_prefix, is_non_idle_and_non_empty, + is_sm90_supported, make_layers, use_intel_amx_backend, ) @@ -2973,6 +2974,17 @@ class DeepseekV2ForCausalLM(nn.Module, DeepseekV2WeightLoaderMixin): ) if get_exec().moe.enforce_shared_experts_fusion: return None + if ( + quant_config is not None + and quant_config.get_name() == "modelopt_fp4" + and is_sm90_supported() + and get_moe_runner_backend().is_marlin() + ): + return ( + "Hopper modelopt_fp4 with moe_runner_backend=marlin: " + "fusion off by default until the shared-expert fused load path " + "is validated." + ) if is_sbo_enabled() or is_tbo_enabled(): return "SBO/TBO enabled: incompatible with fusing shared expert into MoE kernel." if is_deepep_class_backend(): diff --git a/test/registered/unit/models/test_shared_experts_fusion_gates.py b/test/registered/unit/models/test_shared_experts_fusion_gates.py index e94dc1bf5..ede82ad1d 100644 --- a/test/registered/unit/models/test_shared_experts_fusion_gates.py +++ b/test/registered/unit/models/test_shared_experts_fusion_gates.py @@ -159,6 +159,55 @@ class TestDeepseekV2Gate(_FusionGateCase): ) self.assertIsNone(self._reason(DeepseekV2ForCausalLM, self._config(), matched)) + def test_hopper_modelopt_fp4_marlin_disables_fusion_by_default(self): + import sglang.srt.models.deepseek_v2 as deepseek_v2 + from sglang.srt.layers.moe.utils import MoeRunnerBackend + from sglang.srt.models.deepseek_v2 import DeepseekV2ForCausalLM + + self._seed() + with ( + unittest.mock.patch.object( + deepseek_v2, "is_sm90_supported", return_value=True + ), + unittest.mock.patch.object( + deepseek_v2, + "get_moe_runner_backend", + return_value=MoeRunnerBackend.MARLIN, + ), + ): + self.assertIn( + "fusion off by default", + self._reason( + DeepseekV2ForCausalLM, + self._config(), + _quant("modelopt_fp4"), + ), + ) + + def test_hopper_modelopt_fp4_marlin_can_still_be_forced(self): + import sglang.srt.models.deepseek_v2 as deepseek_v2 + from sglang.srt.layers.moe.utils import MoeRunnerBackend + from sglang.srt.models.deepseek_v2 import DeepseekV2ForCausalLM + + self._seed(enforce_shared_experts_fusion=True) + with ( + unittest.mock.patch.object( + deepseek_v2, "is_sm90_supported", return_value=True + ), + unittest.mock.patch.object( + deepseek_v2, + "get_moe_runner_backend", + return_value=MoeRunnerBackend.MARLIN, + ), + ): + self.assertIsNone( + self._reason( + DeepseekV2ForCausalLM, + self._config(), + _quant("modelopt_fp4"), + ) + ) + class TestGlmMoeLiteGate(_FusionGateCase): def _config(self, **kw):