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 15, 02:15 UTC
[Misc] MoE ModularKernel : Introduce TopKWeightAndReduce (#20648)
Signed-off-by: Varun Sundar Rabindranath <vsundarr@redhat.com> Co-authored-by: Varun Sundar Rabindranath <vsundarr@redhat.com>
Varun Sundar Rabindranath committed
Jul 10, 2025 at 17:40 UTC
f0c98cae2758b1a706537aa412c6868bb060c151
14 files changed
+297
-59
tests/kernels/moe/test_pplx_moe.py
+3
@@ -32,6 +32,8 @@ from vllm.model_executor.layers.fused_moe.fused_batched_moe import (
32
from vllm.model_executor.layers.fused_moe.fused_moe import get_default_config
33
from vllm.model_executor.layers.fused_moe.modular_kernel import (
34
FusedMoEModularKernel)
35
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
36
+ TopKWeightAndReduceDelegate)
37
from vllm.platforms import current_platform
38
from vllm.utils import round_up
39
@@ -371,6 +373,7 @@ def pplx_prepare_finalize(
373
chunk_topk_weight,
374
chunk_topk_ids,
375
False,
376
+ weight_and_reduce_impl=TopKWeightAndReduceDelegate(),
377
)
378
379
torch.cuda.synchronize()
vllm/model_executor/layers/fused_moe/batched_deep_gemm_moe.py
+6
@@ -7,6 +7,8 @@ import torch
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm.logger import init_logger
9
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
10
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
11
+ TopKWeightAndReduceDelegate)
12
from vllm.model_executor.layers.fused_moe.utils import _resize_cache
13
from vllm.triton_utils import tl, triton
14
@@ -217,6 +219,10 @@ class BatchedDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
219
def supports_expert_map(self) -> bool:
220
return False
221
222
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
223
+ # Let PrepareAndFinalize::finalize() decide the impl.
224
+ return TopKWeightAndReduceDelegate()
225
+
226
def workspace_shapes(
227
self,
228
a: torch.Tensor,
vllm/model_executor/layers/fused_moe/batched_triton_or_deep_gemm_moe.py
+19
@@ -88,6 +88,25 @@ class BatchedTritonOrDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
88
return ((bdge is None or bdge.supports_expert_map())
89
and (bte is None or bte.supports_expert_map()))
90
91
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
92
+ bdge = self.batched_deep_gemm_experts
93
+ bte = self.batched_triton_experts
94
+ bdge_war = bdge.finalize_weight_and_reduce_impl() if bdge else None
95
+ bte_war = bte.finalize_weight_and_reduce_impl() if bte else None
96
+ is_bdge_war = bdge_war is not None
97
+ is_bte_war = bte_war is not None
98
+
99
+ if is_bdge_war and is_bte_war:
100
+ assert bdge_war == bte_war, (
101
+ "Both implementations should agree on WeightAndReduce impls. "
102
+ f"Got bdge_war: {bdge_war}, and bte_war: {bte_war}")
103
+
104
+ if bdge_war is not None:
105
+ return bdge_war
106
+
107
+ assert bte_war is not None
108
+ return bte_war
109
+
110
def workspace_shapes(
111
self,
112
a: torch.Tensor,
vllm/model_executor/layers/fused_moe/cutlass_moe.py
+6
@@ -11,6 +11,8 @@ from vllm.logger import init_logger
11
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
12
from vllm.model_executor.layers.fused_moe.prepare_finalize import (
13
MoEPrepareAndFinalizeNoEP)
14
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
15
+ TopKWeightAndReduceDelegate)
16
from vllm.model_executor.layers.fused_moe.utils import (_fp8_perm,
17
_fp8_quantize,
18
_resize_cache)
@@ -255,6 +257,10 @@ class CutlassExpertsFp8(mk.FusedMoEPermuteExpertsUnpermute):
257
def supports_expert_map(self) -> bool:
258
return not self.use_batched_format
259
260
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
261
+ # Let PrepareAndFinalize::finalize() decide the impl.
262
+ return TopKWeightAndReduceDelegate()
263
+
264
def workspace_shapes(
265
self,
266
a: torch.Tensor,
vllm/model_executor/layers/fused_moe/deep_gemm_moe.py
+6
@@ -12,6 +12,8 @@ from vllm.model_executor.layers.fused_moe.moe_permute_unpermute import (
12
_moe_permute)
13
from vllm.model_executor.layers.fused_moe.prepare_finalize import (
14
MoEPrepareAndFinalizeNoEP)
15
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
16
+ TopKWeightAndReduceDelegate)
17
from vllm.model_executor.layers.fused_moe.utils import (
18
_resize_cache, per_token_group_quant_fp8)
19
from vllm.utils import has_deep_gemm, round_up
@@ -85,6 +87,10 @@ class DeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
87
def supports_expert_map(self) -> bool:
88
return True
89
90
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
91
+ # Let PrepareAndFinalize::finalize() decide the impl.
92
+ return TopKWeightAndReduceDelegate()
93
+
94
def workspace_shapes(
95
self, a: torch.Tensor, aq: torch.Tensor, M: int, N: int, K: int,
96
topk: int, global_num_experts: int, local_num_experts: int
vllm/model_executor/layers/fused_moe/deepep_ht_prepare_finalize.py
+10
-29
@@ -6,8 +6,9 @@ import deep_ep
6
import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
-from vllm import _custom_ops as ops
9
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
10
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
11
+ TopKWeightAndReduceContiguous, TopKWeightAndReduceDelegate)
12
from vllm.model_executor.layers.fused_moe.utils import (
13
moe_kernel_quantize_input)
14
@@ -187,45 +188,25 @@ class DeepEPHTPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
188
return (expert_x, expert_x_scale, expert_tokens_meta, expert_topk_ids,
189
expert_topk_weights)
190
190
- def _apply_weights_and_reduce(self, num_tokens: int,
191
- fused_expert_output: torch.Tensor,
192
- topk_weights: torch.Tensor,
193
- apply_router_weight_on_input: bool,
194
- output_dtype: torch.dtype):
195
-
196
- hidden_dim = fused_expert_output.size(-1)
197
- if fused_expert_output.ndim == 2:
198
- fused_expert_output = fused_expert_output.view(
199
- num_tokens, -1, hidden_dim)
200
-
201
- if not apply_router_weight_on_input:
202
- # The DeepEP combine kernels don't do the topk weight
203
- # multiplication. We multiply the weights locally.
204
- m_x_topk = fused_expert_output.size(0)
205
- fused_expert_output.mul_(topk_weights.view(m_x_topk, -1, 1))
206
-
207
- out = torch.empty((num_tokens, hidden_dim),
208
- device=fused_expert_output.device,
209
- dtype=output_dtype)
210
- ops.moe_sum(fused_expert_output, out)
211
-
212
- return out
213
-
191
def finalize(self, output: torch.Tensor, fused_expert_output: torch.Tensor,
192
topk_weights: torch.Tensor, topk_ids: torch.Tensor,
216
- apply_router_weight_on_input: bool) -> None:
193
+ apply_router_weight_on_input: bool,
194
+ weight_and_reduce_impl: mk.TopKWeightAndReduce) -> None:
195
196
assert self.handle is not None
197
198
# fused_expert_output can have 0 tokens - This happens when none of the
199
# tokens from the all2all reach this EP rank.
200
if fused_expert_output.numel() != 0:
223
- fused_expert_output = self._apply_weights_and_reduce(
224
- num_tokens=topk_ids.size(0),
201
+ if isinstance(weight_and_reduce_impl, TopKWeightAndReduceDelegate):
202
+ weight_and_reduce_impl = TopKWeightAndReduceContiguous()
203
+ fused_expert_output = weight_and_reduce_impl.apply(
204
+ output=None,
205
fused_expert_output=fused_expert_output,
206
topk_weights=topk_weights,
207
+ topk_ids=topk_ids,
208
apply_router_weight_on_input=apply_router_weight_on_input,
228
- output_dtype=output.dtype)
209
+ )
210
211
combined_x, _, event = self.buffer.combine(
212
x=fused_expert_output,
vllm/model_executor/layers/fused_moe/deepep_ll_prepare_finalize.py
+7
-2
@@ -7,6 +7,8 @@ import torch
7
8
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
9
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
10
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
11
+ TopKWeightAndReduceDelegate)
12
from vllm.model_executor.layers.fused_moe.utils import (
13
moe_kernel_quantize_input, normalize_batched_scales_shape)
14
@@ -166,8 +168,11 @@ class DeepEPLLPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
168
169
def finalize(self, output: torch.Tensor, fused_expert_output: torch.Tensor,
170
topk_weights: torch.Tensor, topk_ids: torch.Tensor,
169
- apply_router_weight_on_input: bool) -> None:
170
-
171
+ apply_router_weight_on_input: bool,
172
+ weight_and_reduce_impl: mk.TopKWeightAndReduce) -> None:
173
+ assert isinstance(
174
+ weight_and_reduce_impl, TopKWeightAndReduceDelegate
175
+ ), ("Weight application and reduction happens in the combine kernel.")
176
assert self.handle is not None
177
178
combine_topk_weights = topk_weights
vllm/model_executor/layers/fused_moe/fused_batched_moe.py
+20
-18
@@ -11,6 +11,8 @@ import vllm.model_executor.layers.fused_moe.modular_kernel as mk
11
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
12
from vllm.model_executor.layers.fused_moe.fused_moe import (
13
get_config_dtype_str, try_get_optimal_moe_config)
14
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
15
+ TopKWeightAndReduceDelegate, TopKWeightAndReduceNaiveBatched)
16
from vllm.model_executor.layers.fused_moe.utils import (
17
_resize_cache, moe_kernel_quantize_input, normalize_batched_scales_shape,
18
normalize_scales_shape)
@@ -600,25 +602,17 @@ class BatchedPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
602
topk_weights: torch.Tensor,
603
topk_ids: torch.Tensor,
604
apply_router_weight_on_input: bool,
605
+ weight_and_reduce_impl: mk.TopKWeightAndReduce,
606
) -> None:
604
- num_tokens = topk_ids.size(0)
605
- num_local_experts = fused_expert_output.size(0)
606
- K = fused_expert_output.size(-1)
607
- assert output.size(0) == num_tokens and output.size(1) == K
608
-
609
- output.fill_(0)
610
-
611
- first_expert = num_local_experts * self.rank
612
- last_expert = first_expert + num_local_experts
613
-
614
- for expert_id in range(first_expert, last_expert):
615
- matching_tokens = topk_ids == expert_id
616
- topks = torch.any(matching_tokens, dim=1).flatten()
617
- rows = torch.count_nonzero(topks)
618
- rhs = fused_expert_output[expert_id - first_expert, :rows, :]
619
- if not apply_router_weight_on_input:
620
- rhs.mul_(topk_weights[matching_tokens].view(rhs.size(0), 1))
621
- output[topks] = output[topks] + rhs
607
+ if isinstance(weight_and_reduce_impl, TopKWeightAndReduceDelegate):
608
+ weight_and_reduce_impl = TopKWeightAndReduceNaiveBatched(self.rank)
609
+ weight_and_reduce_impl.apply(
610
+ output=output,
611
+ fused_expert_output=fused_expert_output,
612
+ topk_weights=topk_weights,
613
+ topk_ids=topk_ids,
614
+ apply_router_weight_on_input=apply_router_weight_on_input,
615
+ )
616
617
618
class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
@@ -670,6 +664,10 @@ class NaiveBatchedExperts(mk.FusedMoEPermuteExpertsUnpermute):
664
def supports_expert_map(self) -> bool:
665
return False
666
667
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
668
+ # Let PrepareAndFinalize::finalize() decide the impl.
669
+ return TopKWeightAndReduceDelegate()
670
+
671
def workspace_shapes(
672
self,
673
a: torch.Tensor,
@@ -877,6 +875,10 @@ class BatchedTritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
875
def supports_expert_map(self) -> bool:
876
return False
877
878
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
879
+ # Let PrepareAndFinalize::finalize() decide the impl.
880
+ return TopKWeightAndReduceDelegate()
881
+
882
def workspace_shapes(
883
self,
884
a: torch.Tensor,
vllm/model_executor/layers/fused_moe/fused_moe.py
+6
@@ -25,6 +25,8 @@ from vllm.model_executor.layers.fused_moe.moe_align_block_size import (
25
moe_align_block_size)
26
from vllm.model_executor.layers.fused_moe.prepare_finalize import (
27
MoEPrepareAndFinalizeNoEP)
28
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
29
+ TopKWeightAndReduceDelegate)
30
from vllm.model_executor.layers.fused_moe.utils import (
31
_resize_cache, moe_kernel_quantize_input)
32
from vllm.model_executor.layers.quantization.utils.mxfp4_utils import (
@@ -1596,6 +1598,10 @@ class TritonExperts(mk.FusedMoEPermuteExpertsUnpermute):
1598
def supports_expert_map(self) -> bool:
1599
return True
1600
1601
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
1602
+ # Let PrepareAndFinalize::finalize() decide the impl.
1603
+ return TopKWeightAndReduceDelegate()
1604
+
1605
def workspace_shapes(
1606
self,
1607
a: torch.Tensor,
vllm/model_executor/layers/fused_moe/modular_kernel.py
+38
-6
@@ -23,7 +23,7 @@ from vllm.utils import cdiv
23
#
24
# [Router] → [Quantize-Dispatch] → [Permute-Experts-Unpermute] → [Combine]
25
#
26
-# Each component will be independent of the others except for
26
+# Each component will be independent of (but may inform) the others except for
27
# [Quantize-Dispatch] and `[Combine] (see below). The components can then be
28
# mixed and matched with so that DP+EP can be supported easily for multiple
29
# MoE kernel implementations.
@@ -32,13 +32,19 @@ from vllm.utils import cdiv
32
# * FusedMoEPrepareAndFinalize - an abstract base class for preparation of MoE
33
# inputs (e.g. quantization, distribution) and finalization of Moe outputs.
34
# The prepare method must take care of any needed quantization and the
35
-# finalize method must apply weights and do the final reduction of the output.
35
+# finalize method, informed by the FusedMoEPermuteExpertsUnpermute method,
36
+# may apply weights and/or do the final reduction of the output.
37
# * FusedMoEPermuteExpertsUnpermute - an abstract base class for the main fused
37
-# MoE operation. One important feature to note is that this class does not
38
-# apply topk weights or reduce the final output.
38
+# MoE operation, i.e matmul + act_mul + optionally quant + matmul.
39
+# Some FusedMoEPermuteExpertsUnpermute implementations may choose to do
40
+# the weight application and/or reduction. The class communicates this
41
+# to [Finalize] via a TopKWeightAndReduce object.
42
# * FusedMoEModularKernel - an interface class that combines a
43
# FusedMoEPrepareAndFinalize and a FusedMoEPermuteExpertsUnpermute to
44
# provide the standard fused MoE kernel interface.
45
+# * TopKWeightAndReduce - A TopKWeightAndReduce implementation chosen
46
+# by the FusedMoEPermuteExpertsUnpermute implementation that is passed
47
+# on to [Finalize].
48
#
49
# [Quantize-Prepare] and [Finalize] functionality are bundled into a single
50
# class `FusedMoEPrepareAndFinalize` since they could use collective
@@ -117,6 +123,24 @@ class ExpertTokensMetadata:
123
expert_num_tokens_cpu=expert_num_tokens_cpu)
124
125
126
+class TopKWeightAndReduce(ABC):
127
+ """
128
+ An abstract base class for weight application and reduction implementations.
129
+ """
130
+
131
+ @abstractmethod
132
+ def apply(self, output: Optional[torch.Tensor],
133
+ fused_expert_output: torch.Tensor, topk_weights: torch.Tensor,
134
+ topk_ids: torch.Tensor,
135
+ apply_router_weight_on_input: bool) -> torch.Tensor:
136
+ """
137
+ Apply topk_weights to the fused_experts_outputs and/or reduce.
138
+ If an output tensor is not passed, it will be created in the
139
+ function.
140
+ """
141
+ raise NotImplementedError
142
+
143
+
144
# TODO: pass FusedMoEParallelConfig in as ctor parameter?
145
class FusedMoEPrepareAndFinalize(ABC):
146
"""
@@ -173,6 +197,7 @@ class FusedMoEPrepareAndFinalize(ABC):
197
topk_weights: torch.Tensor,
198
topk_ids: torch.Tensor,
199
apply_router_weight_on_input: bool,
200
+ weight_and_reduce_impl: TopKWeightAndReduce,
201
) -> None:
202
"""
203
Perform any combine plus apply weights and perform a reduction on the
@@ -184,6 +209,8 @@ class FusedMoEPrepareAndFinalize(ABC):
209
- topk_ids: The topk_ids.
210
- apply_router_weight_on_input: When False, apply the weights to
211
fused_expert_output.
212
+ - weight_and_reduce_impl: An optional TopKWeightAndReduce
213
+ implementation.
214
"""
215
raise NotImplementedError
216
@@ -323,6 +350,9 @@ class FusedMoEPermuteExpertsUnpermute(ABC):
350
return envs.VLLM_ENABLE_FUSED_MOE_ACTIVATION_CHUNKING and \
351
self.supports_chunking()
352
353
+ def finalize_weight_and_reduce_impl(self) -> TopKWeightAndReduce:
354
+ raise NotImplementedError
355
+
356
@abstractmethod
357
def apply(
358
self,
@@ -702,7 +732,9 @@ class FusedMoEModularKernel(torch.nn.Module):
732
a2_scale=a2_scale,
733
expert_tokens_meta=expert_tokens_meta)
734
705
- self.prepare_finalize.finalize(output, fused_out, topk_weights,
706
- topk_ids, apply_router_weight_on_input)
735
+ self.prepare_finalize.finalize(
736
+ output, fused_out, topk_weights, topk_ids,
737
+ apply_router_weight_on_input,
738
+ self.fused_experts.finalize_weight_and_reduce_impl())
739
740
return output
vllm/model_executor/layers/fused_moe/pplx_prepare_finalize.py
+7
@@ -8,6 +8,8 @@ import torch
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 FusedMoEQuantConfig
11
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
12
+ TopKWeightAndReduceDelegate)
13
from vllm.model_executor.layers.fused_moe.utils import (
14
_validate_scale_shape, moe_kernel_quantize_input)
15
from vllm.utils import cdiv, round_up
@@ -222,7 +224,12 @@ class PplxPrepareAndFinalize(mk.FusedMoEPrepareAndFinalize):
224
topk_weights: torch.Tensor,
225
topk_ids: torch.Tensor,
226
apply_router_weight_on_input: bool,
227
+ weight_and_reduce_impl: mk.TopKWeightAndReduce,
228
) -> None:
229
+ assert isinstance(
230
+ weight_and_reduce_impl, TopKWeightAndReduceDelegate
231
+ ), ("Weight application and reduction happens in the combine kernel.")
232
+
233
# This argument is optional
234
# There's not much point setting this unless it is != topk_ids.size(0)
235
bound_m: Optional[torch.Tensor] = None
vllm/model_executor/layers/fused_moe/prepare_finalize.py
+11
-4
@@ -6,8 +6,8 @@ import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
9
-from vllm.model_executor.layers.fused_moe.moe_permute_unpermute import (
10
- _moe_unpermute_and_reduce)
9
+from vllm.model_executor.layers.fused_moe.topk_weight_and_reduce import (
10
+ TopKWeightAndReduceContiguous, TopKWeightAndReduceDelegate)
11
from vllm.model_executor.layers.fused_moe.utils import (
12
moe_kernel_quantize_input)
13
@@ -62,6 +62,13 @@ class MoEPrepareAndFinalizeNoEP(mk.FusedMoEPrepareAndFinalize):
62
topk_weights: torch.Tensor,
63
topk_ids: torch.Tensor,
64
apply_router_weight_on_input: bool,
65
+ weight_and_reduce_impl: mk.TopKWeightAndReduce,
66
) -> None:
66
- _moe_unpermute_and_reduce(output, fused_expert_output, None,
67
- topk_weights, apply_router_weight_on_input)
67
+ if isinstance(weight_and_reduce_impl, TopKWeightAndReduceDelegate):
68
+ weight_and_reduce_impl = TopKWeightAndReduceContiguous()
69
+ weight_and_reduce_impl.apply(
70
+ output=output,
71
+ fused_expert_output=fused_expert_output,
72
+ topk_weights=topk_weights,
73
+ topk_ids=topk_ids,
74
+ apply_router_weight_on_input=apply_router_weight_on_input)
vllm/model_executor/layers/fused_moe/topk_weight_and_reduce.py
new
+139
@@ -0,0 +1,139 @@
1
+# SPDX-License-Identifier: Apache-2.0
2
+# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+
4
+from typing import Optional
5
+
6
+import torch
7
+
8
+import vllm._custom_ops as ops
9
+import vllm.model_executor.layers.fused_moe.modular_kernel as mk
10
+
11
+
12
+class TopKWeightAndReduceDelegate(mk.TopKWeightAndReduce):
13
+ """
14
+ Useful in the case when some FusedMoEPermuteExpertsUnpermute
15
+ implementation does not perform weight application and reduction
16
+ but cannot address the needs of all the compatible PrepareAndFinalize
17
+ implementations.
18
+ For example, BatchedTritonExperts is compatible with both
19
+ PplxPrepareAndFinalize and BatchedPrepareAndFinalize. PplxPrepareAndFinalize
20
+ does the weight-application + reduction as part of the pplx combine kernel.
21
+ But the BatchedPrepareAndFinalize needs an implementation. To facilitate
22
+ this case, the BatchedTritonExperts could use TopKWeightAndReduceDelegate
23
+ so the PrepareAndFinalize implementations could choose how to
24
+ weight + reduce.
25
+ """
26
+
27
+ def __eq__(self, other):
28
+ return isinstance(other, TopKWeightAndReduceDelegate)
29
+
30
+ def apply(self, output: Optional[torch.Tensor],
31
+ fused_expert_output: torch.Tensor, topk_weights: torch.Tensor,
32
+ topk_ids: torch.Tensor,
33
+ apply_router_weight_on_input: bool) -> torch.Tensor:
34
+ raise RuntimeError("The caller is expected to choose an appropriate "
35
+ "TopKWeightAndReduce implementation.")
36
+
37
+
38
+class TopKWeightAndReduceNoOP(mk.TopKWeightAndReduce):
39
+ """
40
+ The fused_experts outputs have already been weight applied and reduced.
41
+ This implementation is a no-op.
42
+ """
43
+
44
+ def __eq__(self, other):
45
+ return isinstance(other, TopKWeightAndReduceNoOP)
46
+
47
+ def apply(self, output: Optional[torch.Tensor],
48
+ fused_expert_output: torch.Tensor, topk_weights: torch.Tensor,
49
+ topk_ids: torch.Tensor,
50
+ apply_router_weight_on_input: bool) -> torch.Tensor:
51
+ # Relax this if an explicit copy is necessary. Note that,
52
+ # if a copy is employed we have to make sure that the
53
+ # tensors don't overlap
54
+ assert output is None
55
+ return fused_expert_output
56
+
57
+
58
+class TopKWeightAndReduceContiguous(mk.TopKWeightAndReduce):
59
+ """
60
+ TopKWeightAndReduce implementation for a fused_experts output
61
+ of shape (m, topk, K)
62
+ """
63
+
64
+ def __eq__(self, other):
65
+ return isinstance(other, TopKWeightAndReduceContiguous)
66
+
67
+ def apply(self, output: Optional[torch.Tensor],
68
+ fused_expert_output: torch.Tensor, topk_weights: torch.Tensor,
69
+ topk_ids: torch.Tensor,
70
+ apply_router_weight_on_input: bool) -> torch.Tensor:
71
+
72
+ m, num_topk = topk_ids.size()
73
+ k = fused_expert_output.size(-1)
74
+ if fused_expert_output.ndim == 2:
75
+ fused_expert_output = fused_expert_output.view(m, num_topk, k)
76
+
77
+ assert fused_expert_output.size() == (m, num_topk, k), (
78
+ f"Expected fused_expert_output size {(m, num_topk, k)}. But got "
79
+ f"{fused_expert_output.size()}")
80
+
81
+ if not apply_router_weight_on_input:
82
+ fused_expert_output.mul_(topk_weights.view(m, -1, 1))
83
+
84
+ if output is None:
85
+ output = torch.empty((m, k),
86
+ device=fused_expert_output.device,
87
+ dtype=fused_expert_output.dtype)
88
+ assert output.size() == (m, k), (
89
+ f"Expected output size {(m, k)}. But got {output.size()}")
90
+
91
+ ops.moe_sum(fused_expert_output, output)
92
+ return output
93
+
94
+
95
+class TopKWeightAndReduceNaiveBatched(mk.TopKWeightAndReduce):
96
+ """
97
+ TopKWeightAndReduce implementation for a fused_experts output
98
+ of shape (num_experts, batch_size, K)
99
+ """
100
+
101
+ def __init__(self, rank: int):
102
+ self.rank = rank
103
+
104
+ def __eq__(self, other):
105
+ return (isinstance(other, TopKWeightAndReduceNaiveBatched)
106
+ and (other.rank == self.rank))
107
+
108
+ def apply(self, output: Optional[torch.Tensor],
109
+ fused_expert_output: torch.Tensor, topk_weights: torch.Tensor,
110
+ topk_ids: torch.Tensor,
111
+ apply_router_weight_on_input: bool) -> torch.Tensor:
112
+ assert fused_expert_output.ndim == 3
113
+ num_tokens = topk_ids.size(0)
114
+ num_local_experts = fused_expert_output.size(0)
115
+ K = fused_expert_output.size(-1)
116
+
117
+ if output is None:
118
+ output = torch.zeros((num_tokens, K),
119
+ device=fused_expert_output.device,
120
+ dtype=fused_expert_output.dtype)
121
+ else:
122
+ output.fill_(0)
123
+
124
+ assert output.size() == (num_tokens, K), (
125
+ f"Expected output size {(num_tokens, K)}, but got {output.size()}")
126
+
127
+ first_expert = num_local_experts * self.rank
128
+ last_expert = first_expert + num_local_experts
129
+
130
+ for expert_id in range(first_expert, last_expert):
131
+ matching_tokens = topk_ids == expert_id
132
+ topks = torch.any(matching_tokens, dim=1).flatten()
133
+ rows = torch.count_nonzero(topks)
134
+ rhs = fused_expert_output[expert_id - first_expert, :rows, :]
135
+ if not apply_router_weight_on_input:
136
+ rhs.mul_(topk_weights[matching_tokens].view(rhs.size(0), 1))
137
+ output[topks] = output[topks] + rhs
138
+
139
+ return output
vllm/model_executor/layers/fused_moe/triton_deep_gemm_moe.py
+19
@@ -69,6 +69,25 @@ class TritonOrDeepGemmExperts(mk.FusedMoEPermuteExpertsUnpermute):
69
return ((dge is None or dge.supports_expert_map())
70
and (te is None or te.supports_expert_map()))
71
72
+ def finalize_weight_and_reduce_impl(self) -> mk.TopKWeightAndReduce:
73
+ dge = self.deep_gemm_expert
74
+ te = self.triton_expert
75
+ dge_war = dge.finalize_weight_and_reduce_impl() if dge else None
76
+ te_war = te.finalize_weight_and_reduce_impl() if te else None
77
+ is_dge_war = dge_war is not None
78
+ is_te_war = te_war is not None
79
+
80
+ if is_dge_war and is_te_war:
81
+ assert dge_war == te_war, (
82
+ "Both implementations should agree on WeightAndReduce impls. "
83
+ f"Got dge_war: {dge_war}, and te_war: {te_war}")
84
+
85
+ if dge_war is not None:
86
+ return dge_war
87
+
88
+ assert te_war is not None
89
+ return te_war
90
+
91
def workspace_shapes(
92
self,
93
a: torch.Tensor,