A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 9, 14:45 UTC
[MoE Refactor] Integrate Naive Prepare Finalize into MK (#32567)
Signed-off-by: Robert Shaw <robshaw@redhat.com> Signed-off-by: Amir Klein <203507526+amirkl94@users.noreply.github.com> Co-authored-by: Robert Shaw <robshaw@redhat.com> Co-authored-by: amirkl94 <203507526+amirkl94@users.noreply.github.com>
Robert Shaw committed
Jan 26, 2026 at 20:28 UTC
5a93b9162bfef6be6714ba70d3191e1e7ab7d3e4
46 files changed
+1017
-875
.buildkite/test-amd.yaml
+1
-1
@@ -1131,7 +1131,7 @@ steps:
1131
- csrc/quantization/cutlass_w8a8/moe/
1132
- vllm/model_executor/layers/fused_moe/cutlass_moe.py
1133
- vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
1134
- - vllm/model_executor/layers/fused_moe/flashinfer_cutlass_prepare_finalize.py
1134
+ - vllm/model_executor/layers/fused_moe/flashinfer_a2a_prepare_finalize.py
1135
- vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
1136
- vllm/v1/attention/backends/flashinfer.py
1137
- vllm/v1/attention/backends/mla/cutlass_mla.py
.buildkite/test-pipeline.yaml
+1
-1
@@ -1017,7 +1017,7 @@ steps:
1017
- csrc/quantization/cutlass_w8a8/moe/
1018
- vllm/model_executor/layers/fused_moe/cutlass_moe.py
1019
- vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
1020
- - vllm/model_executor/layers/fused_moe/flashinfer_cutlass_prepare_finalize.py
1020
+ - vllm/model_executor/layers/fused_moe/flashinfer_a2a_prepare_finalize.py
1021
- vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
1022
- vllm/v1/attention/backends/flashinfer.py
1023
- vllm/v1/attention/backends/mla/cutlass_mla.py
.buildkite/test_areas/kernels.yaml
+1
-1
@@ -85,7 +85,7 @@ steps:
85
- csrc/quantization/cutlass_w8a8/moe/
86
- vllm/model_executor/layers/fused_moe/cutlass_moe.py
87
- vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
88
- - vllm/model_executor/layers/fused_moe/flashinfer_cutlass_prepare_finalize.py
88
+ - vllm/model_executor/layers/fused_moe/flashinfer_a2a_prepare_finalize.py
89
- vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
90
- vllm/v1/attention/backends/flashinfer.py
91
- vllm/v1/attention/backends/mla/cutlass_mla.py
benchmarks/kernels/benchmark_cutlass_moe_nvfp4.py
+2
-2
@@ -197,7 +197,7 @@ def bench_run(
197
)
198
199
kernel = mk.FusedMoEModularKernel(
200
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
200
+ MoEPrepareAndFinalizeNoEP(),
201
CutlassExpertsFp4(
202
make_dummy_moe_config(),
203
quant_config=quant_config,
@@ -242,7 +242,7 @@ def bench_run(
242
)
243
244
kernel = mk.FusedMoEModularKernel(
245
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
245
+ MoEPrepareAndFinalizeNoEP(),
246
CutlassExpertsFp4(
247
make_dummy_moe_config(),
248
quant_config=quant_config,
docs/design/moe_kernel_features.md
+1
-2
@@ -36,8 +36,7 @@ th {
36
| pplx | batched | fp8,int8 | G,A,T | Y | Y | [`PplxPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.pplx_prepare_finalize.PplxPrepareAndFinalize] |
37
| deepep_high_throughput | standard | fp8 | G(128),A,T<sup>2</sup> | Y | Y | [`DeepEPLLPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.deepep_ll_prepare_finalize.DeepEPLLPrepareAndFinalize] |
38
| deepep_low_latency | batched | fp8 | G(128),A,T<sup>3</sup> | Y | Y | [`DeepEPHTPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.deepep_ht_prepare_finalize.DeepEPHTPrepareAndFinalize] |
39
-| flashinfer_all2allv | standard | nvfp4,fp8 | G,A,T | N | N | [`FlashInferAllToAllMoEPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize.FlashInferAllToAllMoEPrepareAndFinalize] |
40
-| flashinfer<sup>4</sup> | standard | nvfp4,fp8 | G,A,T | N | N | [`FlashInferCutlassMoEPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize.FlashInferCutlassMoEPrepareAndFinalize] |
39
+| flashinfer_all2allv | standard | nvfp4,fp8 | G,A,T | N | N | [`FlashInferA2APrepareAndFinalize`][vllm.model_executor.layers.fused_moe.flashinfer_a2a_prepare_finalize.FlashInferA2APrepareAndFinalize] |
40
| MoEPrepareAndFinalizeNoEP<sup>5</sup> | standard | fp8,int8 | G,A,T | N | Y | [`MoEPrepareAndFinalizeNoEP`][vllm.model_executor.layers.fused_moe.prepare_finalize.MoEPrepareAndFinalizeNoEP] |
41
| BatchedPrepareAndFinalize<sup>5</sup> | batched | fp8,int8 | G,A,T | N | Y | [`BatchedPrepareAndFinalize`][vllm.model_executor.layers.fused_moe.fused_batched_moe.BatchedPrepareAndFinalize] |
42
tests/kernels/moe/modular_kernel_tools/common.py
+8
-4
@@ -22,6 +22,9 @@ from vllm.distributed import (
22
)
23
from vllm.forward_context import set_forward_context
24
from vllm.model_executor.layers.fused_moe import fused_topk
25
+from vllm.model_executor.layers.fused_moe.all2all_utils import (
26
+ maybe_make_prepare_finalize,
27
+)
28
from vllm.model_executor.layers.fused_moe.config import (
29
FusedMoEConfig,
30
FusedMoEParallelConfig,
@@ -40,7 +43,6 @@ from .mk_objects import (
43
TestMoEQuantConfig,
44
expert_info,
45
make_fused_experts,
43
- make_prepare_finalize,
46
prepare_finalize_info,
47
)
48
from .parallel_utils import ProcessGroupInfo
@@ -603,10 +605,12 @@ def make_modular_kernel(
605
routing_method=RoutingMethodType.DeepSeekV3,
606
)
607
606
- # make modular kernel
607
- prepare_finalize = make_prepare_finalize(
608
- config.prepare_finalize_type, config.all2all_backend(), moe, quant_config
608
+ prepare_finalize = maybe_make_prepare_finalize(
609
+ moe=moe,
610
+ quant_config=quant_config,
611
+ allow_new_interface=True,
612
)
613
+ assert prepare_finalize is not None
614
615
fused_experts = make_fused_experts(
616
config.fused_experts_type,
tests/kernels/moe/modular_kernel_tools/mk_objects.py
+3
-25
@@ -7,9 +7,6 @@ import torch
7
# Fused experts and PrepareFinalize imports
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.model_executor.layers.fused_moe import TritonExperts
10
-from vllm.model_executor.layers.fused_moe.all2all_utils import (
11
- maybe_make_prepare_finalize,
12
-)
10
from vllm.model_executor.layers.fused_moe.batched_deep_gemm_moe import (
11
BatchedDeepGemmExperts,
12
)
@@ -255,13 +252,12 @@ if has_pplx():
252
)
253
254
if has_flashinfer_cutlass_fused_moe() and current_platform.has_device_capability(100):
255
+ from vllm.model_executor.layers.fused_moe.flashinfer_a2a_prepare_finalize import ( # noqa: E501
256
+ FlashInferCutlassMoEPrepareAndFinalize,
257
+ )
258
from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_moe import (
259
FlashInferExperts,
260
)
261
- from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
262
- FlashInferCutlassMoEPrepareAndFinalize,
263
- create_flashinfer_prepare_finalize,
264
- )
261
262
register_prepare_and_finalize(
263
FlashInferCutlassMoEPrepareAndFinalize,
@@ -429,24 +425,6 @@ if cutlass_fp4_supported() or has_flashinfer_cutlass_fused_moe():
425
]
426
427
432
-def make_prepare_finalize(
433
- prepare_finalize_type: mk.FusedMoEPrepareAndFinalize,
434
- backend: str | None,
435
- moe: FusedMoEConfig,
436
- quant_config: FusedMoEQuantConfig,
437
-) -> mk.FusedMoEPrepareAndFinalize:
438
- if backend != "naive" and backend is not None:
439
- prepare_finalize = maybe_make_prepare_finalize(moe, quant_config)
440
- assert prepare_finalize is not None
441
- return prepare_finalize
442
- elif prepare_finalize_type == FlashInferCutlassMoEPrepareAndFinalize:
443
- return create_flashinfer_prepare_finalize(
444
- use_dp=moe.moe_parallel_config.dp_size > 1
445
- )
446
- else:
447
- return MoEPrepareAndFinalizeNoEP()
448
-
449
-
428
def _slice(rank: int, num_local_experts: int, t: torch.Tensor) -> torch.Tensor:
429
s = rank * num_local_experts
430
e = s + num_local_experts
tests/kernels/moe/test_flashinfer.py
+1
-6
@@ -294,12 +294,7 @@ def test_flashinfer_cutlass_moe_fp8_no_graph(
294
)
295
296
kernel = mk.FusedMoEModularKernel(
297
- MoEPrepareAndFinalizeNoEP(
298
- defer_input_quant=FlashInferExperts.expects_unquantized_inputs(
299
- moe_config=moe_config,
300
- quant_config=quant_config,
301
- )
302
- ),
297
+ MoEPrepareAndFinalizeNoEP(),
298
FlashInferExperts(
299
moe_config=moe_config,
300
quant_config=quant_config,
tests/kernels/moe/test_flashinfer_moe.py
+1
-6
@@ -106,12 +106,7 @@ def test_flashinfer_fp4_moe_no_graph(
106
)
107
108
flashinfer_experts = FusedMoEModularKernel(
109
- MoEPrepareAndFinalizeNoEP(
110
- defer_input_quant=FlashInferExperts.expects_unquantized_inputs(
111
- moe_config=moe_config,
112
- quant_config=quant_config,
113
- )
114
- ),
109
+ MoEPrepareAndFinalizeNoEP(),
110
FlashInferExperts(moe_config=moe_config, quant_config=quant_config),
111
)
112
tests/kernels/moe/test_nvfp4_moe.py
+1
-1
@@ -90,7 +90,7 @@ def test_cutlass_fp4_moe_no_graph(
90
)
91
92
kernel = mk.FusedMoEModularKernel(
93
- MoEPrepareAndFinalizeNoEP(defer_input_quant=True),
93
+ MoEPrepareAndFinalizeNoEP(),
94
CutlassExpertsFp4(
95
moe_config=make_dummy_moe_config(),
96
quant_config=quant_config,
vllm/distributed/device_communicators/all2all.py
+98
-4
@@ -59,7 +59,7 @@ class NaiveAll2AllManager(All2AllManagerBase):
59
60
return buffer
61
62
- def dispatch(
62
+ def dispatch_router_logits(
63
self,
64
hidden_states: torch.Tensor,
65
router_logits: torch.Tensor,
@@ -84,6 +84,34 @@ class NaiveAll2AllManager(All2AllManagerBase):
84
85
return hidden_states, router_logits
86
87
+ def dispatch(
88
+ self,
89
+ hidden_states: torch.Tensor,
90
+ topk_weights: torch.Tensor,
91
+ topk_ids: torch.Tensor,
92
+ is_sequence_parallel: bool = False,
93
+ extra_tensors: list[torch.Tensor] | None = None,
94
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
95
+ if extra_tensors is not None:
96
+ raise NotImplementedError(
97
+ "extra_tensors is not supported for NaiveAll2AllManager"
98
+ )
99
+ sp_size = self.tp_group.world_size if is_sequence_parallel else 1
100
+ dp_metadata = get_forward_context().dp_metadata
101
+ assert dp_metadata is not None
102
+ cu_tokens_across_sp_cpu = dp_metadata.cu_tokens_across_sp(sp_size)
103
+
104
+ hidden_states = self.naive_multicast(
105
+ hidden_states, cu_tokens_across_sp_cpu, is_sequence_parallel
106
+ )
107
+ topk_weights = self.naive_multicast(
108
+ topk_weights, cu_tokens_across_sp_cpu, is_sequence_parallel
109
+ )
110
+ topk_ids = self.naive_multicast(
111
+ topk_ids, cu_tokens_across_sp_cpu, is_sequence_parallel
112
+ )
113
+ return hidden_states, topk_weights, topk_ids
114
+
115
def combine(
116
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
117
) -> torch.Tensor:
@@ -114,7 +142,7 @@ class AgRsAll2AllManager(All2AllManagerBase):
142
def __init__(self, cpu_group):
143
super().__init__(cpu_group)
144
117
- def dispatch(
145
+ def dispatch_router_logits(
146
self,
147
hidden_states: torch.Tensor,
148
router_logits: torch.Tensor,
@@ -148,6 +176,46 @@ class AgRsAll2AllManager(All2AllManagerBase):
176
return (gathered_tensors[0], gathered_tensors[1], gathered_tensors[2:])
177
return gathered_tensors[0], gathered_tensors[1]
178
179
+ def dispatch(
180
+ self,
181
+ hidden_states: torch.Tensor,
182
+ topk_weights: torch.Tensor,
183
+ topk_ids: torch.Tensor,
184
+ is_sequence_parallel: bool = False,
185
+ extra_tensors: list[torch.Tensor] | None = None,
186
+ ) -> (
187
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
188
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
189
+ ):
190
+ """
191
+ Gather hidden_states and router_logits from all dp ranks.
192
+ """
193
+ dp_metadata = get_forward_context().dp_metadata
194
+ assert dp_metadata is not None
195
+ sizes = dp_metadata.get_chunk_sizes_across_dp_rank()
196
+ assert sizes is not None
197
+ dist_group = get_ep_group() if is_sequence_parallel else get_dp_group()
198
+ assert sizes[dist_group.rank_in_group] == hidden_states.shape[0]
199
+
200
+ tensors_to_gather = [hidden_states, topk_weights, topk_ids]
201
+ if extra_tensors is not None:
202
+ tensors_to_gather.extend(extra_tensors)
203
+
204
+ gathered_tensors = dist_group.all_gatherv(
205
+ tensors_to_gather,
206
+ dim=0,
207
+ sizes=sizes,
208
+ )
209
+
210
+ hidden_states = gathered_tensors[0]
211
+ topk_weights = gathered_tensors[1]
212
+ topk_ids = gathered_tensors[2]
213
+
214
+ if extra_tensors is None:
215
+ return hidden_states, topk_weights, topk_ids
216
+
217
+ return hidden_states, topk_weights, topk_ids, gathered_tensors[3:]
218
+
219
def combine(
220
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
221
) -> torch.Tensor:
@@ -216,7 +284,7 @@ class PPLXAll2AllManager(All2AllManagerBase):
284
pplx.AllToAll.internode if self.internode else pplx.AllToAll.intranode,
285
)
286
219
- def dispatch(
287
+ def dispatch_router_logits(
288
self,
289
hidden_states: torch.Tensor,
290
router_logits: torch.Tensor,
@@ -225,6 +293,19 @@ class PPLXAll2AllManager(All2AllManagerBase):
293
) -> tuple[torch.Tensor, torch.Tensor]:
294
raise NotImplementedError
295
296
+ def dispatch(
297
+ self,
298
+ hidden_states: torch.Tensor,
299
+ topk_weights: torch.Tensor,
300
+ topk_ids: torch.Tensor,
301
+ is_sequence_parallel: bool = False,
302
+ extra_tensors: list[torch.Tensor] | None = None,
303
+ ) -> (
304
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
305
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
306
+ ):
307
+ raise NotImplementedError
308
+
309
def combine(
310
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
311
) -> torch.Tensor:
@@ -264,7 +345,7 @@ class DeepEPAll2AllManagerBase(All2AllManagerBase):
345
def get_handle(self, kwargs):
346
raise NotImplementedError
347
267
- def dispatch(
348
+ def dispatch_router_logits(
349
self,
350
hidden_states: torch.Tensor,
351
router_logits: torch.Tensor,
@@ -273,6 +354,19 @@ class DeepEPAll2AllManagerBase(All2AllManagerBase):
354
) -> tuple[torch.Tensor, torch.Tensor]:
355
raise NotImplementedError
356
357
+ def dispatch(
358
+ self,
359
+ hidden_states: torch.Tensor,
360
+ topk_weights: torch.Tensor,
361
+ topk_ids: torch.Tensor,
362
+ is_sequence_parallel: bool = False,
363
+ extra_tensors: list[torch.Tensor] | None = None,
364
+ ) -> (
365
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
366
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
367
+ ):
368
+ raise NotImplementedError
369
+
370
def combine(
371
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
372
) -> torch.Tensor:
vllm/distributed/device_communicators/base_device_communicator.py
+43
-4
@@ -1,7 +1,6 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
import threading
4
-from typing import Any
4
from weakref import WeakValueDictionary
5
6
import torch
@@ -64,13 +63,32 @@ class All2AllManagerBase:
63
# and reuse it for the same config.
64
raise NotImplementedError
65
67
- def dispatch(
66
+ def dispatch_router_logits(
67
self,
68
hidden_states: torch.Tensor,
69
router_logits: torch.Tensor,
70
is_sequence_parallel: bool = False,
71
extra_tensors: list[torch.Tensor] | None = None,
73
- ) -> Any:
72
+ ) -> (
73
+ tuple[torch.Tensor, torch.Tensor]
74
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
75
+ ):
76
+ # Subclasses should either:
77
+ # - implement handling for extra_tensors, or
78
+ # - raise a clear error if extra_tensors is not supported.
79
+ raise NotImplementedError
80
+
81
+ def dispatch(
82
+ self,
83
+ hidden_states: torch.Tensor,
84
+ topk_weights: torch.Tensor,
85
+ topk_ids: torch.Tensor,
86
+ is_sequence_parallel: bool = False,
87
+ extra_tensors: list[torch.Tensor] | None = None,
88
+ ) -> (
89
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
90
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
91
+ ):
92
# Subclasses should either:
93
# - implement handling for extra_tensors, or
94
# - raise a clear error if extra_tensors is not supported.
@@ -280,7 +298,7 @@ class DeviceCommunicatorBase:
298
for module in moe_modules:
299
module.maybe_init_modular_kernel()
300
283
- def dispatch(
301
+ def dispatch_router_logits(
302
self,
303
hidden_states: torch.Tensor,
304
router_logits: torch.Tensor,
@@ -294,8 +312,29 @@ class DeviceCommunicatorBase:
312
Dispatch the hidden states and router logits to the appropriate device.
313
This is a no-op in the base class.
314
"""
315
+ if extra_tensors is not None:
316
+ return hidden_states, router_logits, extra_tensors
317
return hidden_states, router_logits
318
319
+ def dispatch(
320
+ self,
321
+ hidden_states: torch.Tensor,
322
+ topk_weights: torch.Tensor,
323
+ topk_ids: torch.Tensor,
324
+ is_sequence_parallel: bool = False,
325
+ extra_tensors: list[torch.Tensor] | None = None,
326
+ ) -> (
327
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
328
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
329
+ ):
330
+ """
331
+ Dispatch the hidden states and topk weights/ids to the appropriate device.
332
+ This is a no-op in the base class.
333
+ """
334
+ if extra_tensors is not None:
335
+ return hidden_states, topk_weights, topk_ids, extra_tensors
336
+ return hidden_states, topk_weights, topk_ids
337
+
338
def combine(
339
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
340
) -> torch.Tensor:
vllm/distributed/device_communicators/cpu_communicator.py
+43
-7
@@ -130,29 +130,65 @@ class CpuCommunicator(DeviceCommunicatorBase):
130
) -> dict[str, torch.Tensor | Any]:
131
return self.dist_module.recv_tensor_dict(src)
132
133
- def dispatch( # type: ignore[override]
133
+ def dispatch_router_logits(
134
self,
135
hidden_states: torch.Tensor,
136
router_logits: torch.Tensor,
137
is_sequence_parallel: bool = False,
138
extra_tensors: list[torch.Tensor] | None = None,
139
- ) -> tuple[torch.Tensor, torch.Tensor]:
139
+ ) -> (
140
+ tuple[torch.Tensor, torch.Tensor]
141
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
142
+ ):
143
+ """
144
+ Dispatch the hidden states and router logits to the appropriate device.
145
+ This is a no-op in the base class.
146
+ """
147
+
148
assert self.all2all_manager is not None
141
- return self.all2all_manager.dispatch(
149
+ return self.all2all_manager.dispatch_router_logits(
150
hidden_states,
151
router_logits,
152
is_sequence_parallel,
145
- extra_tensors, # type: ignore[call-arg]
153
+ extra_tensors,
154
+ )
155
+
156
+ def dispatch(
157
+ self,
158
+ hidden_states: torch.Tensor,
159
+ topk_weights: torch.Tensor,
160
+ topk_ids: torch.Tensor,
161
+ is_sequence_parallel: bool = False,
162
+ extra_tensors: list[torch.Tensor] | None = None,
163
+ ) -> (
164
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
165
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
166
+ ):
167
+ """
168
+ Dispatch the hidden states and topk weights/ids to the appropriate device.
169
+ This is a no-op in the base class.
170
+ """
171
+ assert self.all2all_manager is not None
172
+ return self.all2all_manager.dispatch(
173
+ hidden_states,
174
+ topk_weights,
175
+ topk_ids,
176
+ is_sequence_parallel,
177
+ extra_tensors=extra_tensors,
178
)
179
180
def combine(
181
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
182
) -> torch.Tensor:
183
+ """
184
+ Combine the hidden states and router logits from the appropriate device.
185
+ This is a no-op in the base class.
186
+ """
187
assert self.all2all_manager is not None
152
- hidden_states = self.all2all_manager.combine(
153
- hidden_states, is_sequence_parallel
188
+ return self.all2all_manager.combine(
189
+ hidden_states,
190
+ is_sequence_parallel,
191
)
155
- return hidden_states
192
193
194
class _CPUSHMDistributed:
vllm/distributed/device_communicators/cuda_communicator.py
+39
-6
@@ -322,7 +322,7 @@ class CudaCommunicator(DeviceCommunicatorBase):
322
323
return output_list
324
325
- def dispatch( # type: ignore[override]
325
+ def dispatch_router_logits(
326
self,
327
hidden_states: torch.Tensor,
328
router_logits: torch.Tensor,
@@ -332,19 +332,52 @@ class CudaCommunicator(DeviceCommunicatorBase):
332
tuple[torch.Tensor, torch.Tensor]
333
| tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
334
):
335
+ """
336
+ Dispatch the hidden states and router logits to the appropriate device.
337
+ This is a no-op in the base class.
338
+ """
339
+
340
assert self.all2all_manager is not None
336
- return self.all2all_manager.dispatch(
341
+ return self.all2all_manager.dispatch_router_logits(
342
hidden_states,
343
router_logits,
344
is_sequence_parallel,
340
- extra_tensors, # type: ignore[call-arg]
345
+ extra_tensors,
346
+ )
347
+
348
+ def dispatch(
349
+ self,
350
+ hidden_states: torch.Tensor,
351
+ topk_weights: torch.Tensor,
352
+ topk_ids: torch.Tensor,
353
+ is_sequence_parallel: bool = False,
354
+ extra_tensors: list[torch.Tensor] | None = None,
355
+ ) -> (
356
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
357
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
358
+ ):
359
+ """
360
+ Dispatch the hidden states and topk weights/ids to the appropriate device.
361
+ This is a no-op in the base class.
362
+ """
363
+ assert self.all2all_manager is not None
364
+ return self.all2all_manager.dispatch(
365
+ hidden_states,
366
+ topk_weights,
367
+ topk_ids,
368
+ is_sequence_parallel,
369
+ extra_tensors=extra_tensors,
370
)
371
372
def combine(
373
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
374
) -> torch.Tensor:
375
+ """
376
+ Combine the hidden states and router logits from the appropriate device.
377
+ This is a no-op in the base class.
378
+ """
379
assert self.all2all_manager is not None
347
- hidden_states = self.all2all_manager.combine(
348
- hidden_states, is_sequence_parallel
380
+ return self.all2all_manager.combine(
381
+ hidden_states,
382
+ is_sequence_parallel,
383
)
350
- return hidden_states
vllm/distributed/device_communicators/mnnvl_compat.py
+11
@@ -1,5 +1,7 @@
1
# SPDX-License-Identifier: Apache-2.0
2
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+from typing import Any
4
+
5
import torch.distributed as dist
6
from flashinfer.comm.mnnvl import CommBackend as CommBackend
7
@@ -23,5 +25,14 @@ class CustomCommunicator(CommBackend):
25
dist.all_gather_object(gathered, data, group=self._group)
26
return gathered
27
28
+ # NOTE(rob): CommBackend is an abstract class, and bcast/barrier
29
+ # are unimplemented on vLLM side. If we need to utilize these
30
+ # methods in the future, can create a concrete implementation.
31
+ def bcast(self, data: Any, root: int) -> Any:
32
+ raise NotImplementedError
33
+
34
+ def barrier(self) -> None:
35
+ raise NotImplementedError
36
+
37
def Split(self, color: int, key: int) -> "CustomCommunicator":
38
return self
vllm/distributed/device_communicators/xpu_communicator.py
+43
-7
@@ -196,26 +196,62 @@ class XpuCommunicator(DeviceCommunicatorBase):
196
def broadcast(self, input_: torch.Tensor, src: int = 0) -> None:
197
dist.broadcast(input_, src=src, group=self.device_group)
198
199
- def dispatch(
199
+ def dispatch_router_logits(
200
self,
201
hidden_states: torch.Tensor,
202
router_logits: torch.Tensor,
203
is_sequence_parallel: bool = False,
204
extra_tensors: list[torch.Tensor] | None = None,
205
- ) -> tuple[torch.Tensor, torch.Tensor]:
205
+ ) -> (
206
+ tuple[torch.Tensor, torch.Tensor]
207
+ | tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
208
+ ):
209
+ """
210
+ Dispatch the hidden states and router logits to the appropriate device.
211
+ This is a no-op in the base class.
212
+ """
213
+
214
assert self.all2all_manager is not None
207
- return self.all2all_manager.dispatch(
215
+ return self.all2all_manager.dispatch_router_logits(
216
hidden_states,
217
router_logits,
218
is_sequence_parallel,
211
- extra_tensors, # type: ignore[call-arg]
219
+ extra_tensors,
220
+ )
221
+
222
+ def dispatch(
223
+ self,
224
+ hidden_states: torch.Tensor,
225
+ topk_weights: torch.Tensor,
226
+ topk_ids: torch.Tensor,
227
+ is_sequence_parallel: bool = False,
228
+ extra_tensors: list[torch.Tensor] | None = None,
229
+ ) -> (
230
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor]
231
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
232
+ ):
233
+ """
234
+ Dispatch the hidden states and topk weights/ids to the appropriate device.
235
+ This is a no-op in the base class.
236
+ """
237
+ assert self.all2all_manager is not None
238
+ return self.all2all_manager.dispatch(
239
+ hidden_states,
240
+ topk_weights,
241
+ topk_ids,
242
+ is_sequence_parallel,
243
+ extra_tensors=extra_tensors,
244
)
245
246
def combine(
247
self, hidden_states: torch.Tensor, is_sequence_parallel: bool = False
248
) -> torch.Tensor:
249
+ """
250
+ Combine the hidden states and router logits from the appropriate device.
251
+ This is a no-op in the base class.
252
+ """
253
assert self.all2all_manager is not None
218
- hidden_states = self.all2all_manager.combine(
219
- hidden_states, is_sequence_parallel
254
+ return self.all2all_manager.combine(
255
+ hidden_states,
256
+ is_sequence_parallel,
257
)
221
- return hidden_states
vllm/distributed/parallel_state.py
+24
-2
@@ -1000,7 +1000,7 @@ class GroupCoordinator:
1000
if self.device_communicator is not None:
1001
self.device_communicator.prepare_communication_buffer_for_model(model)
1002
1003
- def dispatch(
1003
+ def dispatch_router_logits(
1004
self,
1005
hidden_states: torch.Tensor,
1006
router_logits: torch.Tensor,
@@ -1011,7 +1011,7 @@ class GroupCoordinator:
1011
| tuple[torch.Tensor, torch.Tensor, list[torch.Tensor]]
1012
):
1013
if self.device_communicator is not None:
1014
- return self.device_communicator.dispatch( # type: ignore[call-arg]
1014
+ return self.device_communicator.dispatch_router_logits(
1015
hidden_states,
1016
router_logits,
1017
is_sequence_parallel,
@@ -1020,6 +1020,28 @@ class GroupCoordinator:
1020
else:
1021
return hidden_states, router_logits
1022
1023
+ def dispatch(
1024
+ self,
1025
+ hidden_states: torch.Tensor,
1026
+ topk_weights: torch.Tensor,
1027
+ topk_ids: torch.Tensor,
1028
+ is_sequence_parallel: bool = False,
1029
+ extra_tensors: list[torch.Tensor] | None = None,
1030
+ ) -> (
1031
+ tuple[torch.Tensor, torch.Tensor, torch.Tensor, list[torch.Tensor]]
1032
+ | tuple[torch.Tensor, torch.Tensor, torch.Tensor]
1033
+ ):
1034
+ if self.device_communicator is not None:
1035
+ return self.device_communicator.dispatch(
1036
+ hidden_states,
1037
+ topk_weights,
1038
+ topk_ids,
1039
+ is_sequence_parallel,
1040
+ extra_tensors,
1041
+ )
1042
+ else:
1043
+ return hidden_states, topk_weights, topk_ids
1044
+
1045
def combine(
1046
self, hidden_states, is_sequence_parallel: bool = False
1047
) -> torch.Tensor:
vllm/model_executor/layers/fused_moe/all2all_utils.py
+54
-6
@@ -7,17 +7,27 @@ import torch
7
from vllm.distributed import (
8
get_ep_group,
9
)
10
+from vllm.logger import init_logger
11
from vllm.model_executor.layers.fused_moe.config import (
12
FusedMoEConfig,
13
FusedMoEParallelConfig,
14
FusedMoEQuantConfig,
15
)
16
+from vllm.model_executor.layers.fused_moe.flashinfer_a2a_prepare_finalize import (
17
+ FlashInferA2APrepareAndFinalize,
18
+)
19
from vllm.model_executor.layers.fused_moe.modular_kernel import (
20
FusedMoEPrepareAndFinalize,
21
)
22
+from vllm.model_executor.layers.fused_moe.prepare_finalize import (
23
+ MoEPrepareAndFinalizeNaiveEP,
24
+ MoEPrepareAndFinalizeNoEP,
25
+)
26
from vllm.platforms import current_platform
27
from vllm.utils.import_utils import has_deep_ep, has_mori, has_pplx
28
29
+logger = init_logger(__name__)
30
+
31
if current_platform.is_cuda_alike():
32
if has_pplx():
33
from .pplx_prepare_finalize import (
@@ -70,20 +80,46 @@ def maybe_make_prepare_finalize(
80
moe: FusedMoEConfig,
81
quant_config: FusedMoEQuantConfig | None,
82
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
83
+ allow_new_interface: bool = False,
84
) -> FusedMoEPrepareAndFinalize | None:
85
+ # NOTE(rob): we are migrating each quant_method to hold the MK
86
+ # in all cases. The allow_new_interface=False flag allow us to fall
87
+ # back to the old method for methods that have not yet been migrated.
88
+ #
89
+ # In old method:
90
+ # * maybe_init_modular_kernel() calls this function. If we are
91
+ # using no Dp/Ep or naive all2all, we return None this function
92
+ # returns None and no ModularKernelMethod is created. If non-naive
93
+ # all2all is used, this returns a PrepareAndFinalize object and
94
+ # a ModularKernelMethod is created.
95
+ # In new method:
96
+ # * maybe_make_prepare_finalize() is called from the oracle. We
97
+ # always return a PrepareAndFinalize object and the quant method
98
+ # holds the ModularKernel.
99
if not moe.moe_parallel_config.use_all2all_kernels:
75
- return None
100
+ if not allow_new_interface:
101
+ return None
102
+
103
+ # For DP/TP case, fall back to naive P/F.
104
+ if moe.moe_parallel_config.dp_size > 1:
105
+ logger.info_once(
106
+ "Detected DP deployment with no --enable-expert-parallel. "
107
+ "Falling back to AllGather+ReduceScatter dispatch/combine."
108
+ )
109
+ return MoEPrepareAndFinalizeNaiveEP(
110
+ is_sequence_parallel=moe.moe_parallel_config.is_sequence_parallel,
111
+ num_dispatchers=(
112
+ get_ep_group().device_communicator.all2all_manager.world_size
113
+ ),
114
+ )
115
+ else:
116
+ return MoEPrepareAndFinalizeNoEP()
117
118
all2all_manager = get_ep_group().device_communicator.all2all_manager
119
assert all2all_manager is not None
120
121
prepare_finalize: FusedMoEPrepareAndFinalize | None = None
122
82
- # TODO(rob): update this as part of the MoE refactor.
83
- assert not moe.use_flashinfer_cutlass_kernels, (
84
- "Must be created in modelopt.py or fp8.py"
85
- )
86
-
123
if moe.use_pplx_kernels:
124
assert quant_config is not None
125
@@ -203,4 +239,16 @@ def maybe_make_prepare_finalize(
239
use_fp8_dispatch=use_fp8_dispatch,
240
)
241
242
+ elif moe.use_fi_all2allv_kernels:
243
+ assert quant_config is not None
244
+ prepare_finalize = FlashInferA2APrepareAndFinalize(
245
+ num_dispatchers=all2all_manager.world_size,
246
+ )
247
+
248
+ elif moe.use_naive_all2all_kernels and allow_new_interface:
249
+ prepare_finalize = MoEPrepareAndFinalizeNaiveEP(
250
+ is_sequence_parallel=(moe.moe_parallel_config.is_sequence_parallel),
251
+ num_dispatchers=all2all_manager.world_size,
252
+ )
253
+
254
return prepare_finalize
vllm/model_executor/layers/fused_moe/config.py
+16
-10
@@ -20,7 +20,6 @@ from vllm.model_executor.layers.quantization.utils.ocp_mx_utils import (
20
)
21
from vllm.model_executor.layers.quantization.utils.quant_utils import GroupShape
22
from vllm.platforms import current_platform
23
-from vllm.utils.flashinfer import has_flashinfer_cutlass_fused_moe
23
from vllm.utils.import_utils import has_triton_kernels
24
from vllm.utils.math_utils import cdiv
25
@@ -862,6 +861,7 @@ class FusedMoEParallelConfig:
861
862
use_ep: bool # whether to use EP or not
863
all2all_backend: str # all2all backend for MoE communication
864
+ is_sequence_parallel: bool # whether sequence parallelism is used
865
enable_eplb: bool # whether to enable expert load balancing
866
867
@property
@@ -883,6 +883,12 @@ class FusedMoEParallelConfig:
883
def use_deepep_ll_kernels(self):
884
return self.use_all2all_kernels and self.all2all_backend == "deepep_low_latency"
885
886
+ @property
887
+ def use_fi_all2allv_kernels(self):
888
+ return (
889
+ self.use_all2all_kernels and self.all2all_backend == "flashinfer_all2allv"
890
+ )
891
+
892
@property
893
def use_batched_activation_format(self):
894
return self.use_deepep_ll_kernels or self.use_pplx_kernels
@@ -1014,6 +1020,7 @@ class FusedMoEParallelConfig:
1020
ep_rank=0,
1021
use_ep=False,
1022
all2all_backend=vllm_parallel_config.all2all_backend,
1023
+ is_sequence_parallel=vllm_parallel_config.use_sequence_parallel_moe,
1024
enable_eplb=vllm_parallel_config.enable_eplb,
1025
)
1026
# DP + EP / TP + EP / DP + TP + EP
@@ -1033,6 +1040,7 @@ class FusedMoEParallelConfig:
1040
ep_rank=ep_rank,
1041
use_ep=True,
1042
all2all_backend=vllm_parallel_config.all2all_backend,
1043
+ is_sequence_parallel=vllm_parallel_config.use_sequence_parallel_moe,
1044
enable_eplb=vllm_parallel_config.enable_eplb,
1045
)
1046
@@ -1051,6 +1059,7 @@ class FusedMoEParallelConfig:
1059
use_ep=False,
1060
all2all_backend="naive",
1061
enable_eplb=False,
1062
+ is_sequence_parallel=False,
1063
)
1064
1065
@@ -1145,12 +1154,9 @@ class FusedMoEConfig:
1154
return self.moe_parallel_config.use_mori_kernels
1155
1156
@property
1148
- def use_flashinfer_cutlass_kernels(self):
1149
- """
1150
- Whether to use FlashInfer cutlass kernels for NVFP4 MoE.
1151
- """
1152
- return (
1153
- envs.VLLM_USE_FLASHINFER_MOE_FP4
1154
- and has_flashinfer_cutlass_fused_moe()
1155
- and envs.VLLM_FLASHINFER_MOE_BACKEND == "throughput"
1156
- )
1157
+ def use_fi_all2allv_kernels(self):
1158
+ return self.moe_parallel_config.use_fi_all2allv_kernels
1159
+
1160
+ @property
1161
+ def use_naive_all2all_kernels(self):
1162
+ return self.moe_parallel_config.use_naive_all2all_kernels
vllm/model_executor/layers/fused_moe/cutlass_moe.py
+14
-6
@@ -103,7 +103,14 @@ def run_cutlass_moe_fp8(
103
or a2_scale.size(0) == a1q.shape[0]
104
), "Intermediate scale shape mismatch"
105
assert out_dtype in [torch.half, torch.bfloat16], "Invalid output dtype"
106
- if expert_map is not None:
106
+
107
+ # NOTE(rob): the expert_map is used for the STANDARD case and
108
+ # the batched format is used by the BATCHED case.
109
+ # TODO(rob): update the MK interface to only pass the expert_map
110
+ # during the STANDARD case to make this clearer across all kernels.
111
+ if use_batched_format:
112
+ assert expert_num_tokens is not None
113
+ else:
114
assert expert_num_tokens is None
115
116
# We have two modes: batched experts and non-batched experts.
@@ -379,7 +386,10 @@ class CutlassExpertsFp8(CutlassExpertsFp8Base):
386
# needed for STANDARD activation format kernels in DP/EP mode.
387
# Note that the BATCHED activation format does not use
388
# the expert map for identifying experts.
382
- return not moe_parallel_config.use_all2all_kernels
389
+ return not (
390
+ moe_parallel_config.use_fi_all2allv_kernels
391
+ or moe_parallel_config.use_deepep_ht_kernels
392
+ )
393
394
def supports_chunking(self) -> bool:
395
return True
@@ -641,10 +651,8 @@ def run_cutlass_moe_fp4(
651
652
653
class CutlassExpertsFp4(mk.FusedMoEPermuteExpertsUnpermute):
644
- @staticmethod
645
- def expects_unquantized_inputs(
646
- moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig
647
- ) -> bool:
654
+ @property
655
+ def expects_unquantized_inputs(self) -> bool:
656
return True
657
658
@staticmethod
vllm/model_executor/layers/fused_moe/deep_gemm_moe.py
+2
-1
@@ -148,7 +148,8 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
148
149
@staticmethod
150
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
151
- return True
151
+ # NOTE(rob): discovered an IMA with this combination. Needs investigation.
152
+ return not moe_parallel_config.use_fi_all2allv_kernels
153
154
def supports_chunking(self) -> bool:
155
return True
vllm/model_executor/layers/fused_moe/deepep_ht_prepare_finalize.py
+24
-8
@@ -103,6 +103,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
103
num_experts: int,
104
a1_scale: torch.Tensor | None,
105
quant_config: FusedMoEQuantConfig,
106
+ defer_input_quant: bool,
107
) -> Callable:
108
has_scales = token_scales is not None
109
@@ -174,6 +175,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
175
expert_topk_weights,
176
a1_scale,
177
quant_config,
178
+ defer_input_quant=defer_input_quant,
179
)
180
181
def _receiver(
@@ -187,6 +189,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
189
expert_topk_weights: torch.Tensor | None,
190
a1_scale: torch.Tensor | None,
191
quant_config: FusedMoEQuantConfig,
192
+ defer_input_quant: bool,
193
) -> mk.PrepareResultType:
194
if event.event is not None:
195
event.current_stream_wait()
@@ -221,14 +224,15 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
224
expert_num_tokens_per_expert_list, device=expert_x.device
225
)
226
224
- # Dispatch and Quant
225
- # DeepEP kernels only support dispatching block-quantized
226
- # activation scales.
227
- # Dispatch in bfloat16 and quantize afterwards
228
- if not quant_config.is_block_quantized:
227
+ # * For non-block quant, dispatch in b16 and quantize now as
228
+ # DeepEP kernels only support dispatching block scales.
229
+ # * For expert kernels that require unquantized inputs,
230
+ # defer quantization to FusedMoEExpertsPermuteUnpermute.
231
+ if not quant_config.is_block_quantized and not defer_input_quant:
232
# Quantize after dispatch.
233
expert_x_scale = None
234
if expert_x.numel() != 0:
235
+ # TODO: support per_act_token_quant,
236
expert_x, expert_x_scale = moe_kernel_quantize_input(
237
expert_x,
238
a1_scale,
@@ -257,6 +261,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
261
expert_map: torch.Tensor | None,
262
apply_router_weight_on_input: bool,
263
quant_config: FusedMoEQuantConfig,
264
+ defer_input_quant: bool = False,
265
) -> mk.ReceiverType:
266
if apply_router_weight_on_input:
267
topk = topk_ids.size(1)
@@ -266,8 +271,12 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
271
)
272
a1 = a1 * topk_weights.to(a1.dtype)
273
269
- if quant_config.is_block_quantized:
270
- # Quant and Dispatch
274
+ # * DeepEP only supports fp8 block scales so quantize
275
+ # before the dispatch for these models.
276
+ # * For all other quantization, dispatch after.
277
+ # * For expert kernels that require unquantized inputs,
278
+ # defer quantization to FusedMoEExpertsPermuteUnpermute.
279
+ if quant_config.is_block_quantized and not defer_input_quant:
280
a1q, a1q_scale = moe_kernel_quantize_input(
281
a1,
282
quant_config.a1_scale,
@@ -281,7 +290,11 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
290
else:
291
a1q = a1
292
a1q_scale = None
284
- a1_post_scale = quant_config.a1_scale
293
+ a1_post_scale = (
294
+ quant_config.a1_gscale
295
+ if quant_config.quant_dtype == "nvfp4"
296
+ else quant_config.a1_scale
297
+ )
298
299
return self._do_dispatch(
300
tokens=a1q,
@@ -291,6 +304,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
304
num_experts=num_experts,
305
a1_scale=a1_post_scale,
306
quant_config=quant_config,
307
+ defer_input_quant=defer_input_quant,
308
)
309
310
def prepare(
@@ -302,6 +316,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
316
expert_map: torch.Tensor | None,
317
apply_router_weight_on_input: bool,
318
quant_config: FusedMoEQuantConfig,
319
+ defer_input_quant: bool = False,
320
) -> mk.PrepareResultType:
321
receiver = self.prepare_async(
322
a1,
@@ -311,6 +326,7 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
326
expert_map,
327
apply_router_weight_on_input,
328
quant_config,
329
+ defer_input_quant,
330
)
331
return receiver()
332
vllm/model_executor/layers/fused_moe/deepep_ll_prepare_finalize.py
+13
@@ -242,7 +242,14 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
242
expert_map: torch.Tensor | None,
243
apply_router_weight_on_input: bool,
244
quant_config: FusedMoEQuantConfig,
245
+ defer_input_quant: bool = False,
246
) -> tuple[Callable, mk.ReceiverType]:
247
+ if defer_input_quant:
248
+ raise NotImplementedError(
249
+ f"{self.__class__.__name__} does not support defer_input_quant=True. "
250
+ "Please select an MoE kernel that accepts quantized inputs."
251
+ )
252
+
253
hidden_size = a1.size(1)
254
assert hidden_size in self.SUPPORTED_HIDDEN_SIZES, (
255
f"Hidden Size {hidden_size} not in supported list of hidden sizes"
@@ -344,7 +351,13 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
351
expert_map: torch.Tensor | None,
352
apply_router_weight_on_input: bool,
353
quant_config: FusedMoEQuantConfig,
354
+ defer_input_quant: bool = False,
355
) -> mk.PrepareResultType:
356
+ if defer_input_quant:
357
+ raise NotImplementedError(
358
+ f"{self.__class__.__name__} does not support defer_input_quant=True. "
359
+ "Please select an MoE kernel that accepts quantized inputs."
360
+ )
361
hook, receiver = self.prepare_async(
362
a1,
363
topk_weights,
vllm/model_executor/layers/fused_moe/flashinfer_a2a_prepare_finalize.py
new
+226
@@ -0,0 +1,226 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+import torch
5
+
6
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.distributed import get_ep_group
8
+from vllm.distributed.device_communicators.base_device_communicator import (
9
+ All2AllManagerBase,
10
+)
11
+from vllm.forward_context import get_forward_context
12
+from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
13
+from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
14
+from vllm.utils.flashinfer import nvfp4_block_scale_interleave
15
+
16
+
17
+def get_local_sizes():
18
+ return get_forward_context().dp_metadata.get_chunk_sizes_across_dp_rank()
19
+
20
+
21
+class FlashInferA2APrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
22
+ """Base class for FlashInfer MoE prepare and finalize operations."""
23
+
24
+ def __init__(
25
+ self,
26
+ num_dispatchers: int = 1,
27
+ ):
28
+ super().__init__()
29
+ self.num_dispatchers_ = num_dispatchers
30
+ self.all2all_manager = get_ep_group().device_communicator.all2all_manager
31
+
32
+ @property
33
+ def activation_format(self) -> mk.FusedMoEActivationFormat:
34
+ return mk.FusedMoEActivationFormat.Standard
35
+
36
+ def max_num_tokens_per_rank(self) -> int | None:
37
+ return None
38
+
39
+ def topk_indices_dtype(self) -> torch.dtype | None:
40
+ return None
41
+
42
+ def num_dispatchers(self) -> int:
43
+ return self.num_dispatchers_
44
+
45
+ def output_is_reduced(self) -> bool:
46
+ return False
47
+
48
+ def _apply_router_weight_on_input(
49
+ self,
50
+ a1: torch.Tensor,
51
+ topk_weights: torch.Tensor,
52
+ topk_ids: torch.Tensor,
53
+ apply_router_weight_on_input: bool,
54
+ ) -> None:
55
+ """Apply router weight on input if needed."""
56
+ if apply_router_weight_on_input:
57
+ topk = topk_ids.size(1)
58
+ assert topk == 1, (
59
+ "apply_router_weight_on_input is only implemented for topk=1"
60
+ )
61
+ a1.mul_(topk_weights.to(a1.dtype))
62
+
63
+ def prepare(
64
+ self,
65
+ a1: torch.Tensor,
66
+ topk_weights: torch.Tensor,
67
+ topk_ids: torch.Tensor,
68
+ num_experts: int,
69
+ expert_map: torch.Tensor | None,
70
+ apply_router_weight_on_input: bool,
71
+ quant_config: FusedMoEQuantConfig,
72
+ defer_input_quant: bool = False,
73
+ ) -> mk.PrepareResultType:
74
+ self._apply_router_weight_on_input(
75
+ a1, topk_weights, topk_ids, apply_router_weight_on_input
76
+ )
77
+ global_num_tokens_cpu = get_local_sizes()
78
+ top_k = topk_ids.size(1)
79
+
80
+ (self.alltoall_info, topk_ids, topk_weights, a1q, a1q_scale) = (
81
+ flashinfer_alltoall_dispatch(
82
+ self.all2all_manager,
83
+ global_num_tokens_cpu,
84
+ a1,
85
+ quant_config.a1_gscale,
86
+ topk_ids,
87
+ topk_weights,
88
+ top_k,
89
+ num_experts,
90
+ quant_config,
91
+ defer_input_quant=defer_input_quant,
92
+ )
93
+ )
94
+
95
+ return a1q, a1q_scale, None, topk_ids, topk_weights
96
+
97
+ def finalize(
98
+ self,
99
+ output: torch.Tensor,
100
+ fused_expert_output: torch.Tensor,
101
+ topk_weights: torch.Tensor,
102
+ topk_ids: torch.Tensor,
103
+ apply_router_weight_on_input: bool,
104
+ weight_and_reduce_impl: mk.TopKWeightAndReduce,
105
+ ) -> None:
106
+ top_k = topk_ids.size(1)
107
+ token_count = output.shape[0]
108
+ fused_expert_output = flashinfer_alltoall_combine(
109
+ self.all2all_manager,
110
+ fused_expert_output,
111
+ top_k=top_k,
112
+ token_count=token_count,
113
+ alltoall_info=self.alltoall_info,
114
+ )
115
+ output.copy_(fused_expert_output)
116
+
117
+
118
+def flashinfer_alltoall_dispatch(
119
+ all2all_manager: All2AllManagerBase,
120
+ global_num_tokens_cpu: list[int],
121
+ x: torch.Tensor,
122
+ gs: torch.Tensor,
123
+ topk_ids: torch.Tensor,
124
+ topk_weights: torch.Tensor,
125
+ top_k: int,
126
+ num_experts: int,
127
+ quant_config: FusedMoEQuantConfig,
128
+ defer_input_quant: bool = False,
129
+):
130
+ from flashinfer.comm.trtllm_alltoall import MnnvlMoe
131
+
132
+ assert all2all_manager.ensure_alltoall_workspace_initialized(), (
133
+ "FlashInfer AllToAll workspace not available"
134
+ )
135
+
136
+ ep_rank = all2all_manager.rank
137
+ ep_size = all2all_manager.world_size
138
+ max_num_token = (
139
+ max(global_num_tokens_cpu) if global_num_tokens_cpu is not None else x.shape[0]
140
+ )
141
+ orig_topk_weights_dtype = topk_weights.dtype
142
+ alltoall_info, topk_ids, topk_weights, _ = (
143
+ MnnvlMoe.mnnvl_moe_alltoallv_prepare_without_allgather(
144
+ topk_ids,
145
+ topk_weights,
146
+ None,
147
+ all2all_manager.prepare_workspace_tensor,
148
+ max_num_token,
149
+ ep_rank,
150
+ ep_size,
151
+ num_experts,
152
+ num_experts,
153
+ top_k,
154
+ )
155
+ )
156
+ topk_weights = topk_weights.view(dtype=orig_topk_weights_dtype)
157
+
158
+ if not defer_input_quant:
159
+ x, x_sf = moe_kernel_quantize_input(
160
+ x,
161
+ gs,
162
+ quant_config.quant_dtype,
163
+ quant_config.per_act_token_quant,
164
+ quant_config.block_shape,
165
+ # NOTE: swizzling pads the scales to multiple of 128
166
+ # which makes the scales tensor different shape than
167
+ # the hidden states, breaking the A2A kernel. So, we
168
+ # delay the swizzling until after the A2A.
169
+ is_fp4_scale_swizzled=False,
170
+ )
171
+
172
+ x = MnnvlMoe.mnnvl_moe_alltoallv(
173
+ x,
174
+ alltoall_info,
175
+ all2all_manager.workspace_tensor,
176
+ ep_rank,
177
+ ep_size,
178
+ )
179
+
180
+ x_sf = MnnvlMoe.mnnvl_moe_alltoallv(
181
+ x_sf,
182
+ alltoall_info,
183
+ all2all_manager.workspace_tensor,
184
+ ep_rank,
185
+ ep_size,
186
+ )
187
+
188
+ # Swizzle after the A2A if nvfp4.
189
+ if quant_config.quant_dtype == "nvfp4":
190
+ if x_sf.element_size() == 1:
191
+ x_sf = x_sf.view(torch.uint8)
192
+ x_sf = nvfp4_block_scale_interleave(x_sf)
193
+ else:
194
+ # Block-scale path: pass activations through without quantization
195
+ x_sf = None
196
+ x = MnnvlMoe.mnnvl_moe_alltoallv(
197
+ x,
198
+ alltoall_info,
199
+ all2all_manager.workspace_tensor,
200
+ ep_rank,
201
+ ep_size,
202
+ )
203
+ return alltoall_info, topk_ids, topk_weights, x, x_sf
204
+
205
+
206
+def flashinfer_alltoall_combine(
207
+ all2all_manager: All2AllManagerBase,
208
+ output: torch.Tensor,
209
+ top_k: int,
210
+ token_count: int,
211
+ alltoall_info,
212
+):
213
+ from flashinfer.comm.trtllm_alltoall import MnnvlMoe
214
+
215
+ assert all2all_manager.ensure_alltoall_workspace_initialized(), (
216
+ "FlashInfer AllToAll workspace not available"
217
+ )
218
+ return MnnvlMoe.mnnvl_moe_alltoallv_combine(
219
+ output,
220
+ alltoall_info,
221
+ all2all_manager.workspace_tensor,
222
+ ep_rank=all2all_manager.rank,
223
+ ep_size=all2all_manager.world_size,
224
+ top_k=top_k,
225
+ token_count=token_count,
226
+ )
vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
+8
-16
@@ -78,16 +78,9 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
78
# - skip input activation quantization (kernel applies scaling)
79
self.use_deepseek_fp8_block_scale = quant_config.is_block_quantized
80
81
- @staticmethod
82
- def expects_unquantized_inputs(
83
- moe_config: mk.FusedMoEConfig, quant_config: FusedMoEQuantConfig
84
- ) -> bool:
85
- # NVFP4 TP kernels and FP8 block-quantized kernels apply
86
- # input quantization inside FusedMoEPermuteExpertsUnpermute.
87
- return (
88
- quant_config.use_nvfp4_w4a4
89
- and not moe_config.moe_parallel_config.use_all2all_kernels
90
- ) or (quant_config.use_fp8_w8a8 and quant_config.is_block_quantized)
81
+ @property
82
+ def expects_unquantized_inputs(self) -> bool:
83
+ return self.quant_config.use_fp8_w8a8 and self.quant_config.is_block_quantized
84
85
@staticmethod
86
def _supports_current_device() -> bool:
@@ -144,10 +137,8 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
137
# FLASHINFER_CUTLASS currently uses its down P/F, which does not
138
# work with SP. This will be removed in follow up after we get
139
# rid of the FlashInfer specific P/F function.
147
- return (
148
- moe_parallel_config.dp_size == 1
149
- or moe_parallel_config.dp_size == moe_parallel_config.ep_size
150
- )
140
+ # TODO: the per-tensor fp8 kernels don't work with MNNVL FI A2As.
141
+ return not moe_parallel_config.is_sequence_parallel
142
143
@staticmethod
144
def activation_format() -> mk.FusedMoEActivationFormat:
@@ -194,8 +185,9 @@ class FlashInferExperts(mk.FusedMoEPermuteExpertsUnpermute):
185
"""
186
workspace1 = (M, K)
187
workspace2 = (0,)
197
- # For TP, the quantization is fused with fused_moe call.
198
- output_shape = (M, K * 2 if self.quant_dtype == "nvfp4" and self.use_dp else K)
188
+ # For NVFP4, the output is stored in a packed int8 format,
189
+ # so the actual hidden dim is 2x the size of K here.
190
+ output_shape = (M, K * 2 if self.quant_dtype == "nvfp4" else K)
191
# The workspace is determined by `aq`, since it comes after any
192
# potential communication op and is involved in the expert computation.
193
return (workspace1, workspace2, output_shape)
vllm/model_executor/layers/fused_moe/flashinfer_cutlass_prepare_finalize.py
deleted
-373
@@ -1,373 +0,0 @@
1
-# SPDX-License-Identifier: Apache-2.0
2
-# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
-
4
-import torch
5
-
6
-import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
-from vllm.distributed import get_dp_group, get_ep_group
8
-from vllm.distributed.device_communicators.base_device_communicator import (
9
- All2AllManagerBase,
10
-)
11
-from vllm.forward_context import get_forward_context
12
-from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
13
-from vllm.model_executor.layers.fused_moe.prepare_finalize import (
14
- MoEPrepareAndFinalizeNoEP,
15
-)
16
-from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
17
- TopKWeightAndReduceNoOP,
18
-)
19
-from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
20
-from vllm.utils.flashinfer import nvfp4_block_scale_interleave
21
-
22
-
23
-def get_local_sizes():
24
- return get_forward_context().dp_metadata.get_chunk_sizes_across_dp_rank()
25
-
26
-
27
-class FlashInferCutlassMoEPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
28
- """Base class for FlashInfer MoE prepare and finalize operations."""
29
-
30
- def __init__(
31
- self,
32
- use_dp: bool,
33
- num_dispatchers: int = 1,
34
- use_deepseek_fp8_block_scale: bool = False,
35
- ):
36
- super().__init__()
37
- self.num_dispatchers_ = num_dispatchers
38
- self.use_dp = use_dp
39
- self.local_tokens = None
40
- # Toggle for DeepSeek-style FP8 block-scale path where activations are
41
- # not quantized here and weight block scales are consumed by the kernel.
42
- self.use_deepseek_fp8_block_scale = use_deepseek_fp8_block_scale
43
-
44
- @property
45
- def activation_format(self) -> mk.FusedMoEActivationFormat:
46
- return mk.FusedMoEActivationFormat.Standard
47
-
48
- def max_num_tokens_per_rank(self) -> int | None:
49
- return None
50
-
51
- def topk_indices_dtype(self) -> torch.dtype | None:
52
- return None
53
-
54
- def num_dispatchers(self) -> int:
55
- return self.num_dispatchers_
56
-
57
- def output_is_reduced(self) -> bool:
58
- return False
59
-
60
- def _apply_router_weight_on_input(
61
- self,
62
- a1: torch.Tensor,
63
- topk_weights: torch.Tensor,
64
- topk_ids: torch.Tensor,
65
- apply_router_weight_on_input: bool,
66
- ) -> None:
67
- """Apply router weight on input if needed."""
68
- if apply_router_weight_on_input:
69
- topk = topk_ids.size(1)
70
- assert topk == 1, (
71
- "apply_router_weight_on_input is only implemented for topk=1"
72
- )
73
- a1.mul_(topk_weights.to(a1.dtype))
74
-
75
-
76
-class FlashInferAllToAllMoEPrepareAndFinalize(FlashInferCutlassMoEPrepareAndFinalize):
77
- """FlashInfer implementation using AllToAll communication."""
78
-
79
- def __init__(
80
- self,
81
- use_dp: bool,
82
- num_dispatchers: int = 1,
83
- use_deepseek_fp8_block_scale: bool = False,
84
- ):
85
- super().__init__(use_dp, num_dispatchers, use_deepseek_fp8_block_scale)
86
- self.alltoall_info = None
87
-
88
- # Initialize all2all_manager only for DP case
89
- self.all2all_manager = None
90
- if self.use_dp:
91
- self.all2all_manager = get_ep_group().device_communicator.all2all_manager
92
-
93
- def prepare(
94
- self,
95
- a1: torch.Tensor,
96
- topk_weights: torch.Tensor,
97
- topk_ids: torch.Tensor,
98
- num_experts: int,
99
- expert_map: torch.Tensor | None,
100
- apply_router_weight_on_input: bool,
101
- quant_config: FusedMoEQuantConfig,
102
- ) -> mk.PrepareResultType:
103
- self._apply_router_weight_on_input(
104
- a1, topk_weights, topk_ids, apply_router_weight_on_input
105
- )
106
-
107
- if not self.use_dp:
108
- # Non-DP case: quantize activations unless using block-scale path
109
- if not self.use_deepseek_fp8_block_scale:
110
- a1q, a1q_scale = moe_kernel_quantize_input(
111
- a1,
112
- quant_config.a1_gscale,
113
- quant_config.quant_dtype,
114
- quant_config.per_act_token_quant,
115
- quant_config.block_shape,
116
- is_fp4_scale_swizzled=not self.use_dp,
117
- )
118
- else:
119
- a1q = a1
120
- a1q_scale = None
121
- else:
122
- # DP case: use FlashInfer AllToAll
123
- global_num_tokens_cpu = get_local_sizes()
124
- top_k = topk_ids.size(1)
125
-
126
- (self.alltoall_info, topk_ids, topk_weights, a1q, a1q_scale) = (
127
- flashinfer_alltoall_dispatch(
128
- self.all2all_manager,
129
- global_num_tokens_cpu,
130
- a1,
131
- quant_config.a1_gscale,
132
- topk_ids,
133
- topk_weights,
134
- top_k,
135
- num_experts,
136
- quant_config,
137
- use_deepseek_fp8_block_scale=self.use_deepseek_fp8_block_scale,
138
- )
139
- )
140
-
141
- return a1q, a1q_scale, None, topk_ids, topk_weights
142
-
143
- def finalize(
144
- self,
145
- output: torch.Tensor,
146
- fused_expert_output: torch.Tensor,
147
- topk_weights: torch.Tensor,
148
- topk_ids: torch.Tensor,
149
- apply_router_weight_on_input: bool,
150
- weight_and_reduce_impl: mk.TopKWeightAndReduce,
151
- ) -> None:
152
- if self.use_dp:
153
- top_k = topk_ids.size(1)
154
- token_count = output.shape[0]
155
- fused_expert_output = flashinfer_alltoall_combine(
156
- self.all2all_manager,
157
- fused_expert_output,
158
- top_k=top_k,
159
- token_count=token_count,
160
- alltoall_info=self.alltoall_info,
161
- )
162
- output.copy_(fused_expert_output)
163
-
164
-
165
-class FlashInferAllGatherMoEPrepareAndFinalize(FlashInferCutlassMoEPrepareAndFinalize):
166
- def __init__(
167
- self,
168
- use_dp: bool,
169
- num_dispatchers: int = 1,
170
- use_deepseek_fp8_block_scale: bool = False,
171
- ):
172
- super().__init__(use_dp, num_dispatchers, use_deepseek_fp8_block_scale)
173
-
174
- def prepare(
175
- self,
176
- a1: torch.Tensor,
177
- topk_weights: torch.Tensor,
178
- topk_ids: torch.Tensor,
179
- num_experts: int,
180
- expert_map: torch.Tensor | None,
181
- apply_router_weight_on_input: bool,
182
- quant_config: FusedMoEQuantConfig,
183
- ) -> mk.PrepareResultType:
184
- self._apply_router_weight_on_input(
185
- a1, topk_weights, topk_ids, apply_router_weight_on_input
186
- )
187
- is_nvfp4 = quant_config.quant_dtype == "nvfp4"
188
- if not self.use_dp and is_nvfp4:
189
- return a1, None, None, topk_ids, topk_weights
190
-
191
- if not self.use_deepseek_fp8_block_scale:
192
- a1q, a1q_scale = moe_kernel_quantize_input(
193
- a1,
194
- quant_config.a1_gscale if is_nvfp4 else quant_config.a1_scale,
195
- quant_config.quant_dtype,
196
- quant_config.per_act_token_quant,
197
- quant_config.block_shape,
198
- is_fp4_scale_swizzled=not self.use_dp,
199
- )
200
- else:
201
- # Block-scale path: pass activations through, omit per-token scales
202
- a1q = a1
203
- a1q_scale = None
204
-
205
- if self.use_dp:
206
- # Build gather list conditionally - omit a1q_scale if None
207
- # (block-scale path)
208
- gather_list = [topk_weights, topk_ids, a1q]
209
- if a1q_scale is not None:
210
- gather_list.append(a1q_scale)
211
- gathered = get_dp_group().all_gatherv(
212
- gather_list,
213
- dim=0,
214
- sizes=get_local_sizes(),
215
- )
216
- topk_weights, topk_ids, a1q, a1q_scale = gathered
217
- else:
218
- gathered = get_dp_group().all_gatherv(
219
- gather_list,
220
- dim=0,
221
- sizes=get_local_sizes(),
222
- )
223
- topk_weights, topk_ids, a1q = gathered
224
- a1q_scale = None
225
-
226
- if is_nvfp4 and a1q_scale is not None:
227
- if a1q_scale.element_size() == 1:
228
- a1q_scale = a1q_scale.view(torch.uint8)
229
- a1q_scale = nvfp4_block_scale_interleave(a1q_scale)
230
-
231
- return a1q, a1q_scale, None, topk_ids, topk_weights
232
-
233
- def finalize(
234
- self,
235
- output: torch.Tensor,
236
- fused_expert_output: torch.Tensor,
237
- topk_weights: torch.Tensor,
238
- topk_ids: torch.Tensor,
239
- apply_router_weight_on_input: bool,
240
- weight_and_reduce_impl: mk.TopKWeightAndReduce,
241
- ) -> None:
242
- assert isinstance(weight_and_reduce_impl, TopKWeightAndReduceNoOP)
243
-
244
- if self.use_dp:
245
- fused_expert_output = get_dp_group().reduce_scatterv(
246
- fused_expert_output, dim=0, sizes=get_local_sizes()
247
- )
248
- output.copy_(fused_expert_output)
249
-
250
-
251
-def flashinfer_alltoall_dispatch(
252
- all2all_manager: All2AllManagerBase,
253
- global_num_tokens_cpu: list[int],
254
- x: torch.Tensor,
255
- gs: torch.Tensor,
256
- topk_ids: torch.Tensor,
257
- topk_weights: torch.Tensor,
258
- top_k: int,
259
- num_experts: int,
260
- quant_config: FusedMoEQuantConfig,
261
- use_deepseek_fp8_block_scale: bool = False,
262
-):
263
- from flashinfer.comm.trtllm_alltoall import MnnvlMoe
264
-
265
- assert all2all_manager.ensure_alltoall_workspace_initialized(), (
266
- "FlashInfer AllToAll workspace not available"
267
- )
268
-
269
- ep_rank = all2all_manager.rank
270
- ep_size = all2all_manager.world_size
271
- max_num_token = (
272
- max(global_num_tokens_cpu) if global_num_tokens_cpu is not None else x.shape[0]
273
- )
274
- orig_topk_weights_dtype = topk_weights.dtype
275
- alltoall_info, topk_ids, topk_weights, _ = (
276
- MnnvlMoe.mnnvl_moe_alltoallv_prepare_without_allgather(
277
- topk_ids,
278
- topk_weights,
279
- None,
280
- all2all_manager.prepare_workspace_tensor,
281
- max_num_token,
282
- ep_rank,
283
- ep_size,
284
- num_experts,
285
- num_experts,
286
- top_k,
287
- )
288
- )
289
- topk_weights = topk_weights.view(dtype=orig_topk_weights_dtype)
290
-
291
- if not use_deepseek_fp8_block_scale:
292
- x, x_sf = moe_kernel_quantize_input(
293
- x,
294
- gs,
295
- quant_config.quant_dtype,
296
- quant_config.per_act_token_quant,
297
- quant_config.block_shape,
298
- is_fp4_scale_swizzled=False, # delay swizzle to after comm
299
- )
300
- x = MnnvlMoe.mnnvl_moe_alltoallv(
301
- x,
302
- alltoall_info,
303
- all2all_manager.workspace_tensor,
304
- ep_rank,
305
- ep_size,
306
- )
307
-
308
- x_sf = MnnvlMoe.mnnvl_moe_alltoallv(
309
- x_sf,
310
- alltoall_info,
311
- all2all_manager.workspace_tensor,
312
- ep_rank,
313
- ep_size,
314
- )
315
- if quant_config.quant_dtype == "nvfp4":
316
- x_sf = nvfp4_block_scale_interleave(x_sf)
317
- else:
318
- # Block-scale path: pass activations through without quantization
319
- x_sf = None
320
- x = MnnvlMoe.mnnvl_moe_alltoallv(
321
- x,
322
- alltoall_info,
323
- all2all_manager.workspace_tensor,
324
- ep_rank,
325
- ep_size,
326
- )
327
- return alltoall_info, topk_ids, topk_weights, x, x_sf
328
-
329
-
330
-def flashinfer_alltoall_combine(
331
- all2all_manager: All2AllManagerBase,
332
- output: torch.Tensor,
333
- top_k: int,
334
- token_count: int,
335
- alltoall_info,
336
-):
337
- from flashinfer.comm.trtllm_alltoall import MnnvlMoe
338
-
339
- assert all2all_manager.ensure_alltoall_workspace_initialized(), (
340
- "FlashInfer AllToAll workspace not available"
341
- )
342
- return MnnvlMoe.mnnvl_moe_alltoallv_combine(
343
- output,
344
- alltoall_info,
345
- all2all_manager.workspace_tensor,
346
- ep_rank=all2all_manager.rank,
347
- ep_size=all2all_manager.world_size,
348
- top_k=top_k,
349
- token_count=token_count,
350
- )
351
-
352
-
353
-def create_flashinfer_prepare_finalize(
354
- use_dp: bool,
355
- use_nvfp4: bool = False,
356
- enable_alltoallv: bool = False,
357
- use_deepseek_fp8_block_scale: bool = False,
358
-) -> FlashInferCutlassMoEPrepareAndFinalize | MoEPrepareAndFinalizeNoEP:
359
- """Factory function to create the appropriate FlashInfer implementation."""
360
-
361
- if use_dp:
362
- if enable_alltoallv:
363
- assert use_nvfp4
364
- return FlashInferAllToAllMoEPrepareAndFinalize(use_dp)
365
- return FlashInferAllGatherMoEPrepareAndFinalize(
366
- use_dp=True,
367
- use_deepseek_fp8_block_scale=use_deepseek_fp8_block_scale,
368
- )
369
- else:
370
- # CUTLASS FP8 BLOCK and CUTLASS NVFP4 apply input quantization
371
- # in a single call with the MoE experts kernel.
372
- defer_input_quant = use_deepseek_fp8_block_scale or use_nvfp4
373
- return MoEPrepareAndFinalizeNoEP(defer_input_quant=defer_input_quant)
vllm/model_executor/layers/fused_moe/fused_batched_moe.py
+6
@@ -533,7 +533,13 @@ class BatchedPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
533
expert_map: torch.Tensor | None,
534
apply_router_weight_on_input: bool,
535
quant_config: FusedMoEQuantConfig,
536
+ defer_input_quant: bool = False,
537
) -> mk.PrepareResultType:
538
+ if defer_input_quant:
539
+ raise NotImplementedError(
540
+ f"{self.__class__.__name__} does not support defer_input_quant=True. "
541
+ "Please select an MoE kernel that accepts quantized inputs."
542
+ )
543
assert a1.dim() == 2
544
assert topk_ids.dim() == 2
545
assert topk_ids.size(0) == a1.size(0)
vllm/model_executor/layers/fused_moe/fused_marlin_moe.py
+1
-1
@@ -593,7 +593,7 @@ class MarlinExpertsBase(mk.FusedMoEPermuteExpertsUnpermute):
593
594
@staticmethod
595
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
596
- return True
596
+ return not moe_parallel_config.use_fi_all2allv_kernels
597
598
@property
599
def quant_type_id(self) -> int:
vllm/model_executor/layers/fused_moe/fused_moe.py
+1
-1
@@ -1951,7 +1951,7 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1951
1952
@staticmethod
1953
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
1954
- return True
1954
+ return not moe_parallel_config.use_fi_all2allv_kernels
1955
1956
def supports_chunking(self) -> bool:
1957
return True
vllm/model_executor/layers/fused_moe/fused_moe_method_base.py
+16
@@ -5,6 +5,7 @@ from abc import abstractmethod
5
6
import torch
7
8
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.logger import init_logger
10
from vllm.model_executor.layers.fused_moe.config import (
11
FusedMoEConfig,
@@ -26,6 +27,19 @@ class FusedMoEMethodBase(QuantizeMethodBase):
27
super().__init__()
28
self.moe: FusedMoEConfig = moe
29
self.moe_quant_config: FusedMoEQuantConfig | None = None
30
+ self.moe_mk: mk.FusedMoEModularKernel | None = None
31
+
32
+ @property
33
+ def supports_internal_mk(self) -> bool:
34
+ # NOTE(rob): temporary attribute to indicate support for
35
+ # completed migration to the new internal MK interface.
36
+ return self.moe_mk is not None
37
+
38
+ @property
39
+ def mk_owns_shared_expert(self) -> bool:
40
+ # NOTE(rob): temporary attribute to indicate support for
41
+ # completed migration to the new internal MK interface.
42
+ return self.moe_mk is not None and self.moe_mk.shared_experts is not None
43
44
@abstractmethod
45
def create_weights(
@@ -91,6 +105,8 @@ class FusedMoEMethodBase(QuantizeMethodBase):
105
106
@property
107
def topk_indices_dtype(self) -> torch.dtype | None:
108
+ if self.moe_mk is not None:
109
+ return self.moe_mk.prepare_finalize.topk_indices_dtype()
110
return None
111
112
@property
vllm/model_executor/layers/fused_moe/fused_moe_modular_method.py
+4
-7
@@ -30,11 +30,11 @@ class FusedMoEModularMethod(FusedMoEMethodBase, CustomOp):
30
):
31
super().__init__(old_quant_method.moe)
32
self.moe_quant_config = old_quant_method.moe_quant_config
33
- self.fused_experts = experts
33
+ self.moe_mk = experts
34
self.disable_expert_map = getattr(
35
old_quant_method,
36
"disable_expert_map",
37
- not self.fused_experts.supports_expert_map(),
37
+ not self.moe_mk.supports_expert_map(),
38
)
39
self.old_quant_method = old_quant_method
40
assert not self.old_quant_method.is_monolithic
@@ -57,10 +57,6 @@ class FusedMoEModularMethod(FusedMoEMethodBase, CustomOp):
57
),
58
)
59
60
- @property
61
- def topk_indices_dtype(self) -> torch.dtype | None:
62
- return self.fused_experts.prepare_finalize.topk_indices_dtype()
63
-
60
@property
61
def supports_eplb(self) -> bool:
62
return self.old_quant_method.supports_eplb
@@ -96,7 +92,8 @@ class FusedMoEModularMethod(FusedMoEMethodBase, CustomOp):
92
topk_weights: torch.Tensor,
93
topk_ids: torch.Tensor,
94
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
99
- return self.fused_experts(
95
+ assert self.moe_mk is not None
96
+ return self.moe_mk(
97
hidden_states=x,
98
w1=layer.w13_weight,
99
w2=layer.w2_weight,
vllm/model_executor/layers/fused_moe/layer.py
+13
-17
@@ -571,9 +571,6 @@ class FusedMoE(CustomOp):
571
device=vllm_config.device_config.device,
572
routing_method=self.routing_method_type,
573
)
574
- self.moe_config_use_flashinfer_cutlass_kernels = (
575
- self.moe_config.use_flashinfer_cutlass_kernels
576
- )
574
if self.use_mori_kernels:
575
assert self.rocm_aiter_fmoe_enabled, (
576
"Mori needs to be used with aiter fused_moe for now."
@@ -646,6 +643,11 @@ class FusedMoE(CustomOp):
643
# This is called after all weight loading and post-processing, so it
644
# should be safe to swap out the quant_method.
645
def maybe_init_modular_kernel(self) -> None:
646
+ # NOTE(rob): WIP refactor. For quant methods that own the MK
647
+ # we create the MK during process_weights_after_loading.
648
+ if self.quant_method.supports_internal_mk or self.quant_method.is_monolithic:
649
+ return None
650
+
651
self.ensure_moe_quant_config_init()
652
# routing_tables only needed for round-robin expert placement with
653
# DeepEP all2all backend.
@@ -728,14 +730,6 @@ class FusedMoE(CustomOp):
730
def use_mori_kernels(self):
731
return self.moe_parallel_config.use_mori_kernels
732
731
- @property
732
- def use_flashinfer_cutlass_kernels(self):
733
- return (
734
- self.moe_quant_config is not None
735
- and self.moe_quant_config.quant_dtype == "nvfp4"
736
- and self.moe_config_use_flashinfer_cutlass_kernels
737
- )
738
-
733
@property
734
def use_marlin_kernels(self):
735
return getattr(self.quant_method, "use_marlin", False)
@@ -746,7 +740,7 @@ class FusedMoE(CustomOp):
740
self.moe_parallel_config.use_pplx_kernels
741
or self.moe_parallel_config.use_deepep_ll_kernels
742
or self.moe_parallel_config.use_mori_kernels
749
- or (self.dp_size > 1 and self.use_flashinfer_cutlass_kernels)
743
+ or self.moe_parallel_config.use_fi_all2allv_kernels
744
) and envs.VLLM_ENABLE_MOE_DP_CHUNK
745
746
@property
@@ -1532,7 +1526,7 @@ class FusedMoE(CustomOp):
1526
assert self.quant_method is not None
1527
return (
1528
isinstance(self.quant_method, FusedMoEModularMethod)
1535
- and self.quant_method.fused_experts.output_is_reduced()
1529
+ and self.quant_method.moe_mk.output_is_reduced() # type: ignore[union-attr]
1530
)
1531
1532
def maybe_all_reduce_tensor_model_parallel(self, final_hidden_states: torch.Tensor):
@@ -1765,7 +1759,7 @@ class FusedMoE(CustomOp):
1759
self.ensure_dp_chunking_init()
1760
1761
has_separate_shared_experts = (
1768
- not isinstance(self.quant_method, FusedMoEModularMethod)
1762
+ not self.quant_method.mk_owns_shared_expert
1763
and self.shared_experts is not None
1764
)
1765
@@ -1789,8 +1783,10 @@ class FusedMoE(CustomOp):
1783
hidden_states, router_logits, has_separate_shared_experts
1784
)
1785
1792
- do_naive_dispatch_combine: bool = self.dp_size > 1 and not isinstance(
1793
- self.quant_method, FusedMoEModularMethod
1786
+ # NOTE(rob): once we finish migrating all the quant methods to use
1787
+ # MKs, we can remove the naive dispatch/combine path from here.
1788
+ do_naive_dispatch_combine = (
1789
+ self.dp_size > 1 and not self.quant_method.supports_internal_mk
1790
)
1791
1792
ctx = get_forward_context()
@@ -1818,7 +1814,7 @@ class FusedMoE(CustomOp):
1814
else:
1815
hidden_states_to_dispatch = hidden_states
1816
1821
- dispatch_res = get_ep_group().dispatch(
1817
+ dispatch_res = get_ep_group().dispatch_router_logits(
1818
hidden_states_to_dispatch,
1819
router_logits,
1820
self.is_sequence_parallel,
vllm/model_executor/layers/fused_moe/modular_kernel.py
+12
-4
@@ -180,6 +180,7 @@ class FusedMoEPrepareAndFinalize(ABC):
180
expert_map: torch.Tensor | None,
181
apply_router_weight_on_input: bool,
182
quant_config: FusedMoEQuantConfig,
183
+ defer_input_quant: bool,
184
) -> PrepareResultType:
185
"""
186
Perform any quantization (and/or) dispatching needed for this kernel.
@@ -192,6 +193,9 @@ class FusedMoEPrepareAndFinalize(ABC):
193
- apply_router_weight_on_input: When True, apply the weights to the
194
activations, before quantization + dispatching.
195
- quant_config: Quantization info provided by the fused experts.
196
+ - defer_input_quant: Runtime parameter indicating whether or not to
197
+ defer input quantization to the FusedMoEPermuteExpertsUnpermute
198
+ in cases where the compute kernel expects unquantized inputs
199
200
Returns a tuple of:
201
- quantized + dispatched a.
@@ -220,6 +224,7 @@ class FusedMoEPrepareAndFinalize(ABC):
224
expert_map: torch.Tensor | None,
225
apply_router_weight_on_input: bool,
226
quant_config: FusedMoEQuantConfig,
227
+ defer_input_quant: bool,
228
) -> tuple[Callable, ReceiverType] | ReceiverType:
229
"""
230
Perform any quantization (and/or) dispatching needed for this kernel
@@ -235,6 +240,9 @@ class FusedMoEPrepareAndFinalize(ABC):
240
space to the local expert space of the expert parallel shard.
241
- apply_router_weight_on_input: When True, apply the weights to the
242
activations, before quantization + dispatching.
243
+ - defer_input_quant: Runtime parameter indicating whether or not to
244
+ defer input quantization to the FusedMoEPermuteExpertsUnpermute
245
+ in cases where the compute kernel expects unquantized inputs
246
247
Returns a callback or a hook callback pair that when invoked waits for
248
results from other workers and has the same return signature as
@@ -407,10 +415,8 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
415
self.max_num_tokens = max_num_tokens
416
self.num_dispatchers = num_dispatchers
417
410
- @staticmethod
411
- def expects_unquantized_inputs(
412
- moe_config: FusedMoEConfig, quant_config: FusedMoEQuantConfig
413
- ) -> bool:
418
+ @property
419
+ def expects_unquantized_inputs(self) -> bool:
420
"""
421
Whether or not the PrepareFinalize should defer input quantization
422
in the prepare step. If True, then the Experts kernel will
@@ -1069,6 +1075,7 @@ class FusedMoEModularKernel(torch.nn.Module):
1075
expert_map,
1076
apply_router_weight_on_input,
1077
self.fused_experts.quant_config,
1078
+ defer_input_quant=self.fused_experts.expects_unquantized_inputs,
1079
)
1080
else:
1081
# Overlap shared expert compute with all2all dispatch.
@@ -1081,6 +1088,7 @@ class FusedMoEModularKernel(torch.nn.Module):
1088
expert_map,
1089
apply_router_weight_on_input,
1090
self.fused_experts.quant_config,
1091
+ defer_input_quant=self.fused_experts.expects_unquantized_inputs,
1092
)
1093
1094
# TODO(lucas): refactor this in the alternative schedules followup
vllm/model_executor/layers/fused_moe/mori_prepare_finalize.py
+6
@@ -58,6 +58,7 @@ class MoriPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
58
expert_map: torch.Tensor | None,
59
apply_router_weight_on_input: bool,
60
quant_config: FusedMoEQuantConfig,
61
+ defer_input_quant: bool = False,
62
) -> mk.PrepareResultType:
63
"""
64
Returns a tuple of:
@@ -69,6 +70,11 @@ class MoriPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
70
- Optional dispatched expert topk IDs
71
- Optional dispatched expert topk weight
72
"""
73
+ if defer_input_quant:
74
+ raise NotImplementedError(
75
+ f"{self.__class__.__name__} does not support defer_input_quant=True. "
76
+ "Please select an MoE kernel that accepts quantized inputs."
77
+ )
78
assert not apply_router_weight_on_input, (
79
"mori does not support apply_router_weight_on_input=True now."
80
)
vllm/model_executor/layers/fused_moe/oracle/fp8.py
+31
-47
@@ -8,6 +8,9 @@ import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm import envs
9
from vllm._aiter_ops import rocm_aiter_ops
10
from vllm.logger import init_logger
11
+from vllm.model_executor.layers.fused_moe.all2all_utils import (
12
+ maybe_make_prepare_finalize,
13
+)
14
from vllm.model_executor.layers.fused_moe.config import (
15
FusedMoEConfig,
16
FusedMoEQuantConfig,
@@ -17,9 +20,6 @@ from vllm.model_executor.layers.fused_moe.config import (
20
from vllm.model_executor.layers.fused_moe.flashinfer_trtllm_moe import (
21
is_supported_config_trtllm,
22
)
20
-from vllm.model_executor.layers.fused_moe.prepare_finalize import (
21
- MoEPrepareAndFinalizeNoEP,
22
-)
23
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
24
FlashinferMoeBackend,
25
get_flashinfer_moe_backend,
@@ -465,68 +465,52 @@ def make_fp8_moe_quant_config(
465
)
466
467
468
-def make_fp8_moe_kernel_for_mkm(
468
+def make_fp8_moe_kernel(
469
+ moe_quant_config: FusedMoEQuantConfig,
470
moe_config: FusedMoEConfig,
470
- quant_config: FusedMoEQuantConfig,
471
experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
472
- prepare_finalize: mk.FusedMoEPrepareAndFinalize,
473
-) -> mk.FusedMoEPermuteExpertsUnpermute:
472
+ fp8_backend: Fp8MoeBackend,
473
+ routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
474
+ shared_experts: torch.nn.Module | None = None,
475
+) -> tuple[mk.FusedMoEModularKernel, bool]:
476
+ # Create Prepare/Finalize.
477
+ prepare_finalize = maybe_make_prepare_finalize(
478
+ moe=moe_config,
479
+ quant_config=moe_quant_config,
480
+ routing_tables=routing_tables,
481
+ allow_new_interface=True,
482
+ )
483
+ assert prepare_finalize is not None
484
+
485
+ logger.info_once("Using %s", prepare_finalize.__class__.__name__)
486
+
487
+ # Create Experts.
488
if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
475
- max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
476
- assert max_num_tokens_per_rank is not None
489
+ max_num_tokens = prepare_finalize.max_num_tokens_per_rank()
490
+ assert max_num_tokens is not None
491
experts = experts_cls(
492
moe_config=moe_config,
479
- quant_config=quant_config,
480
- max_num_tokens=max_num_tokens_per_rank,
493
+ quant_config=moe_quant_config,
494
+ max_num_tokens=max_num_tokens,
495
num_dispatchers=prepare_finalize.num_dispatchers(),
496
)
497
else:
498
experts = experts_cls(
499
moe_config=moe_config,
486
- quant_config=quant_config,
500
+ quant_config=moe_quant_config,
501
)
502
489
- logger.debug_once("Using %s", experts.__class__.__name__)
490
- return experts
491
-
492
-
493
-def make_fp8_moe_kernel(
494
- moe_quant_config: FusedMoEQuantConfig,
495
- moe_config: FusedMoEConfig,
496
- fp8_backend: Fp8MoeBackend,
497
- experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
498
-) -> tuple[mk.FusedMoEModularKernel, bool]:
499
- # TODO(rob): unify after we merge tp and dp/ep.
500
- if (
501
- moe_config.moe_parallel_config.use_all2all_kernels
502
- and moe_config.moe_parallel_config.all2all_backend
503
- not in ["allgather_reducescatter", "naive"]
504
- ):
505
- raise ValueError(
506
- "Fp8 Oracle should not create non-naive A2A P/F. "
507
- "This should happen via the ModularKernelMethod."
508
- )
509
-
510
- # Create Prepare/Finalize.
511
- prepare_finalize = MoEPrepareAndFinalizeNoEP(
512
- defer_input_quant=experts_cls.expects_unquantized_inputs(
513
- moe_config, moe_quant_config
514
- ),
515
- )
516
-
517
- # Create Experts.
518
- experts = experts_cls(
519
- moe_config=moe_config,
520
- quant_config=moe_quant_config,
521
- )
522
-
503
# NOTE(rob): we only want the mk to control the shared_expert
504
# if using all2all (for SBO). bnell is making this explict in
505
# the new MoE runner class.
506
kernel = mk.FusedMoEModularKernel(
507
prepare_finalize,
508
experts,
529
- shared_experts=None,
509
+ shared_experts=(
510
+ shared_experts
511
+ if moe_config.moe_parallel_config.use_all2all_kernels
512
+ else None
513
+ ),
514
moe_parallel_config=moe_config.moe_parallel_config,
515
)
516
vllm/model_executor/layers/fused_moe/oracle/nvfp4.py
+30
-46
@@ -7,6 +7,9 @@ import torch
7
import vllm.envs as envs
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.logger import init_logger
10
+from vllm.model_executor.layers.fused_moe.all2all_utils import (
11
+ maybe_make_prepare_finalize,
12
+)
13
from vllm.model_executor.layers.fused_moe.config import (
14
FusedMoEConfig,
15
FusedMoEQuantConfig,
@@ -14,9 +17,6 @@ from vllm.model_executor.layers.fused_moe.config import (
17
nvfp4_moe_quant_config,
18
nvfp4_w4a16_moe_quant_config,
19
)
17
-from vllm.model_executor.layers.fused_moe.prepare_finalize import (
18
- MoEPrepareAndFinalizeNoEP,
19
-)
20
from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
21
is_supported_config_trtllm,
22
prepare_nvfp4_moe_layer_for_fi_or_cutlass,
@@ -391,67 +391,51 @@ def make_nvfp4_moe_quant_config(
391
)
392
393
394
-def make_nvfp4_moe_kernel_for_mkm(
394
+def make_nvfp4_moe_kernel(
395
+ moe_quant_config: FusedMoEQuantConfig,
396
moe_config: FusedMoEConfig,
396
- quant_config: FusedMoEQuantConfig,
397
experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
398
- prepare_finalize: mk.FusedMoEPrepareAndFinalize,
399
-) -> mk.FusedMoEPermuteExpertsUnpermute:
398
+ routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
399
+ shared_experts: torch.nn.Module | None = None,
400
+) -> mk.FusedMoEModularKernel:
401
+ # Create Prepare/Finalize.
402
+ prepare_finalize = maybe_make_prepare_finalize(
403
+ moe=moe_config,
404
+ quant_config=moe_quant_config,
405
+ routing_tables=routing_tables,
406
+ allow_new_interface=True,
407
+ )
408
+ assert prepare_finalize is not None
409
+
410
+ logger.info_once("Using %s", prepare_finalize.__class__.__name__)
411
+
412
+ # Create Experts.
413
if prepare_finalize.activation_format == mk.FusedMoEActivationFormat.BatchedExperts:
401
- max_num_tokens_per_rank = prepare_finalize.max_num_tokens_per_rank()
402
- assert max_num_tokens_per_rank is not None
414
+ max_num_tokens = prepare_finalize.max_num_tokens_per_rank()
415
+ assert max_num_tokens is not None
416
experts = experts_cls(
417
moe_config=moe_config,
405
- quant_config=quant_config,
406
- max_num_tokens=max_num_tokens_per_rank,
418
+ quant_config=moe_quant_config,
419
+ max_num_tokens=max_num_tokens,
420
num_dispatchers=prepare_finalize.num_dispatchers(),
421
)
422
else:
423
experts = experts_cls(
424
moe_config=moe_config,
412
- quant_config=quant_config,
425
+ quant_config=moe_quant_config,
426
)
427
415
- logger.debug_once("Using %s", experts.__class__.__name__)
416
- return experts
417
-
418
-
419
-def make_nvfp4_moe_kernel(
420
- moe_quant_config: FusedMoEQuantConfig,
421
- moe_config: FusedMoEConfig,
422
- experts_cls: type[mk.FusedMoEPermuteExpertsUnpermute],
423
-) -> mk.FusedMoEModularKernel:
424
- # TODO(rob): unify after we merge tp and dp/ep.
425
- if (
426
- moe_config.moe_parallel_config.use_all2all_kernels
427
- and moe_config.moe_parallel_config.all2all_backend
428
- not in ["allgather_reducescatter", "naive"]
429
- ):
430
- raise ValueError(
431
- "NvFP4 Oracle should not create non-naive A2A P/F. "
432
- "This should happen via the ModularKernelMethod."
433
- )
434
-
435
- # Create Prepare/Finalize.
436
- prepare_finalize = MoEPrepareAndFinalizeNoEP(
437
- defer_input_quant=experts_cls.expects_unquantized_inputs(
438
- moe_config, moe_quant_config
439
- ),
440
- )
441
-
442
- # Create Experts.
443
- experts = experts_cls(
444
- moe_config=moe_config,
445
- quant_config=moe_quant_config,
446
- )
447
-
428
# NOTE(rob): we only want the mk to control the shared_expert
429
# if using all2all (for SBO). bnell is making this explict in
430
# the new MoE runner class.
431
kernel = mk.FusedMoEModularKernel(
432
prepare_finalize,
433
experts,
454
- shared_experts=None,
434
+ shared_experts=(
435
+ shared_experts
436
+ if moe_config.moe_parallel_config.use_all2all_kernels
437
+ else None
438
+ ),
439
moe_parallel_config=moe_config.moe_parallel_config,
440
)
441
vllm/model_executor/layers/fused_moe/pplx_prepare_finalize.py
+9
@@ -106,7 +106,14 @@ class PplxPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
106
expert_map: torch.Tensor | None,
107
apply_router_weight_on_input: bool,
108
quant_config: FusedMoEQuantConfig,
109
+ defer_input_quant: bool = False,
110
) -> tuple[Callable, mk.ReceiverType]:
111
+ if defer_input_quant:
112
+ raise NotImplementedError(
113
+ f"{self.__class__.__name__} does not support defer_input_quant=True. "
114
+ "Please select an MoE kernel that accepts quantized inputs."
115
+ )
116
+
117
num_tokens = a1.size(0) # M
118
hidden_dim = a1.size(-1) # K
119
@@ -274,6 +281,7 @@ class PplxPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
281
expert_map: torch.Tensor | None,
282
apply_router_weight_on_input: bool,
283
quant_config: FusedMoEQuantConfig,
284
+ defer_input_quant: bool = False,
285
) -> mk.PrepareResultType:
286
hook, receiver = self.prepare_async(
287
a1,
@@ -283,6 +291,7 @@ class PplxPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
291
expert_map,
292
apply_router_weight_on_input,
293
quant_config,
294
+ defer_input_quant=defer_input_quant,
295
)
296
hook()
297
return receiver()
vllm/model_executor/layers/fused_moe/prepare_finalize.py
+125
-5
@@ -4,18 +4,25 @@
4
import torch
5
6
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
+from vllm.distributed import get_ep_group
8
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
9
from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
10
TopKWeightAndReduceContiguous,
11
TopKWeightAndReduceDelegate,
12
)
13
from vllm.model_executor.layers.fused_moe.utils import moe_kernel_quantize_input
14
+from vllm.utils.flashinfer import nvfp4_block_scale_interleave
15
16
15
-class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
16
- def __init__(self, defer_input_quant: bool = False) -> None:
17
+class MoEPrepareAndFinalizeNaiveEP(mk.FusedMoEPrepareAndFinalize):
18
+ def __init__(
19
+ self,
20
+ is_sequence_parallel: bool = False,
21
+ num_dispatchers: int = 1,
22
+ ) -> None:
23
super().__init__()
18
- self.defer_input_quant = defer_input_quant
24
+ self.is_sequence_parallel = is_sequence_parallel
25
+ self._num_dispatchers = num_dispatchers
26
27
@property
28
def activation_format(self) -> mk.FusedMoEActivationFormat:
@@ -27,6 +34,113 @@ class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
34
def topk_indices_dtype(self) -> torch.dtype | None:
35
return None
36
37
+ def num_dispatchers(self) -> int:
38
+ return self._num_dispatchers
39
+
40
+ def output_is_reduced(self) -> bool:
41
+ return False
42
+
43
+ def prepare(
44
+ self,
45
+ a1: torch.Tensor,
46
+ topk_weights: torch.Tensor,
47
+ topk_ids: torch.Tensor,
48
+ num_experts: int,
49
+ expert_map: torch.Tensor | None,
50
+ apply_router_weight_on_input: bool,
51
+ quant_config: FusedMoEQuantConfig,
52
+ defer_input_quant: bool = False,
53
+ ) -> mk.PrepareResultType:
54
+ if apply_router_weight_on_input:
55
+ topk = topk_ids.size(1)
56
+ assert topk == 1, (
57
+ "apply_router_weight_on_input is only implemented for topk=1"
58
+ )
59
+ # Note: do not use inplace for shared experts overlap
60
+ a1 = a1 * topk_weights.to(a1.dtype)
61
+
62
+ # Defer input quantization to the MoE kernel.
63
+ use_nvfp4 = quant_config.use_nvfp4_w4a4
64
+ if defer_input_quant:
65
+ a1q = a1
66
+ a1q_scale = None
67
+ else:
68
+ a1q, a1q_scale = moe_kernel_quantize_input(
69
+ a1,
70
+ quant_config.a1_gscale if use_nvfp4 else quant_config.a1_scale,
71
+ quant_config.quant_dtype,
72
+ quant_config.per_act_token_quant,
73
+ quant_config.block_shape,
74
+ # NOTE: swizzling pads the scales to multiple of 128
75
+ # which makes the scales tensor different shape than
76
+ # the hidden states, breaking the A2A kernel. So, we
77
+ # delay the swizzling until after the A2A.
78
+ is_fp4_scale_swizzled=False,
79
+ )
80
+
81
+ # Skip gathering scales if we have static quantization
82
+ # (the scale is a scalar, replicated on all ranks) or
83
+ # if quantization is deferred.
84
+ skip_gather_scales = a1q_scale is None or a1q_scale.ndim == 0
85
+ scales = None if skip_gather_scales else [a1q_scale]
86
+
87
+ res = get_ep_group().dispatch(
88
+ a1q,
89
+ topk_weights,
90
+ topk_ids,
91
+ is_sequence_parallel=self.is_sequence_parallel,
92
+ extra_tensors=scales,
93
+ )
94
+ if skip_gather_scales:
95
+ a1q, topk_weights, topk_ids = res
96
+ else:
97
+ a1q, topk_weights, topk_ids, scales = res
98
+ assert scales is not None and len(scales) == 1
99
+ a1q_scale = scales[0]
100
+ if quant_config.quant_dtype == "nvfp4":
101
+ assert a1q_scale is not None
102
+ if a1q_scale.element_size() == 1:
103
+ a1q_scale = a1q_scale.view(torch.uint8)
104
+ a1q_scale = nvfp4_block_scale_interleave(a1q_scale)
105
+
106
+ return a1q, a1q_scale, None, topk_ids, topk_weights
107
+
108
+ def finalize(
109
+ self,
110
+ output: torch.Tensor,
111
+ fused_expert_output: torch.Tensor,
112
+ topk_weights: torch.Tensor,
113
+ topk_ids: torch.Tensor,
114
+ apply_router_weight_on_input: bool,
115
+ weight_and_reduce_impl: mk.TopKWeightAndReduce,
116
+ ) -> None:
117
+ if isinstance(weight_and_reduce_impl, TopKWeightAndReduceDelegate):
118
+ weight_and_reduce_impl = TopKWeightAndReduceContiguous()
119
+
120
+ out = weight_and_reduce_impl.apply(
121
+ output=None,
122
+ fused_expert_output=fused_expert_output,
123
+ topk_weights=topk_weights,
124
+ topk_ids=topk_ids,
125
+ apply_router_weight_on_input=apply_router_weight_on_input,
126
+ )
127
+
128
+ output.copy_(
129
+ get_ep_group().combine(out, is_sequence_parallel=self.is_sequence_parallel)
130
+ )
131
+
132
+
133
+class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
134
+ @property
135
+ def activation_format(self) -> mk.FusedMoEActivationFormat:
136
+ return mk.FusedMoEActivationFormat.Standard
137
+
138
+ def max_num_tokens_per_rank(self) -> int | None:
139
+ return None
140
+
141
+ def topk_indices_dtype(self) -> torch.dtype | None:
142
+ return None
143
+
144
def num_dispatchers(self) -> int:
145
return 1
146
@@ -42,6 +156,7 @@ class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
156
expert_map: torch.Tensor | None,
157
apply_router_weight_on_input: bool,
158
quant_config: FusedMoEQuantConfig,
159
+ defer_input_quant: bool = False,
160
) -> mk.PrepareResultType:
161
if apply_router_weight_on_input:
162
topk = topk_ids.size(1)
@@ -54,12 +169,17 @@ class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
169
170
# Defer input quant to moe kernel for backends (e.g. AITER, FI)
171
# which use a single kernel call for quant + experts.
57
- if self.defer_input_quant:
172
+ if defer_input_quant:
173
return a1, None, None, None, None
174
175
+ input_sf = (
176
+ quant_config.a1_gscale
177
+ if quant_config.use_nvfp4_w4a4
178
+ else quant_config.a1_scale
179
+ )
180
a1q, a1q_scale = moe_kernel_quantize_input(
181
a1,
62
- quant_config.a1_scale,
182
+ input_sf,
183
quant_config.quant_dtype,
184
quant_config.per_act_token_quant,
185
quant_config.block_shape,
vllm/model_executor/layers/fused_moe/rocm_aiter_fused_moe.py
+5
-8
@@ -287,17 +287,14 @@ def rocm_aiter_fused_experts(
287
288
289
class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
290
+ @property
291
+ def expects_unquantized_inputs(self) -> bool:
292
+ return True
293
+
294
@staticmethod
295
def activation_format() -> mk.FusedMoEActivationFormat:
296
return mk.FusedMoEActivationFormat.Standard
297
294
- @staticmethod
295
- def expects_unquantized_inputs(
296
- fused_moe_config: mk.FusedMoEConfig, quant_config: FusedMoEQuantConfig
297
- ) -> bool:
298
- # AITER fused MoE kernels handle input quantization internally.
299
- return True
300
-
298
@staticmethod
299
def _supports_current_device() -> bool:
300
return rocm_aiter_ops.is_fused_moe_enabled()
@@ -329,7 +326,7 @@ class AiterExperts(mk.FusedMoEPermuteExpertsUnpermute):
326
327
@staticmethod
328
def _supports_parallel_config(moe_parallel_config: FusedMoEParallelConfig) -> bool:
332
- return True
329
+ return not moe_parallel_config.use_fi_all2allv_kernels
330
331
def supports_expert_map(self):
332
return True
vllm/model_executor/layers/fused_moe/shared_fused_moe.py
+1
-1
@@ -37,7 +37,7 @@ class SharedFusedMoE(FusedMoE):
37
use_overlapped
38
and not (
39
(self.enable_eplb and backend != "allgather_reducescatter")
40
- or (self.moe_config.use_flashinfer_cutlass_kernels and self.dp_size > 1)
40
+ or self.moe_parallel_config.use_fi_all2allv_kernels
41
)
42
and self._shared_experts is not None
43
)
vllm/model_executor/layers/quantization/compressed_tensors/compressed_tensors_moe.py
+35
-85
@@ -43,7 +43,6 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
43
Fp8MoeBackend,
44
convert_to_fp8_moe_kernel_format,
45
make_fp8_moe_kernel,
46
- make_fp8_moe_kernel_for_mkm,
46
make_fp8_moe_quant_config,
47
select_fp8_moe_backend,
48
)
@@ -53,7 +52,6 @@ from vllm.model_executor.layers.fused_moe.oracle.nvfp4 import (
52
is_global_sf_supported_for_nvfp4_backend,
53
make_mxfp4_moe_quant_config,
54
make_nvfp4_moe_kernel,
56
- make_nvfp4_moe_kernel_for_mkm,
55
make_nvfp4_moe_quant_config,
56
select_nvfp4_moe_backend,
57
)
@@ -67,7 +65,6 @@ from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
65
)
66
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
67
apply_fi_trtllm_fp8_per_tensor_moe,
70
- build_flashinfer_fp8_cutlass_moe_prepare_finalize,
68
)
69
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
70
process_fp8_input_tensor_strategy_moe,
@@ -243,7 +240,6 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
240
self.group_size = 32
241
self.mxfp4_backend = NvFp4MoeBackend.MARLIN
242
self.experts_cls = MarlinExperts
246
- self.kernel: mk.FusedMoEModularKernel | None = None
243
244
def create_weights(
245
self,
@@ -320,7 +316,7 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
316
w13_scale=layer.w13_weight_scale, w2_scale=layer.w2_weight_scale
317
)
318
323
- def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
319
+ def process_weights_after_loading(self, layer: FusedMoE) -> None:
320
layer.w13_weight = torch.nn.Parameter(
321
layer.w13_weight_packed.data, requires_grad=False
322
)
@@ -335,10 +331,12 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
331
332
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
333
if self.moe_quant_config is not None:
338
- self.kernel = make_nvfp4_moe_kernel(
334
+ self.moe_mk = make_nvfp4_moe_kernel(
335
moe_quant_config=self.moe_quant_config,
336
moe_config=self.moe,
337
experts_cls=self.experts_cls,
338
+ shared_experts=layer.shared_experts,
339
+ routing_tables=layer._maybe_init_expert_routing_tables(),
340
)
341
342
def apply(
@@ -348,8 +346,8 @@ class CompressedTensorsW4A4Mxfp4MoEMethod(CompressedTensorsMoEMethod):
346
topk_weights: torch.Tensor,
347
topk_ids: torch.Tensor,
348
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
351
- assert self.kernel is not None
352
- return self.kernel(
349
+ assert self.moe_mk is not None
350
+ return self.moe_mk(
351
x,
352
layer.w13_weight,
353
layer.w2_weight,
@@ -380,19 +378,10 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
378
activation_key=None if use_a16 else kNvfp4Dynamic,
379
)
380
383
- # Delay creation of the kernel until after process-weights.
384
- self.kernel: mk.FusedMoEModularKernel | None = None
385
-
381
self.use_global_sf = is_global_sf_supported_for_nvfp4_backend(
382
self.nvfp4_backend
383
)
384
390
- @property
391
- def topk_indices_dtype(self) -> torch.dtype | None:
392
- if self.kernel is not None:
393
- return self.kernel.prepare_finalize.topk_indices_dtype()
394
- return None
395
-
385
def create_weights(
386
self,
387
layer: torch.nn.Module,
@@ -506,7 +495,7 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
495
)
496
set_weight_attrs(w2_input_scale, extra_weight_attrs)
497
509
- def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
498
+ def process_weights_after_loading(self, layer: FusedMoE) -> None:
499
"""
500
Convert NVFP4 MoE weights into kernel format and setup the kernel.
501
"""
@@ -572,48 +561,33 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
561
# TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
562
# in both cases.
563
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
575
- if self.moe_quant_config and (
576
- (not self.moe.moe_parallel_config.use_all2all_kernels)
577
- or self.moe.moe_parallel_config.use_naive_all2all_kernels
578
- ):
564
+ if self.moe_quant_config:
565
assert self.experts_cls is not None
580
- self.kernel = make_nvfp4_moe_kernel(
566
+ self.moe_mk = make_nvfp4_moe_kernel(
567
moe_quant_config=self.moe_quant_config,
568
moe_config=self.moe,
569
experts_cls=self.experts_cls,
570
+ shared_experts=layer.shared_experts,
571
+ routing_tables=layer._maybe_init_expert_routing_tables(),
572
)
573
574
def maybe_make_prepare_finalize(
575
self,
576
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
577
) -> mk.FusedMoEPrepareAndFinalize | None:
590
- if self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
591
- return None
592
- elif self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
593
- # For no-EP case, don't use the MKM framework.
594
- if not self.moe.moe_parallel_config.use_all2all_kernels:
595
- return None
596
-
597
- prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
598
- self.moe,
599
- use_deepseek_fp8_block_scale=False,
600
- )
601
- logger.debug_once("%s", prepare_finalize.__class__.__name__)
602
- return prepare_finalize
603
- return super().maybe_make_prepare_finalize(routing_tables)
578
+ raise ValueError(
579
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
580
+ "logic. This function should not be called."
581
+ )
582
583
def select_gemm_impl(
584
self,
585
prepare_finalize: mk.FusedMoEPrepareAndFinalize,
586
layer: torch.nn.Module,
587
) -> mk.FusedMoEPermuteExpertsUnpermute:
610
- assert self.moe_quant_config is not None
611
- assert self.experts_cls is not None
612
- return make_nvfp4_moe_kernel_for_mkm(
613
- moe_config=self.moe,
614
- quant_config=self.moe_quant_config,
615
- experts_cls=self.experts_cls,
616
- prepare_finalize=prepare_finalize,
588
+ raise ValueError(
589
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
590
+ "logic. This function should not be called."
591
)
592
593
def get_fused_moe_quant_config(
@@ -684,8 +658,8 @@ class CompressedTensorsW4A4Nvfp4MoEMethod(CompressedTensorsMoEMethod):
658
global_num_experts=layer.global_num_experts,
659
)
660
else:
687
- assert self.kernel is not None
688
- return self.kernel(
661
+ assert self.moe_mk is not None
662
+ return self.moe_mk(
663
x,
664
layer.w13_weight,
665
layer.w2_weight,
@@ -759,15 +733,6 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
733
allow_vllm_cutlass=True,
734
)
735
762
- # Delay creation of the kernel until after process-weights.
763
- self.kernel: mk.FusedMoEModularKernel | None = None
764
-
765
- @property
766
- def topk_indices_dtype(self) -> torch.dtype | None:
767
- if self.kernel is not None:
768
- return self.kernel.prepare_finalize.topk_indices_dtype()
769
- return None
770
-
736
def create_weights(
737
self,
738
layer: torch.nn.Module,
@@ -927,7 +892,7 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
892
layer.w13_input_scale = None
893
layer.w2_input_scale = None
894
930
- def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
895
+ def process_weights_after_loading(self, layer: FusedMoE) -> None:
896
# Allow for accessing weights and scales in standard way.
897
w13 = layer.w13_weight
898
w2 = layer.w2_weight
@@ -989,49 +954,34 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
954
# TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
955
# in both cases.
956
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
992
- if self.moe_quant_config and (
993
- (not self.moe.moe_parallel_config.use_all2all_kernels)
994
- or self.moe.moe_parallel_config.use_naive_all2all_kernels
995
- ):
957
+ if self.moe_quant_config:
958
assert self.experts_cls is not None
997
- self.kernel, self.use_inplace = make_fp8_moe_kernel(
959
+ self.moe_mk, self.use_inplace = make_fp8_moe_kernel(
960
moe_quant_config=self.moe_quant_config,
961
moe_config=self.moe,
962
fp8_backend=self.fp8_backend,
963
experts_cls=self.experts_cls,
964
+ routing_tables=layer._maybe_init_expert_routing_tables(),
965
+ shared_experts=layer.shared_experts,
966
)
967
968
def maybe_make_prepare_finalize(
969
self,
970
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
971
) -> mk.FusedMoEPrepareAndFinalize | None:
1008
- if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
1009
- return None
1010
- elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
1011
- # For no-EP case, don't use the MKM framework.
1012
- if not self.moe.moe_parallel_config.use_all2all_kernels:
1013
- return None
1014
-
1015
- prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
1016
- self.moe,
1017
- use_deepseek_fp8_block_scale=self.block_quant,
1018
- )
1019
- logger.debug_once("%s", prepare_finalize.__class__.__name__)
1020
- return prepare_finalize
1021
- return super().maybe_make_prepare_finalize(routing_tables)
972
+ raise ValueError(
973
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
974
+ "logic. This function should not be called."
975
+ )
976
977
def select_gemm_impl(
978
self,
979
prepare_finalize: mk.FusedMoEPrepareAndFinalize,
980
layer: torch.nn.Module,
1027
- ) -> FusedMoEPermuteExpertsUnpermute:
1028
- assert self.moe_quant_config is not None
1029
- assert self.experts_cls is not None
1030
- return make_fp8_moe_kernel_for_mkm(
1031
- moe_config=self.moe,
1032
- quant_config=self.moe_quant_config,
1033
- experts_cls=self.experts_cls,
1034
- prepare_finalize=prepare_finalize,
981
+ ) -> mk.FusedMoEPermuteExpertsUnpermute:
982
+ raise ValueError(
983
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
984
+ "logic. This function should not be called."
985
)
986
987
def get_fused_moe_quant_config(
@@ -1120,8 +1070,8 @@ class CompressedTensorsW8A8Fp8MoEMethod(CompressedTensorsMoEMethod):
1070
topk_ids: torch.Tensor,
1071
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1072
assert not self.is_monolithic
1123
- assert self.kernel is not None
1124
- return self.kernel(
1073
+ assert self.moe_mk is not None
1074
+ return self.moe_mk(
1075
x,
1076
layer.w13_weight,
1077
layer.w2_weight,
vllm/model_executor/layers/quantization/fp8.py
+14
-40
@@ -33,7 +33,6 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
33
Fp8MoeBackend,
34
convert_to_fp8_moe_kernel_format,
35
make_fp8_moe_kernel,
36
- make_fp8_moe_kernel_for_mkm,
36
make_fp8_moe_quant_config,
37
select_fp8_moe_backend,
38
)
@@ -53,7 +52,6 @@ from vllm.model_executor.layers.quantization.kernels.scaled_mm import (
52
from vllm.model_executor.layers.quantization.kv_cache import BaseKVCacheMethod
53
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
54
apply_fi_trtllm_fp8_per_tensor_moe,
56
- build_flashinfer_fp8_cutlass_moe_prepare_finalize,
55
)
56
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
57
W8A8BlockFp8LinearOp,
@@ -679,15 +677,6 @@ class Fp8MoEMethod(FusedMoEMethodBase):
677
allow_vllm_cutlass=False,
678
)
679
682
- # Delay creation of the kernel until after process-weights.
683
- self.kernel: mk.FusedMoEModularKernel | None = None
684
-
685
- @property
686
- def topk_indices_dtype(self) -> torch.dtype | None:
687
- if self.kernel is not None:
688
- return self.kernel.prepare_finalize.topk_indices_dtype()
689
- return None
690
-
680
def create_weights(
681
self,
682
layer: Module,
@@ -813,7 +802,7 @@ class Fp8MoEMethod(FusedMoEMethodBase):
802
803
def _setup_kernel(
804
self,
816
- layer: Module,
805
+ layer: FusedMoE,
806
w13: torch.Tensor,
807
w2: torch.Tensor,
808
w13_scale: torch.Tensor,
@@ -845,16 +834,15 @@ class Fp8MoEMethod(FusedMoEMethodBase):
834
# TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
835
# in both cases.
836
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
848
- if self.moe_quant_config and (
849
- (not self.moe.moe_parallel_config.use_all2all_kernels)
850
- or self.moe.moe_parallel_config.use_naive_all2all_kernels
851
- ):
837
+ if self.moe_quant_config:
838
assert self.experts_cls is not None
853
- self.kernel, self.use_inplace = make_fp8_moe_kernel(
839
+ self.moe_mk, self.use_inplace = make_fp8_moe_kernel(
840
moe_quant_config=self.moe_quant_config,
841
moe_config=self.moe,
842
fp8_backend=self.fp8_backend,
843
experts_cls=self.experts_cls,
844
+ routing_tables=layer._maybe_init_expert_routing_tables(),
845
+ shared_experts=layer.shared_experts,
846
)
847
848
def process_weights_after_loading(self, layer: Module) -> None:
@@ -909,33 +897,19 @@ class Fp8MoEMethod(FusedMoEMethodBase):
897
self,
898
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
899
) -> mk.FusedMoEPrepareAndFinalize | None:
912
- if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
913
- return None
914
- elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
915
- # For no-EP case, don't use the MKM framework.
916
- if not self.moe.moe_parallel_config.use_all2all_kernels:
917
- return None
918
-
919
- prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
920
- self.moe,
921
- use_deepseek_fp8_block_scale=self.block_quant,
922
- )
923
- logger.debug_once("%s", prepare_finalize.__class__.__name__)
924
- return prepare_finalize
925
- return super().maybe_make_prepare_finalize(routing_tables)
900
+ raise ValueError(
901
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
902
+ "logic. This function should not be called."
903
+ )
904
905
def select_gemm_impl(
906
self,
907
prepare_finalize: FusedMoEPrepareAndFinalize,
908
layer: torch.nn.Module,
909
) -> FusedMoEPermuteExpertsUnpermute:
932
- assert self.moe_quant_config is not None
933
- assert self.experts_cls is not None
934
- return make_fp8_moe_kernel_for_mkm(
935
- moe_config=self.moe,
936
- quant_config=self.moe_quant_config,
937
- experts_cls=self.experts_cls,
938
- prepare_finalize=prepare_finalize,
910
+ raise ValueError(
911
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
912
+ "logic. This function should not be called."
913
)
914
915
def get_fused_moe_quant_config(
@@ -1037,9 +1011,9 @@ class Fp8MoEMethod(FusedMoEMethodBase):
1011
topk_weights: torch.Tensor,
1012
topk_ids: torch.Tensor,
1013
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1040
- assert self.kernel is not None
1014
+ assert self.moe_mk is not None
1015
assert not self.is_monolithic
1042
- return self.kernel(
1016
+ return self.moe_mk(
1017
x,
1018
layer.w13_weight,
1019
layer.w2_weight,
vllm/model_executor/layers/quantization/modelopt.py
+27
-77
@@ -26,7 +26,6 @@ from vllm.model_executor.layers.fused_moe.oracle.fp8 import (
26
Fp8MoeBackend,
27
convert_to_fp8_moe_kernel_format,
28
make_fp8_moe_kernel,
29
- make_fp8_moe_kernel_for_mkm,
29
make_fp8_moe_quant_config,
30
select_fp8_moe_backend,
31
)
@@ -35,7 +34,6 @@ from vllm.model_executor.layers.fused_moe.oracle.nvfp4 import (
34
convert_to_nvfp4_moe_kernel_format,
35
is_global_sf_supported_for_nvfp4_backend,
36
make_nvfp4_moe_kernel,
38
- make_nvfp4_moe_kernel_for_mkm,
37
make_nvfp4_moe_quant_config,
38
select_nvfp4_moe_backend,
39
)
@@ -54,13 +52,11 @@ from vllm.model_executor.layers.quantization.kernels.scaled_mm import (
52
)
53
from vllm.model_executor.layers.quantization.kv_cache import BaseKVCacheMethod
54
from vllm.model_executor.layers.quantization.utils.flashinfer_fp4_moe import (
57
- build_flashinfer_fp4_cutlass_moe_prepare_finalize,
55
flashinfer_trtllm_fp4_moe,
56
flashinfer_trtllm_fp4_routed_moe,
57
)
58
from vllm.model_executor.layers.quantization.utils.flashinfer_utils import (
59
apply_fi_trtllm_fp8_per_tensor_moe,
63
- build_flashinfer_fp8_cutlass_moe_prepare_finalize,
60
)
61
from vllm.model_executor.layers.quantization.utils.fp8_utils import (
62
W8A8BlockFp8LinearOp,
@@ -739,47 +735,23 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
735
activation_key=kFp8StaticTensorSym,
736
)
737
742
- # Delay creation of the kernel until after process-weights.
743
- self.kernel: mk.FusedMoEModularKernel | None = None
744
-
745
- @property
746
- def topk_indices_dtype(self) -> torch.dtype | None:
747
- if self.kernel is not None:
748
- return self.kernel.prepare_finalize.topk_indices_dtype()
749
- return None
750
-
738
def maybe_make_prepare_finalize(
739
self,
740
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
741
) -> mk.FusedMoEPrepareAndFinalize | None:
755
- # TRT LLM not supported with all2all yet.
756
- if self.fp8_backend == Fp8MoeBackend.FLASHINFER_TRTLLM:
757
- return None
758
- elif self.fp8_backend == Fp8MoeBackend.FLASHINFER_CUTLASS:
759
- # For no-EP case, don't use the MKM framework.
760
- if not self.moe.moe_parallel_config.use_all2all_kernels:
761
- return None
762
-
763
- prepare_finalize = build_flashinfer_fp8_cutlass_moe_prepare_finalize(
764
- self.moe,
765
- use_deepseek_fp8_block_scale=False,
766
- )
767
- logger.debug_once("%s", prepare_finalize.__class__.__name__)
768
- return prepare_finalize
769
- return super().maybe_make_prepare_finalize(routing_tables)
742
+ raise ValueError(
743
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
744
+ "logic. This function should not be called."
745
+ )
746
747
def select_gemm_impl(
748
self,
749
prepare_finalize: mk.FusedMoEPrepareAndFinalize,
750
layer: torch.nn.Module,
751
) -> mk.FusedMoEPermuteExpertsUnpermute:
776
- assert self.moe_quant_config is not None
777
- assert self.experts_cls is not None
778
- return make_fp8_moe_kernel_for_mkm(
779
- moe_config=self.moe,
780
- quant_config=self.moe_quant_config,
781
- experts_cls=self.experts_cls,
782
- prepare_finalize=prepare_finalize,
752
+ raise ValueError(
753
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
754
+ "logic. This function should not be called."
755
)
756
757
def create_weights(
@@ -863,7 +835,7 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
835
836
def _setup_kernel(
837
self,
866
- layer: torch.nn.Module,
838
+ layer: FusedMoE,
839
w13: torch.Tensor,
840
w2: torch.Tensor,
841
w13_scale: torch.Tensor,
@@ -893,11 +865,13 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
865
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
866
if self.moe_quant_config:
867
assert self.experts_cls is not None
896
- self.kernel, self.use_inplace = make_fp8_moe_kernel(
868
+ self.moe_mk, self.use_inplace = make_fp8_moe_kernel(
869
moe_quant_config=self.moe_quant_config,
870
moe_config=self.moe,
871
fp8_backend=self.fp8_backend,
872
experts_cls=self.experts_cls,
873
+ routing_tables=layer._maybe_init_expert_routing_tables(),
874
+ shared_experts=layer.shared_experts,
875
)
876
877
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
@@ -998,8 +972,8 @@ class ModelOptFp8MoEMethod(FusedMoEMethodBase):
972
f"but got {layer.activation}"
973
)
974
1001
- assert self.kernel is not None
1002
- return self.kernel(
975
+ assert self.moe_mk is not None
976
+ return self.moe_mk(
977
x,
978
layer.w13_weight,
979
layer.w2_weight,
@@ -1379,50 +1353,27 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1353
activation_key=kNvfp4Dynamic,
1354
)
1355
1382
- # Delay creation of the kernel until after process-weights.
1383
- self.kernel: mk.FusedMoEModularKernel | None = None
1384
-
1356
self.use_global_sf = is_global_sf_supported_for_nvfp4_backend(
1357
self.nvfp4_backend
1358
)
1359
1389
- @property
1390
- def topk_indices_dtype(self) -> torch.dtype | None:
1391
- if self.kernel is not None:
1392
- return self.kernel.prepare_finalize.topk_indices_dtype()
1393
- return None
1394
-
1360
def maybe_make_prepare_finalize(
1361
self,
1362
routing_tables: tuple[torch.Tensor, torch.Tensor, torch.Tensor] | None = None,
1363
) -> mk.FusedMoEPrepareAndFinalize | None:
1399
- if self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_TRTLLM:
1400
- return None
1401
- elif self.nvfp4_backend == NvFp4MoeBackend.FLASHINFER_CUTLASS:
1402
- # For no-EP case, don't use the MKM framework.
1403
- if not self.moe.moe_parallel_config.use_all2all_kernels:
1404
- return None
1405
- # For now, fp4 moe only works with the flashinfer dispatcher.
1406
- prepare_finalize = build_flashinfer_fp4_cutlass_moe_prepare_finalize(
1407
- self.moe
1408
- )
1409
- logger.debug_once("%s", prepare_finalize.__class__.__name__)
1410
- return prepare_finalize
1411
- else:
1412
- return super().maybe_make_prepare_finalize(routing_tables)
1364
+ raise ValueError(
1365
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
1366
+ "logic. This function should not be called."
1367
+ )
1368
1369
def select_gemm_impl(
1370
self,
1371
prepare_finalize: mk.FusedMoEPrepareAndFinalize,
1372
layer: torch.nn.Module,
1373
) -> mk.FusedMoEPermuteExpertsUnpermute:
1419
- assert self.moe_quant_config is not None
1420
- assert self.experts_cls is not None
1421
- return make_nvfp4_moe_kernel_for_mkm(
1422
- moe_config=self.moe,
1423
- quant_config=self.moe_quant_config,
1424
- experts_cls=self.experts_cls,
1425
- prepare_finalize=prepare_finalize,
1374
+ raise ValueError(
1375
+ f"{self.__class__.__name__} uses the new modular kernel initialization "
1376
+ "logic. This function should not be called."
1377
)
1378
1379
def uses_weight_scale_2_pattern(self) -> bool:
@@ -1547,7 +1498,7 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1498
)
1499
layer.register_parameter("w2_input_scale", w2_input_scale)
1500
1550
- def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
1501
+ def process_weights_after_loading(self, layer: FusedMoE) -> None:
1502
"""
1503
Convert NVFP4 MoE weights into kernel format and setup the kernel.
1504
"""
@@ -1599,15 +1550,14 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1550
# TODO(rob): unify these so FP8MoEMethod owns the ModularKernel
1551
# in both cases.
1552
self.moe_quant_config = self.get_fused_moe_quant_config(layer)
1602
- if self.moe_quant_config and (
1603
- (not self.moe.moe_parallel_config.use_all2all_kernels)
1604
- or self.moe.moe_parallel_config.use_naive_all2all_kernels
1605
- ):
1553
+ if self.moe_quant_config:
1554
assert self.experts_cls is not None
1607
- self.kernel = make_nvfp4_moe_kernel(
1555
+ self.moe_mk = make_nvfp4_moe_kernel(
1556
moe_quant_config=self.moe_quant_config,
1557
moe_config=self.moe,
1558
experts_cls=self.experts_cls,
1559
+ shared_experts=layer.shared_experts,
1560
+ routing_tables=layer._maybe_init_expert_routing_tables(),
1561
)
1562
1563
@property
@@ -1708,8 +1658,8 @@ class ModelOptNvFp4FusedMoE(FusedMoEMethodBase):
1658
global_num_experts=layer.global_num_experts,
1659
)
1660
else:
1711
- assert self.kernel is not None
1712
- return self.kernel(
1661
+ assert self.moe_mk is not None
1662
+ return self.moe_mk(
1663
x,
1664
layer.w13_weight,
1665
layer.w2_weight,
vllm/model_executor/layers/quantization/utils/flashinfer_fp4_moe.py
-15
@@ -15,9 +15,6 @@ from vllm.model_executor.layers.fused_moe.config import (
15
FusedMoEParallelConfig,
16
RoutingMethodType,
17
)
18
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
19
- create_flashinfer_prepare_finalize,
20
-)
18
from vllm.model_executor.layers.quantization.utils.quant_utils import (
19
QuantKey,
20
kNvfp4Dynamic,
@@ -42,7 +39,6 @@ __all__ = [
39
"is_flashinfer_fp4_cutlass_moe_available",
40
"is_flashinfer_fp4_cutedsl_moe_available",
41
"reorder_w1w3_to_w3w1",
45
- "build_flashinfer_fp4_cutlass_moe_prepare_finalize",
42
]
43
44
#
@@ -163,17 +159,6 @@ def reorder_w1w3_to_w3w1(
159
)
160
161
166
-def build_flashinfer_fp4_cutlass_moe_prepare_finalize(
167
- moe: FusedMoEConfig,
168
-) -> mk.FusedMoEPrepareAndFinalize:
169
- """Create a FlashInfer CUTLASS fused-MoE prepare finalize kernel"""
170
- use_dp = moe.moe_parallel_config.dp_size > 1
171
- enable_alltoallv = moe.moe_parallel_config.all2all_backend == "flashinfer_all2allv"
172
- return create_flashinfer_prepare_finalize(
173
- use_dp=use_dp, use_nvfp4=True, enable_alltoallv=enable_alltoallv
174
- )
175
-
176
-
162
def prepare_static_weights_for_trtllm_fp4_moe(
163
# args_dequant,
164
# args,
vllm/model_executor/layers/quantization/utils/flashinfer_utils.py
-19
@@ -4,15 +4,8 @@ from enum import Enum
4
5
import torch
6
7
-import vllm.model_executor.layers.fused_moe.modular_kernel as mk
7
from vllm import envs
8
from vllm.logger import init_logger
10
-from vllm.model_executor.layers.fused_moe.config import (
11
- FusedMoEConfig,
12
-)
13
-from vllm.model_executor.layers.fused_moe.flashinfer_cutlass_prepare_finalize import ( # noqa: E501
14
- create_flashinfer_prepare_finalize,
15
-)
9
from vllm.platforms import current_platform
10
from vllm.utils.math_utils import round_up
11
@@ -163,18 +156,6 @@ def make_fp8_moe_alpha_scales_for_fi(
156
return g1_alphas, g2_alphas
157
158
166
-def build_flashinfer_fp8_cutlass_moe_prepare_finalize(
167
- moe: FusedMoEConfig | None, use_deepseek_fp8_block_scale: bool = False
168
-) -> mk.FusedMoEPrepareAndFinalize:
169
- """Create a FlashInfer CUTLASS fused-MoE prepare finalize kernel"""
170
- use_dp = moe.moe_parallel_config.dp_size > 1 if moe is not None else False
171
- # Propagate block-scale flag so prepare/finalize can skip act quantization
172
- # and inform the kernel to consume per-block weight scales.
173
- return create_flashinfer_prepare_finalize(
174
- use_dp, use_deepseek_fp8_block_scale=use_deepseek_fp8_block_scale
175
- )
176
-
177
-
159
def get_flashinfer_moe_backend() -> FlashinferMoeBackend:
160
backend_map = {
161
"throughput": FlashinferMoeBackend.CUTLASS,
vllm/model_executor/warmup/deep_gemm_warmup.py
+3
-3
@@ -14,7 +14,6 @@ from vllm.distributed.parallel_state import get_dp_group, is_global_first_rank
14
from vllm.model_executor.layers.fused_moe.deep_gemm_moe import DeepGemmExperts
15
from vllm.model_executor.layers.fused_moe.deep_gemm_utils import compute_aligned_M
16
from vllm.model_executor.layers.fused_moe.layer import FusedMoE, FusedMoEModularMethod
17
-from vllm.model_executor.layers.fused_moe.modular_kernel import FusedMoEModularKernel
17
from vllm.model_executor.layers.fused_moe.triton_deep_gemm_moe import (
18
TritonOrDeepGemmExperts,
19
)
@@ -169,9 +168,10 @@ def _fused_moe_grouped_gemm_may_use_deep_gemm(module: torch.nn.Module) -> bool:
168
# modular kernels could invoke deep_gemm_moe_fp8
169
return True
170
172
- mk: FusedMoEModularKernel = module.quant_method.fused_experts
171
# Further check if the ModularKernel implementation uses the DeepGemmExperts
174
- return isinstance(mk.fused_experts, (DeepGemmExperts, TritonOrDeepGemmExperts))
172
+ return isinstance(
173
+ module.quant_method.moe_mk, (DeepGemmExperts, TritonOrDeepGemmExperts)
174
+ )
175
176
177
FP8_GEMM_NT_WARMUP_CACHE: set[torch.Size] = set()