[Perf] Integrate TRTLLM BF16 MoE Modular Kernel (#45182)

Signed-off-by: Kaihang Jiang <kaihangj@login-lyris02.lyris.clusters.nvidia.com>

Kaihang Jiang committed Jul 8, 2026 at 17:36 UTC 089e41287824763fc2024c715f2e2c5b4276f8e3
5 files changed +485 -55
tests/kernels/moe/test_trtllm_bf16_moe.py new
+145
@@ -0,0 +1,145 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""
4 +Tests for the FlashInfer TRTLLM BF16 MoE backend
5 +(`TrtLlmBf16ExpertsModular`).
6 +
7 +This mirrors the TRTLLM NvFP4 modular test shape: construct the modular
8 +expert wrapper directly, pass production-format BlockMajorK weights, and
9 +compare against a torch MoE reference using the original BF16 weights.
10 +"""
11 +
12 +import pytest
13 +import torch
14 +
15 +import vllm.model_executor.layers.fused_moe.modular_kernel as mk
16 +from tests.kernels.utils import torch_moe
17 +from vllm.config import ParallelConfig, VllmConfig, set_current_vllm_config
18 +from vllm.model_executor.layers.fused_moe.activation import MoEActivation
19 +from vllm.model_executor.layers.fused_moe.all2all_utils import (
20 + maybe_make_prepare_finalize,
21 +)
22 +from vllm.model_executor.layers.fused_moe.config import (
23 + FUSED_MOE_UNQUANTIZED_CONFIG,
24 + FusedMoEConfig,
25 + FusedMoEParallelConfig,
26 + RoutingMethodType,
27 +)
28 +from vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe import (
29 + TrtLlmBf16ExpertsModular,
30 +)
31 +from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
32 + convert_moe_weights_to_flashinfer_trtllm_block_layout,
33 +)
34 +from vllm.platforms import current_platform
35 +from vllm.utils.flashinfer import has_flashinfer_trtllm_fused_moe
36 +from vllm.utils.math_utils import next_power_of_2
37 +from vllm.utils.torch_utils import set_random_seed
38 +
39 +if pytest and (
40 + not has_flashinfer_trtllm_fused_moe()
41 + or not current_platform.has_device_capability(100)
42 +):
43 + pytest.skip(
44 + "Requires flashinfer TRTLLM fused MoE BF16 backend (SM100)",
45 + allow_module_level=True,
46 + )
47 +
48 +# (m, n, k) = (tokens, intermediate_size_per_partition, hidden_dim).
49 +# Covers larger NvFP4-like shapes while keeping BF16's FlashInfer TRTLLM
50 +# intermediate-size multiple-of-128 requirement.
51 +MNK_FACTORS = [
52 + (2, 1024, 1024),
53 + (64, 2048, 1536),
54 + (64, 1024, 4096),
55 +]
56 +
57 +
58 +@pytest.mark.parametrize("m,n,k", MNK_FACTORS)
59 +@pytest.mark.parametrize("e", [128])
60 +@pytest.mark.parametrize("topk", [8])
61 +@pytest.mark.parametrize("dtype", [torch.bfloat16])
62 +@torch.inference_mode()
63 +def test_trtllm_bf16_moe_modular_no_graph(
64 + m: int,
65 + n: int,
66 + k: int,
67 + e: int,
68 + topk: int,
69 + dtype: torch.dtype,
70 + workspace_init,
71 +):
72 + set_random_seed(7)
73 + with set_current_vllm_config(
74 + VllmConfig(parallel_config=ParallelConfig(pipeline_parallel_size=1))
75 + ):
76 + a = torch.randn((m, k), device="cuda", dtype=dtype) / 10
77 + w1 = torch.randn((e, 2 * n, k), device="cuda", dtype=dtype) / 10
78 + w2 = torch.randn((e, k, n), device="cuda", dtype=dtype) / 10
79 + score = torch.randn((m, e), device="cuda", dtype=dtype)
80 + scores = torch.softmax(score, dim=-1, dtype=torch.float32)
81 + topk_weights, topk_ids = torch.topk(scores, topk)
82 + topk_weights = topk_weights.contiguous()
83 + topk_ids = topk_ids.to(torch.int32).contiguous()
84 +
85 + moe_config = FusedMoEConfig(
86 + num_experts=e,
87 + experts_per_token=topk,
88 + hidden_dim=k,
89 + intermediate_size=n,
90 + num_local_experts=e,
91 + num_logical_experts=e,
92 + activation=MoEActivation.SILU,
93 + device="cuda",
94 + moe_parallel_config=FusedMoEParallelConfig.make_no_parallel(),
95 + in_dtype=dtype,
96 + routing_method=RoutingMethodType.TopK,
97 + max_num_tokens=next_power_of_2(m),
98 + )
99 +
100 + trtllm_w1, trtllm_w2 = convert_moe_weights_to_flashinfer_trtllm_block_layout(
101 + {},
102 + w1,
103 + w2,
104 + )
105 +
106 + trtllm_experts = mk.FusedMoEKernel(
107 + maybe_make_prepare_finalize(
108 + moe=moe_config,
109 + quant_config=FUSED_MOE_UNQUANTIZED_CONFIG,
110 + allow_new_interface=True,
111 + use_monolithic=False,
112 + ),
113 + TrtLlmBf16ExpertsModular(
114 + moe_config=moe_config,
115 + quant_config=FUSED_MOE_UNQUANTIZED_CONFIG,
116 + ),
117 + )
118 +
119 + trtllm_output = trtllm_experts.apply(
120 + hidden_states=a,
121 + w1=trtllm_w1,
122 + w2=trtllm_w2,
123 + topk_weights=topk_weights,
124 + topk_ids=topk_ids,
125 + activation=MoEActivation.SILU,
126 + global_num_experts=e,
127 + expert_map=None,
128 + apply_router_weight_on_input=False,
129 + )
130 +
131 + torch_output = torch_moe(
132 + a,
133 + w1,
134 + w2,
135 + score,
136 + topk,
137 + activation=MoEActivation.SILU,
138 + )
139 +
140 + torch.testing.assert_close(
141 + torch_output,
142 + trtllm_output,
143 + atol=1e-1,
144 + rtol=2e-1,
145 + )
tests/kernels/moe/test_unquantized_backend_selection.py
+169 -5
@@ -5,6 +5,7 @@ from unittest.mock import patch
5 import pytest
6
7 from tests.kernels.moe.utils import make_dummy_moe_config
8 +from vllm.model_executor.layers.fused_moe.config import RoutingMethodType
9 from vllm.model_executor.layers.fused_moe.oracle.unquantized import (
10 UnquantizedMoeBackend,
11 select_unquantized_moe_backend,
@@ -117,13 +118,15 @@ def test_select_rocm_aiter_backend(mock_aiter_enabled, mock_has_flashinfer):
118
119
120 @patch(
120 - "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16Experts.is_supported_config",
121 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
122 return_value=(True, None),
123 )
124 @pytest.mark.skipif(
125 not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
126 )
126 -def test_select_cuda_flashinfer_trtllm_backend(mock_is_supported_trtllm):
127 +def test_select_cuda_flashinfer_trtllm_backend(
128 + mock_is_supported_trtllm_monolithic,
129 +):
130 """Test CUDA backend selection when FlashInfer TRTLLM is available and enabled."""
131 with (
132 patch.object(current_platform, "is_cuda", return_value=True),
@@ -146,6 +149,162 @@ def test_select_cuda_flashinfer_trtllm_backend(mock_is_supported_trtllm):
149
150 assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
151 assert experts_cls is not None
152 + assert experts_cls.__name__ == "TrtLlmBf16ExpertsMonolithic"
153 +
154 +
155 +@patch(
156 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
157 + return_value=(False, "monolithic unsupported"),
158 +)
159 +@patch(
160 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsModular.is_supported_config",
161 + return_value=(True, None),
162 +)
163 +@pytest.mark.skipif(
164 + not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
165 +)
166 +def test_select_cuda_flashinfer_trtllm_modular_backend(
167 + mock_is_supported_trtllm_modular,
168 + mock_is_supported_trtllm_monolithic,
169 +):
170 + """Test CUDA backend selection falls back to FlashInfer TRTLLM modular."""
171 + with (
172 + patch.object(current_platform, "is_cuda", return_value=True),
173 + patch.object(current_platform, "is_rocm", return_value=False),
174 + patch.object(current_platform, "is_cpu", return_value=False),
175 + patch.object(current_platform, "is_xpu", return_value=False),
176 + patch.object(current_platform, "is_tpu", return_value=False),
177 + patch.object(current_platform, "is_out_of_tree", return_value=False),
178 + patch.object(current_platform, "has_device_capability", return_value=True),
179 + ):
180 + moe_config = make_dummy_moe_config()
181 + moe_config.moe_backend = "flashinfer_trtllm"
182 + moe_config.moe_parallel_config.use_ep = True
183 + moe_config.moe_parallel_config.use_dp = False
184 +
185 + selected_backend, experts_cls = select_unquantized_moe_backend(
186 + moe_config=moe_config
187 + )
188 +
189 + assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
190 + assert experts_cls is not None
191 + assert experts_cls.__name__ == "TrtLlmBf16ExpertsModular"
192 +
193 +
194 +@patch(
195 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
196 + return_value=True,
197 +)
198 +@pytest.mark.parametrize(
199 + "all2all_backend",
200 + [
201 + "mori_high_throughput",
202 + "mori_low_latency",
203 + "flashinfer_nvlink_two_sided",
204 + "flashinfer_nvlink_one_sided",
205 + ],
206 +)
207 +def test_select_cuda_flashinfer_trtllm_modular_for_standard_all2all(
208 + mock_supports_current_device,
209 + all2all_backend,
210 +):
211 + """Test non-AG/RS standard-format all2all backends select modular BF16."""
212 + with (
213 + patch.object(current_platform, "is_cuda", return_value=True),
214 + patch.object(current_platform, "is_rocm", return_value=False),
215 + patch.object(current_platform, "is_cpu", return_value=False),
216 + patch.object(current_platform, "is_xpu", return_value=False),
217 + patch.object(current_platform, "is_tpu", return_value=False),
218 + patch.object(current_platform, "is_out_of_tree", return_value=False),
219 + patch.object(
220 + current_platform, "is_device_capability_family", return_value=False
221 + ),
222 + ):
223 + moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
224 + moe_config.moe_backend = "flashinfer_trtllm"
225 + moe_config.moe_parallel_config.use_ep = True
226 + moe_config.moe_parallel_config.dp_size = 2
227 + moe_config.moe_parallel_config.ep_size = 2
228 + moe_config.moe_parallel_config.all2all_backend = all2all_backend
229 +
230 + selected_backend, experts_cls = select_unquantized_moe_backend(
231 + moe_config=moe_config
232 + )
233 +
234 + assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
235 + assert experts_cls is not None
236 + assert experts_cls.__name__ == "TrtLlmBf16ExpertsModular"
237 +
238 +
239 +@patch(
240 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
241 + return_value=True,
242 +)
243 +def test_select_cuda_deepep_ht_falls_back_from_trtllm(
244 + mock_supports_current_device,
245 +):
246 + """Test DeepEP HT avoids the unsupported BF16 TRTLLM modular path."""
247 + with (
248 + patch.object(current_platform, "is_cuda", return_value=True),
249 + patch.object(current_platform, "is_rocm", return_value=False),
250 + patch.object(current_platform, "is_cpu", return_value=False),
251 + patch.object(current_platform, "is_xpu", return_value=False),
252 + patch.object(current_platform, "is_tpu", return_value=False),
253 + patch.object(current_platform, "is_out_of_tree", return_value=False),
254 + patch.object(
255 + current_platform, "is_device_capability_family", return_value=False
256 + ),
257 + ):
258 + moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
259 + moe_config.moe_backend = "auto"
260 + moe_config.moe_parallel_config.use_ep = True
261 + moe_config.moe_parallel_config.dp_size = 2
262 + moe_config.moe_parallel_config.ep_size = 2
263 + moe_config.moe_parallel_config.all2all_backend = "deepep_high_throughput"
264 +
265 + selected_backend, experts_cls = select_unquantized_moe_backend(
266 + moe_config=moe_config
267 + )
268 +
269 + assert selected_backend == UnquantizedMoeBackend.TRITON
270 + assert experts_cls is not None
271 + assert experts_cls.__name__ == "TritonExperts"
272 +
273 +
274 +@patch(
275 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsBase._supports_current_device",
276 + return_value=True,
277 +)
278 +def test_select_cuda_flashinfer_trtllm_ag_rs_uses_monolithic(
279 + mock_supports_current_device,
280 +):
281 + """Test AG/RS stays on BF16 TRTLLM monolithic when TRTLLM is supported."""
282 + with (
283 + patch.object(current_platform, "is_cuda", return_value=True),
284 + patch.object(current_platform, "is_rocm", return_value=False),
285 + patch.object(current_platform, "is_cpu", return_value=False),
286 + patch.object(current_platform, "is_xpu", return_value=False),
287 + patch.object(current_platform, "is_tpu", return_value=False),
288 + patch.object(current_platform, "is_out_of_tree", return_value=False),
289 + patch.object(
290 + current_platform, "is_device_capability_family", return_value=False
291 + ),
292 + ):
293 + moe_config = make_dummy_moe_config(num_experts=4, num_local_experts=2)
294 + moe_config.moe_backend = "flashinfer_trtllm"
295 + moe_config.routing_method = RoutingMethodType.Renormalize
296 + moe_config.moe_parallel_config.use_ep = True
297 + moe_config.moe_parallel_config.dp_size = 2
298 + moe_config.moe_parallel_config.ep_size = 2
299 + moe_config.moe_parallel_config.all2all_backend = "allgather_reducescatter"
300 +
301 + selected_backend, experts_cls = select_unquantized_moe_backend(
302 + moe_config=moe_config
303 + )
304 +
305 + assert selected_backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM
306 + assert experts_cls is not None
307 + assert experts_cls.__name__ == "TrtLlmBf16ExpertsMonolithic"
308
309
310 @patch(
@@ -153,7 +312,11 @@ def test_select_cuda_flashinfer_trtllm_backend(mock_is_supported_trtllm):
312 return_value=True,
313 )
314 @patch(
156 - "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16Experts.is_supported_config",
315 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config",
316 + return_value=(False, None),
317 +)
318 +@patch(
319 + "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsModular.is_supported_config",
320 return_value=(False, None),
321 )
322 @patch(
@@ -164,9 +327,10 @@ def test_select_cuda_flashinfer_trtllm_backend(mock_is_supported_trtllm):
327 not current_platform.is_cuda(), reason="Only supported on NVIDIA platforms."
328 )
329 def test_select_cuda_flashinfer_cutlass_backend(
167 - mock_has_flashinfer,
168 - mock_is_supported_trtllm,
330 mock_is_supported_cutlass,
331 + mock_is_supported_trtllm_modular,
332 + mock_is_supported_trtllm_monolithic,
333 + mock_has_flashinfer,
334 ):
335 """Test CUDA backend selection when FlashInfer TRTLLM is not available
336 and FlashInfer CUTLASS is available."""
vllm/model_executor/layers/fused_moe/experts/trtllm_bf16_moe.py
+139 -23
@@ -11,7 +11,13 @@ from vllm.model_executor.layers.fused_moe.config import (
11 FusedMoEQuantConfig,
12 RoutingMethodType,
13 )
14 -from vllm.model_executor.layers.fused_moe.utils import fi_moe_largest_bucket
14 +from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
15 + TopKWeightAndReduceNoOP,
16 +)
17 +from vllm.model_executor.layers.fused_moe.utils import (
18 + fi_moe_largest_bucket,
19 + trtllm_moe_pack_topk_ids_weights,
20 +)
21 from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
22 activation_to_flashinfer_int,
23 )
@@ -22,9 +28,10 @@ from vllm.platforms import current_platform
28 from vllm.utils.flashinfer import has_flashinfer_trtllm_fused_moe
29
30
25 -class TrtLlmBf16Experts(mk.FusedMoEExpertsMonolithic):
31 +class TrtLlmBf16ExpertsBase:
32 """
27 - BF16 unquantized TRTLLM-Gen MoE kernels. Supports monolithic interface.
33 + BF16 unquantized TRTLLM-Gen MoE kernels. Shared base for modular and
34 + monolithic interfaces.
35 """
36
37 def __init__(
@@ -32,7 +39,6 @@ class TrtLlmBf16Experts(mk.FusedMoEExpertsMonolithic):
39 moe_config: FusedMoEConfig,
40 quant_config: FusedMoEQuantConfig,
41 ):
35 - super().__init__(moe_config, quant_config)
42 self.routing_method_type = moe_config.routing_method
43 self.topk = moe_config.experts_per_token
44 self.intermediate_size_per_partition = (
@@ -42,6 +48,9 @@ class TrtLlmBf16Experts(mk.FusedMoEExpertsMonolithic):
48 self.local_num_experts = moe_config.num_local_experts
49 self.ep_rank = moe_config.moe_parallel_config.ep_rank
50
51 + self.moe_config = moe_config
52 + self.quant_config = quant_config
53 +
54 @staticmethod
55 def activation_format() -> mk.FusedMoEActivationFormat:
56 return mk.FusedMoEActivationFormat.Standard
@@ -75,40 +84,147 @@ class TrtLlmBf16Experts(mk.FusedMoEExpertsMonolithic):
84 return activation in [MoEActivation.SILU, MoEActivation.RELU2_NO_MUL]
85
86 @staticmethod
78 - def _supports_routing_method(
87 + def _supports_router_logits_dtype(
88 + router_logits_dtype: torch.dtype | None,
89 routing_method: RoutingMethodType,
80 - weight_key: QuantKey | None,
81 - activation_key: QuantKey | None,
90 ) -> bool:
83 - return routing_method in [
84 - RoutingMethodType.DeepSeekV3,
85 - RoutingMethodType.Llama4,
86 - RoutingMethodType.Renormalize,
87 - RoutingMethodType.RenormalizeNaive,
88 - RoutingMethodType.SigmoidRenorm,
89 - RoutingMethodType.Sigmoid,
90 - ]
91 + return True
92 +
93 + @property
94 + def expects_unquantized_inputs(self) -> bool:
95 + return True
96 +
97 +
98 +class TrtLlmBf16ExpertsModular(TrtLlmBf16ExpertsBase, mk.FusedMoEExpertsModular):
99 + """
100 + BF16 unquantized TRTLLM-Gen MoE kernels. Supports modular interface.
101 + """
102
103 @staticmethod
104 def _supports_parallel_config(
105 moe_parallel_config: FusedMoEParallelConfig,
106 ) -> bool:
96 - """Monolithic kernel so only use with naive DP/EP and TP."""
107 + return (
108 + moe_parallel_config.use_all2all_kernels
109 + and not moe_parallel_config.use_ag_rs_all2all_kernels
110 + and not moe_parallel_config.use_deepep_ht_kernels
111 + and not moe_parallel_config.enable_eplb
112 + )
113 +
114 + def moe_problem_size(
115 + self,
116 + a1: torch.Tensor,
117 + w1: torch.Tensor,
118 + w2: torch.Tensor,
119 + topk_ids: torch.Tensor,
120 + ) -> tuple[int, int, int, int, int]:
121 + """Override to handle 4D BlockMajorK weights (E, K/bk, Mn, bk)."""
122 + if w1.dim() == 4:
123 + E = w1.shape[0]
124 + N = w1.shape[2]
125 + K = a1.size(-1)
126 + M = a1.size(0) if a1.dim() == 2 else a1.size(1)
127 + topk = topk_ids.size(1)
128 + return E, M, N, K, topk
129 + return super().moe_problem_size(a1, w1, w2, topk_ids)
130 +
131 + def workspace_shapes(
132 + self,
133 + M: int,
134 + N: int,
135 + K: int,
136 + topk: int,
137 + global_num_experts: int,
138 + local_num_experts: int,
139 + expert_tokens_meta: mk.ExpertTokensMetadata | None,
140 + activation: MoEActivation,
141 + ) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...]]:
142 + # The workspaces for this implementation are managed by FlashInfer.
143 + workspace1 = (0,)
144 + workspace2 = (0,)
145 + output = (M, K)
146 +
147 + return workspace1, workspace2, output
148 +
149 + def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
150 + return TopKWeightAndReduceNoOP()
151 +
152 + def apply(
153 + self,
154 + output: torch.Tensor,
155 + hidden_states: torch.Tensor,
156 + w1: torch.Tensor,
157 + w2: torch.Tensor,
158 + topk_weights: torch.Tensor,
159 + topk_ids: torch.Tensor,
160 + activation: MoEActivation,
161 + global_num_experts: int,
162 + expert_map: torch.Tensor | None,
163 + a1q_scale: torch.Tensor | None,
164 + a2_scale: torch.Tensor | None,
165 + workspace13: torch.Tensor,
166 + workspace2: torch.Tensor,
167 + expert_tokens_meta: mk.ExpertTokensMetadata | None,
168 + apply_router_weight_on_input: bool,
169 + ):
170 + import flashinfer
171 + from flashinfer.fused_moe import WeightLayout
172 +
173 + # Pack topk ids and weights into format expected by the TRTLLM kernel.
174 + packed_topk_ids = trtllm_moe_pack_topk_ids_weights(topk_ids, topk_weights)
175 +
176 + result = flashinfer.fused_moe.trtllm_bf16_routed_moe(
177 + topk_ids=packed_topk_ids,
178 + hidden_states=hidden_states,
179 + gemm1_weights=w1,
180 + gemm2_weights=w2,
181 + num_experts=global_num_experts,
182 + top_k=self.topk,
183 + n_group=None,
184 + topk_group=None,
185 + intermediate_size=self.intermediate_size_per_partition,
186 + local_expert_offset=self.ep_rank * self.local_num_experts,
187 + local_num_experts=self.local_num_experts,
188 + routed_scaling_factor=None,
189 + routing_method_type=1, # not used
190 + use_shuffled_weight=True,
191 + weight_layout=WeightLayout.BlockMajorK,
192 + do_finalize=True,
193 + activation_type=activation_to_flashinfer_int(activation),
194 + )
195 + # FlashInfer's BF16 routed wrapper does not expose an output= argument.
196 + output.copy_(result[0] if isinstance(result, list) else result)
197 +
198 +
199 +class TrtLlmBf16ExpertsMonolithic(TrtLlmBf16ExpertsBase, mk.FusedMoEExpertsMonolithic):
200 + """
201 + BF16 unquantized TRTLLM-Gen MoE kernels. Supports monolithic interface.
202 + """
203 +
204 + @staticmethod
205 + def _supports_parallel_config(
206 + moe_parallel_config: FusedMoEParallelConfig,
207 + ) -> bool:
208 + """Monolithic kernel supports no-all2all and AG/RS paths."""
209 return (
210 not moe_parallel_config.use_all2all_kernels
211 or moe_parallel_config.use_ag_rs_all2all_kernels
212 ) and not moe_parallel_config.enable_eplb
213
214 @staticmethod
103 - def _supports_router_logits_dtype(
104 - router_logits_dtype: torch.dtype | None,
215 + def _supports_routing_method(
216 routing_method: RoutingMethodType,
217 + weight_key: QuantKey | None,
218 + activation_key: QuantKey | None,
219 ) -> bool:
107 - return True
108 -
109 - @property
110 - def expects_unquantized_inputs(self) -> bool:
111 - return True
220 + return routing_method in [
221 + RoutingMethodType.DeepSeekV3,
222 + RoutingMethodType.Llama4,
223 + RoutingMethodType.Renormalize,
224 + RoutingMethodType.RenormalizeNaive,
225 + RoutingMethodType.SigmoidRenorm,
226 + RoutingMethodType.Sigmoid,
227 + ]
228
229 def apply(
230 self,
vllm/model_executor/layers/fused_moe/oracle/base.py
+4 -2
@@ -63,9 +63,11 @@ class MoEKernelOracle(ABC, Generic[BackendT]):
63 this `moe_config`."""
64
65 @abstractmethod
66 - def backend_to_kernel_cls(self, backend: BackendT) -> type[mk.FusedMoEExperts]:
66 + def backend_to_kernel_cls(
67 + self, backend: BackendT
68 + ) -> list[type[mk.FusedMoEExperts]]:
69 """Map a backend enum value to its concrete `FusedMoEExperts`
68 - subclass."""
70 + subclasses, in selection priority order."""
71
72 @abstractmethod
73 def map_backend(self, runner_backend: MoEBackend) -> BackendT:
vllm/model_executor/layers/fused_moe/oracle/unquantized.py
+28 -25
@@ -101,46 +101,47 @@ def _get_priority_backends(moe_config: FusedMoEConfig) -> list[UnquantizedMoeBac
101
102 def backend_to_kernel_cls(
103 backend: UnquantizedMoeBackend,
104 -) -> type[mk.FusedMoEExperts]:
104 +) -> list[type[mk.FusedMoEExperts]]:
105 if backend == UnquantizedMoeBackend.FLASHINFER_TRTLLM:
106 from vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe import (
107 - TrtLlmBf16Experts,
107 + TrtLlmBf16ExpertsModular,
108 + TrtLlmBf16ExpertsMonolithic,
109 )
110
110 - return TrtLlmBf16Experts
111 + return [TrtLlmBf16ExpertsMonolithic, TrtLlmBf16ExpertsModular]
112
113 elif backend == UnquantizedMoeBackend.FLASHINFER_CUTLASS:
114 from vllm.model_executor.layers.fused_moe.experts.flashinfer_cutlass_moe import ( # noqa: E501
115 FlashInferExperts,
116 )
117
117 - return FlashInferExperts
118 + return [FlashInferExperts]
119
120 elif backend == UnquantizedMoeBackend.AITER:
121 from vllm.model_executor.layers.fused_moe.experts.rocm_aiter_moe import (
122 AiterExperts,
123 )
124
124 - return AiterExperts
125 + return [AiterExperts]
126
127 elif backend == UnquantizedMoeBackend.TRITON:
128 from vllm.model_executor.layers.fused_moe.experts.triton_moe import (
129 TritonExperts,
130 )
131
131 - return TritonExperts
132 + return [TritonExperts]
133
134 elif backend == UnquantizedMoeBackend.BATCHED_TRITON:
135 from vllm.model_executor.layers.fused_moe.experts.fused_batched_moe import (
136 BatchedTritonExperts,
137 )
138
138 - return BatchedTritonExperts
139 + return [BatchedTritonExperts]
140
141 elif backend == UnquantizedMoeBackend.XPU:
142 from vllm.model_executor.layers.fused_moe.experts.xpu_moe import XPUExperts
143
143 - return XPUExperts
144 + return [XPUExperts]
145
146 else:
147 raise ValueError(f"Unknown unquantized MoE backend: {backend.value}")
@@ -183,7 +184,7 @@ def select_unquantized_moe_backend(
184 if moe_config.is_lora_enabled:
185 return UnquantizedMoeBackend.TRITON, backend_to_kernel_cls(
186 UnquantizedMoeBackend.TRITON
186 - )
187 + )[0]
188
189 # NOTE: the kernels are selected in the following order.
190 AVAILABLE_BACKENDS = _get_priority_backends(moe_config)
@@ -222,13 +223,14 @@ def select_unquantized_moe_backend(
223 config: FusedMoEConfig,
224 activation_format: mk.FusedMoEActivationFormat,
225 ) -> tuple[UnquantizedMoeBackend, type[mk.FusedMoEExperts] | None]:
225 - k_cls = backend_to_kernel_cls(backend)
226 - supported, reason = k_cls.is_supported_config(
227 - k_cls, config, None, None, activation_format
228 - )
229 - if supported:
230 - logger.info_once(_make_log_backend(backend))
231 - return backend, k_cls
226 + reason = None
227 + for k_cls in backend_to_kernel_cls(backend):
228 + supported, reason = k_cls.is_supported_config(
229 + k_cls, config, None, None, activation_format
230 + )
231 + if supported:
232 + logger.info_once(_make_log_backend(backend))
233 + return backend, k_cls
234 raise ValueError(_make_log_unsupported(backend, reason))
235
236 runner_backend = moe_config.moe_backend
@@ -254,15 +256,15 @@ def select_unquantized_moe_backend(
256 return _return_or_raise(backend, moe_config, activation_format)
257
258 for backend in AVAILABLE_BACKENDS:
257 - k_cls = backend_to_kernel_cls(backend)
258 - supported, reason = k_cls.is_supported_config(
259 - k_cls, moe_config, None, None, activation_format
260 - )
261 - if supported:
262 - logger.info_once(_make_log_backend(backend))
263 - return backend, k_cls
259 + for k_cls in backend_to_kernel_cls(backend):
260 + supported, reason = k_cls.is_supported_config(
261 + k_cls, moe_config, None, None, activation_format
262 + )
263 + if supported:
264 + logger.info_once(_make_log_backend(backend))
265 + return backend, k_cls
266
265 - logger.debug_once(_make_log_unsupported(backend, reason))
267 + logger.debug_once(_make_log_unsupported(backend, reason))
268
269 raise NotImplementedError(
270 "No Unquantized MoE backend supports the deployment configuration."
@@ -332,6 +334,7 @@ def make_unquantized_moe_kernel(
334 assert prepare_finalize is not None
335
336 logger.info_once("Using %s", prepare_finalize.__class__.__name__)
337 + logger.info_once("Using %s MoE backend", experts_cls.__name__)
338
339 # Create Experts
340 if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
@@ -383,7 +386,7 @@ class UnquantizedMoEKernelOracle(MoEKernelOracle[UnquantizedMoeBackend]):
386
387 def backend_to_kernel_cls(
388 self, backend: UnquantizedMoeBackend
386 - ) -> type[mk.FusedMoEExperts]:
389 + ) -> list[type[mk.FusedMoEExperts]]:
390 return backend_to_kernel_cls(backend)
391
392 def map_backend(self, runner_backend: MoEBackend) -> UnquantizedMoeBackend: