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 4, 22:30 UTC
[MoE Refactor] Move the shared/fused expert output sum into MoERunnerBase (#35949)
Signed-off-by: Bill Nell <bnell@redhat.com> Co-authored-by: Robert Shaw <114415538+robertgshaw2-redhat@users.noreply.github.com>
bnellnm committed
Apr 20, 2026 at 12:28 UTC
726efe177bf22874743d11dfdfef9247dbfb5ff0
52 files changed
+322
-697
tests/kernels/moe/test_moe_layer.py
+17
-76
@@ -236,7 +236,6 @@ class MoETestConfig:
236
use_gate: bool
237
use_routed_input_transform: bool
238
enable_eplb: bool = False
239
- reduce_results: bool = False
239
backend: str | None = None
240
ep_size: int = 1
241
dp_size: int = 1
@@ -295,7 +294,6 @@ def generate_valid_test_configs(
294
use_shared_experts,
295
use_gate,
296
use_routed_input_transform,
298
- reduce_results,
297
) in product(
298
SHAPE_COMBOS,
299
NUM_EXPERTS,
@@ -304,7 +302,6 @@ def generate_valid_test_configs(
302
[False, True], # shared
303
[False, True], # gate
304
[False, True], # routed input exform
307
- [False, True], # reduce results
305
):
306
config = MoETestConfig(
307
shape[0], # m
@@ -318,7 +315,6 @@ def generate_valid_test_configs(
315
use_gate,
316
use_routed_input_transform,
317
enable_eplb,
321
- reduce_results,
318
backend,
319
ep_size,
320
dp_size,
@@ -395,18 +391,7 @@ def is_valid_config(config: MoETestConfig) -> tuple[bool, str | None]:
391
and config.backend.startswith("flashinfer_nvlink")
392
and not current_platform.has_device_capability(90)
393
):
398
- return False, "flashinfer_nvlink needs an H100+ GPUs"
399
-
400
- # reduce_results incompatibilities
401
- if config.reduce_results and config.use_shared_experts:
402
- return False, "reduce_results=True is not compatible with shared_experts=True"
403
-
404
- if config.reduce_results and config.quantization is not None:
405
- return (
406
- False,
407
- "reduce_results=True only tested with unquantized data types in "
408
- "order to limit number of tests run",
409
- )
394
+ return False, "flashinfer_nvlink needs H100+ GPUs"
395
396
# Backend-specific checks
397
if config.backend is not None:
@@ -448,10 +433,6 @@ def is_valid_config(config: MoETestConfig) -> tuple[bool, str | None]:
433
if config.enable_eplb and config.backend not in EPLB_SUPPORTED_BACKENDS:
434
return False, f"EPLB not supported with {config.backend}."
435
451
- world_size = config.tp_size * config.dp_size
452
- if config.reduce_results and world_size == 1:
453
- return False, "reduce_results=True only makes sense for multi-GPU tests"
454
-
436
if (
437
config.backend is not None
438
and config.backend.startswith("flashinfer_nvlink")
@@ -846,7 +827,6 @@ def make_fused_moe_layer(
827
tp_size: int,
828
ep_size: int,
829
dp_size: int,
849
- reduce_results: bool,
830
w1: torch.Tensor,
831
w2: torch.Tensor,
832
top_k: int,
@@ -874,7 +854,7 @@ def make_fused_moe_layer(
854
routed_input_transform: torch.nn.Module | None = None,
855
routed_output_transform: torch.nn.Module | None = None,
856
pcp_size: int | None = 1,
877
-) -> tuple[Callable, FusedMoE]:
857
+) -> FusedMoE:
858
quant_config, qw = make_quant_config(quantization, w1, w2, global_num_experts)
859
860
kwargs = dict()
@@ -887,8 +867,10 @@ def make_fused_moe_layer(
867
# Add gate and routed_input_transform if provided
868
if gate is not None:
869
kwargs["gate"] = gate
870
+
871
if routed_input_transform is not None:
872
kwargs["routed_input_transform"] = routed_input_transform
873
+ kwargs["routed_output_transform"] = routed_output_transform
874
875
layer = builder(
876
num_experts=global_num_experts,
@@ -896,7 +878,6 @@ def make_fused_moe_layer(
878
hidden_size=hidden_size,
879
intermediate_size=intermediate_size,
880
params_dtype=in_dtype,
899
- reduce_results=reduce_results,
881
renormalize=renormalize,
882
use_grouped_topk=use_grouped_topk,
883
num_expert_group=num_expert_group,
@@ -936,36 +917,7 @@ def make_fused_moe_layer(
917
918
layer.quant_method.process_weights_after_loading(layer)
919
939
- def _moe(
940
- hidden_states: torch.Tensor,
941
- router_logits: torch.Tensor,
942
- ) -> torch.Tensor:
943
- if shared_experts is None:
944
- final_shared_states = None
945
- final_hidden_states = layer(hidden_states, router_logits)
946
- else:
947
- final_shared_states, final_hidden_states = layer(
948
- hidden_states, router_logits
949
- )
950
-
951
- # Apply routed output transform if provided
952
- # (e.g., latent space -> original space)
953
- if routed_output_transform is not None:
954
- final_hidden_states = routed_output_transform(final_hidden_states)
955
-
956
- if shared_experts is not None:
957
- assert not reduce_results
958
- assert final_shared_states is not None
959
- final_hidden_states += final_shared_states
960
-
961
- if not reduce_results and layer.tp_size > 1:
962
- final_hidden_states = layer.maybe_all_reduce_tensor_model_parallel(
963
- final_hidden_states
964
- )
965
-
966
- return final_hidden_states
967
-
968
- return _moe, layer
920
+ return layer
921
922
923
def make_fake_moe_layer(
@@ -999,7 +951,6 @@ def make_fake_moe_layer(
951
tp_size: int = 1,
952
dp_size: int = 1,
953
ep_size: int = 1,
1002
- reduce_results: bool = False,
954
) -> Callable:
955
activation = MoEActivation.from_str(activation)
956
@@ -1101,7 +1052,7 @@ def make_fake_moe_layer(
1052
1053
1054
def _test_body_regular(
1104
- moe_fn: Callable,
1055
+ moe_layer: Callable,
1056
hidden_states: torch.Tensor,
1057
router_logits: torch.Tensor,
1058
vllm_config: VllmConfig,
@@ -1118,13 +1069,12 @@ def _test_body_regular(
1069
num_tokens=num_tokens,
1070
num_tokens_across_dp=num_tokens_across_dp,
1071
):
1121
- output = moe_fn(hidden_states, router_logits)
1072
+ output = moe_layer(hidden_states, router_logits)
1073
1074
return baseline_output, output
1075
1076
1077
def _test_body_eplb(
1127
- moe_fn: Callable,
1078
moe_layer: FusedMoE,
1079
hidden_states: torch.Tensor,
1080
router_logits: torch.Tensor,
@@ -1145,7 +1095,6 @@ def _test_body_eplb(
1095
n: int,
1096
top_k: int,
1097
shared_experts,
1148
- reduce_results: bool,
1098
gate: torch.nn.Module | None,
1099
routed_input_transform: torch.nn.Module | None,
1100
routed_output_transform: torch.nn.Module | None,
@@ -1161,7 +1110,7 @@ def _test_body_eplb(
1110
num_tokens=num_tokens,
1111
num_tokens_across_dp=num_tokens_across_dp,
1112
):
1164
- output_before = moe_fn(hidden_states, router_logits)
1113
+ output_before = moe_layer(hidden_states, router_logits)
1114
1115
# Create a fresh FusedMoE layer with enable_eplb=True
1116
# Delete the original layer's registration so the constructor can
@@ -1174,7 +1123,7 @@ def _test_body_eplb(
1123
# When using routed_input_transform, experts operate in latent space
1124
hidden_size_for_layer = k // 2 if routed_input_transform is not None else k
1125
1177
- moe_fn, moe_layer = make_fused_moe_layer(
1126
+ eplb_moe_layer = make_fused_moe_layer(
1127
quantization=quantization,
1128
use_ep=use_ep,
1129
hidden_size=hidden_size_for_layer,
@@ -1183,7 +1132,6 @@ def _test_body_eplb(
1132
tp_size=tp_size,
1133
ep_size=ep_size,
1134
dp_size=dp_size,
1186
- reduce_results=reduce_results,
1135
w1=w1,
1136
w2=w2,
1137
top_k=top_k,
@@ -1196,14 +1144,14 @@ def _test_body_eplb(
1144
)
1145
1146
# Necessary?
1199
- if moe_layer._expert_map is not None:
1200
- moe_layer._expert_map = moe_layer._expert_map.to(device)
1147
+ if eplb_moe_layer._expert_map is not None:
1148
+ eplb_moe_layer._expert_map = eplb_moe_layer._expert_map.to(device)
1149
1150
# All ranks must generate the same permutation
1151
initial_indices = torch.arange(num_experts, dtype=torch.long)
1152
shuffled_indices = initial_indices[torch.randperm(num_experts)]
1153
1206
- expert_weights = [list(moe_layer.get_expert_weights())]
1154
+ expert_weights = [list(eplb_moe_layer.get_expert_weights())]
1155
1156
communicator = create_eplb_communicator(
1157
group_coordinator=get_eplb_group(),
@@ -1227,7 +1175,7 @@ def _test_body_eplb(
1175
num_experts, dtype=torch.int32, device=device
1176
)
1177
1230
- moe_layer.set_eplb_state(
1178
+ eplb_moe_layer.set_eplb_state(
1179
moe_layer_idx=0,
1180
expert_load_view=torch.zeros(
1181
(1, num_experts),
@@ -1244,7 +1192,7 @@ def _test_body_eplb(
1192
),
1193
)
1194
1247
- moe_layer.eplb_state.should_record_tensor = torch.ones(
1195
+ eplb_moe_layer.eplb_state.should_record_tensor = torch.ones(
1196
(), dtype=torch.bool, device=device
1197
)
1198
@@ -1255,7 +1203,7 @@ def _test_body_eplb(
1203
num_tokens=num_tokens,
1204
num_tokens_across_dp=num_tokens_across_dp,
1205
):
1258
- output_after = moe_fn(hidden_states, router_logits)
1206
+ output_after = eplb_moe_layer(hidden_states, router_logits)
1207
1208
return output_before, output_after
1209
@@ -1274,7 +1222,6 @@ def _run_one_config(
1222
num_experts: int,
1223
top_k: int,
1224
quantization: str | None,
1277
- reduce_results: bool,
1225
backend: str | None,
1226
test_body_fn: Callable,
1227
use_shared_experts: bool,
@@ -1341,7 +1288,6 @@ def _run_one_config(
1288
tp_size=tp_size,
1289
ep_size=ep_size,
1290
dp_size=dp_size,
1344
- reduce_results=reduce_results,
1291
)
1292
1293
baseline_output = baseline_layer(hidden_states, router_logits)
@@ -1369,7 +1315,7 @@ def _run_one_config(
1315
hidden_size_for_layer = k // 2 if routed_input_transform is not None else k
1316
1317
# Create initial MoE layer
1372
- moe_fn, moe_layer = make_fused_moe_layer(
1318
+ moe_layer = make_fused_moe_layer(
1319
quantization=quantization,
1320
use_ep=use_ep,
1321
hidden_size=hidden_size_for_layer,
@@ -1378,7 +1324,6 @@ def _run_one_config(
1324
tp_size=tp_size,
1325
ep_size=ep_size,
1326
dp_size=dp_size,
1381
- reduce_results=reduce_results,
1327
w1=w1,
1328
w2=w2,
1329
top_k=top_k,
@@ -1402,7 +1347,6 @@ def _run_one_config(
1347
1348
# Call the test body function with all necessary context
1349
expected, actual = test_body_fn(
1405
- moe_fn=moe_fn,
1350
moe_layer=moe_layer,
1351
hidden_states=hidden_states,
1352
router_logits=router_logits,
@@ -1423,7 +1367,6 @@ def _run_one_config(
1367
m=m,
1368
top_k=top_k,
1369
shared_experts=shared_experts,
1426
- reduce_results=reduce_results,
1370
gate=gate,
1371
routed_input_transform=routed_input_transform,
1372
routed_output_transform=routed_output_transform,
@@ -1520,7 +1463,6 @@ def test_moe_layer_no_parallel(
1463
test_config.num_experts,
1464
test_config.top_k,
1465
test_config.quantization,
1523
- test_config.reduce_results,
1466
test_config.backend,
1467
_test_body_regular,
1468
use_shared_experts=test_config.use_shared_experts,
@@ -1578,7 +1520,6 @@ def _parallel_worker(
1520
test_config.num_experts,
1521
test_config.top_k,
1522
test_config.quantization,
1581
- test_config.reduce_results,
1523
test_config.backend,
1524
functools.partial(
1525
_test_body_config, test_config=test_config, cpu_group=cpu_group
@@ -1597,7 +1538,7 @@ def _parallel_worker(
1538
failed = failed + 1
1539
if verbosity > 0:
1540
traceback.print_exc()
1600
- print(f"\n{str(ex)}\nFAILED {ex.__class__}")
1541
+ print(f"\n{str(ex)}\nFAILED")
1542
else:
1543
print("F", end="")
1544
finally:
tests/kernels/moe/test_shared_fused_moe_routed_transform.py
+10
-26
@@ -165,7 +165,6 @@ def test_routed_input_transform_inside_vs_outside(
165
top_k=top_k,
166
hidden_size=latent_size,
167
intermediate_size=intermediate_size,
168
- reduce_results=False,
168
renormalize=True,
169
params_dtype=dtype,
170
tp_size=1,
@@ -183,7 +182,6 @@ def test_routed_input_transform_inside_vs_outside(
182
top_k=top_k,
183
hidden_size=latent_size,
184
intermediate_size=intermediate_size,
186
- reduce_results=False,
185
renormalize=True,
186
params_dtype=dtype,
187
tp_size=1,
@@ -212,34 +210,20 @@ def test_routed_input_transform_inside_vs_outside(
210
hidden_states = torch.randn(num_tokens, hidden_size, device="cuda", dtype=dtype)
211
router_logits = torch.randn(num_tokens, num_experts, device="cuda", dtype=dtype)
212
215
- # Clone inputs so any in-place modification by Method A
216
- # cannot affect Method B's computation.
217
- hidden_states_A = hidden_states.clone()
218
- router_logits_A = router_logits.clone()
219
-
213
with set_forward_context(None, vllm_config, num_tokens=num_tokens):
221
- shared_out_A, routed_out_A = moe_with_transform(
222
- hidden_states_A, router_logits_A
223
- )
214
+ # Method A: combined output (shared + routed)
215
+ combined_A = moe_with_transform(hidden_states, router_logits)
216
217
+ # Method B: manually transform, get routed output, add shared
218
transformed_hidden = routed_transform(hidden_states)
226
- shared_out_B, routed_out_B = moe_without_transform(
227
- transformed_hidden, router_logits
228
- )
229
-
230
- expected_shared_out = shared_experts(hidden_states)
219
+ routed_out_B = moe_without_transform(transformed_hidden, router_logits)
220
+ shared_out_B = shared_experts(hidden_states)
221
+ combined_B = shared_out_B + routed_out_B
222
232
- _assert_close(
233
- routed_out_A,
234
- routed_out_B,
235
- atol=1e-3,
236
- rtol=1e-3,
237
- label="Routed output: transform inside vs outside",
238
- )
239
- _assert_close(
240
- shared_out_A,
241
- expected_shared_out,
223
+ torch.testing.assert_close(
224
+ combined_A,
225
+ combined_B,
226
atol=1e-3,
227
rtol=1e-3,
244
- label="Shared expert output",
228
+ msg="Combined output should match: transform inside vs outside",
229
)
vllm/lora/layers/fused_moe.py
-3
@@ -592,9 +592,6 @@ class FusedMoEWithLoRA(BaseLayerWithLoRA):
592
def forward(self, *args, **kwargs):
593
return self.base_layer.forward(*args, **kwargs)
594
595
- def maybe_all_reduce_tensor_model_parallel(self, *args, **kwargs):
596
- return self.base_layer.maybe_all_reduce_tensor_model_parallel(*args, **kwargs)
597
-
595
@property
596
def quant_method(self):
597
return self.base_layer.quant_method
vllm/model_executor/layers/fused_moe/fused_marlin_moe.py
+1
-1
@@ -716,7 +716,7 @@ class MarlinExperts(MarlinExpertsBase):
716
):
717
assert self.w1_scale is not None
718
assert self.w2_scale is not None
719
- return fused_marlin_moe(
719
+ fused_marlin_moe(
720
hidden_states=hidden_states,
721
w1=w1,
722
w2=w2,
vllm/model_executor/layers/fused_moe/layer.py
+28
-32
@@ -230,11 +230,18 @@ class FusedMoE(PluggableLayer):
230
hidden_size: Input hidden state size of the transformer
231
intermediate_size: Intermediate size of the experts
232
params_dtype: Data type for the parameters.
233
- reduce_results: Whether to all_reduce on the output of the layer
233
renormalize: Whether to renormalize the logits in the fused_moe kernel
234
quant_config: Quantization configure.
235
enable_eplb: Whether to enable expert parallelism load balancer.
236
router_logits_dtype: Data type for router logits buffers.
237
+ routed_scaling_factor: A scaling factor that is applied to the topk_weights
238
+ by the router or the output of the layer depending
239
+ on the value of `apply_routed_scale_to_output`
240
+ apply_routed_scale_to_output: Determine whether or not `routed_scaling_factor`
241
+ is applied to the topk_weights or to the experts
242
+ output. It is applied to the experts output
243
+ instead of the topk_weights when this feature is
244
+ not supported by the router (or the experts).
245
"""
246
247
# --8<-- [end:fused_moe]
@@ -246,7 +253,6 @@ class FusedMoE(PluggableLayer):
253
hidden_size: int,
254
intermediate_size: int,
255
params_dtype: torch.dtype | None = None,
249
- reduce_results: bool = False,
256
renormalize: bool = True,
257
use_grouped_topk: bool = False,
258
num_expert_group: int | None = None,
@@ -274,12 +280,12 @@ class FusedMoE(PluggableLayer):
280
gate: torch.nn.Module | None = None,
281
shared_experts: torch.nn.Module | None = None,
282
routed_input_transform: torch.nn.Module | None = None,
283
+ routed_output_transform: torch.nn.Module | None = None,
284
+ apply_routed_scale_to_output: bool = False,
285
zero_expert_type: str | None = None,
286
):
287
super().__init__()
288
281
- self._routed_input_transform = routed_input_transform
282
-
289
if params_dtype is None:
290
params_dtype = torch.get_default_dtype()
291
self.params_dtype = params_dtype
@@ -425,7 +431,6 @@ class FusedMoE(PluggableLayer):
431
432
assert intermediate_size % self.tp_size == 0
433
intermediate_size_per_partition = intermediate_size // self.tp_size
428
- self.reduce_results = reduce_results
434
self.renormalize = renormalize
435
436
# TODO(bnell): these attributes are only used by monolithic kernels.
@@ -437,7 +442,14 @@ class FusedMoE(PluggableLayer):
442
self.topk_group = topk_group
443
self.custom_routing_function = custom_routing_function
444
self.scoring_func = scoring_func
440
- self.routed_scaling_factor = routed_scaling_factor
445
+ # When apply_routed_scale_to_output is True, we set the scaling factor
446
+ # to 1.0 so it ends up being a nop. Applying the scale will be handled
447
+ # by the runner in this case.
448
+ # The member variable must be set in the same way as the router since
449
+ # some quantization methods can access it.
450
+ self.routed_scaling_factor = (
451
+ routed_scaling_factor if not apply_routed_scale_to_output else 1.0
452
+ )
453
self.e_score_correction_bias = e_score_correction_bias
454
# TODO(bnell): end attributes
455
@@ -456,7 +468,7 @@ class FusedMoE(PluggableLayer):
468
topk_group=topk_group,
469
custom_routing_function=custom_routing_function,
470
scoring_func=scoring_func,
459
- routed_scaling_factor=routed_scaling_factor,
471
+ routed_scaling_factor=self.routed_scaling_factor,
472
e_score_correction_bias=e_score_correction_bias,
473
num_fused_shared_experts=self.num_fused_shared_experts,
474
enable_eplb=enable_eplb,
@@ -578,12 +590,18 @@ class FusedMoE(PluggableLayer):
590
layer_name=self.layer_name,
591
moe_config=self.moe_config,
592
router=self.router,
581
- routed_input_transform=self._routed_input_transform,
593
gate=gate,
594
shared_experts=shared_experts,
595
quant_method=self.quant_method,
585
- reduce_results=self.reduce_results,
596
enable_dbo=self.vllm_config.parallel_config.enable_dbo,
597
+ routed_input_transform=routed_input_transform,
598
+ routed_output_transform=routed_output_transform,
599
+ # When apply_routed_scale_to_output is True, we allow
600
+ # the scaling factor to be passed to the runner, otherwise
601
+ # we pass 1.0 so it ends up being a nop.
602
+ routed_scaling_factor=routed_scaling_factor
603
+ if apply_routed_scale_to_output
604
+ else 1.0,
605
)
606
607
# TODO(bnell): This method is provided as a hook so vllm/lora/layers/fused_moe.py
@@ -1514,32 +1532,11 @@ class FusedMoE(PluggableLayer):
1532
self.ensure_moe_quant_config_init()
1533
return self.quant_method.moe_quant_config
1534
1517
- def must_reduce_shared_expert_outputs(self) -> bool:
1518
- """
1519
- The shared_experts are typically computed using the RowParallelLinear
1520
- layer. The result of this function is typically used as
1521
- the reduce_results argument to the module.
1522
- When just tensor-parallel is used, it is not required to reduce
1523
- the shared_experts results immediately. Instead we reduce at the
1524
- once at the end of the MoE op. (Refer to DeepSeekV2MoE module)
1525
- With EP and all2all kernels - this is no longer viable as all
1526
- GPU ranks in DP, produce the complete set of hidden_states.
1527
- Therefore it is required that we reduce the shared_experts output
1528
- early.
1529
- """
1530
- return self.runner.must_reduce_shared_expert_outputs()
1531
-
1532
- def maybe_all_reduce_tensor_model_parallel(self, final_hidden_states: torch.Tensor):
1533
- """
1534
- Some combine kernels reduce across GPU ranks by default.
1535
- """
1536
- return self.runner.maybe_all_reduce_tensor_model_parallel(final_hidden_states)
1537
-
1535
def forward(
1536
self,
1537
hidden_states: torch.Tensor,
1538
router_logits: torch.Tensor,
1542
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1539
+ ) -> torch.Tensor:
1540
return self.runner.forward(
1541
hidden_states,
1542
router_logits,
@@ -1613,7 +1610,6 @@ class FusedMoE(PluggableLayer):
1610
f"intermediate_size_per_partition={self.intermediate_size_per_partition}, " # noqa: E501
1611
f"tp_size={self.tp_size},\n"
1612
f"ep_size={self.ep_size}, "
1616
- f"reduce_results={self.reduce_results}, "
1613
)
1614
1615
return s
vllm/model_executor/layers/fused_moe/modular_kernel.py
+1
-1
@@ -1261,7 +1261,7 @@ class FusedMoEKernelModularImpl:
1261
topk_ids: torch.Tensor,
1262
apply_router_weight_on_input: bool,
1263
shared_experts_input: torch.Tensor | None,
1264
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
1264
+ ) -> torch.Tensor:
1265
"""
1266
The _finalize method is a wrapper around self.prepare_finalize.finalize
1267
that handles DBO, async and shared expert overlap.
vllm/model_executor/layers/fused_moe/runner/default_moe_runner.py
-4
@@ -37,10 +37,6 @@ class DefaultMoERunner(MoERunnerBase):
37
for different configurations (e.g., with/without shared experts, gates, etc.).
38
"""
39
40
- @property
41
- def reduce_results(self) -> bool:
42
- return self._reduce_results
43
-
40
@property
41
def do_naive_dispatch_combine(self) -> bool:
42
return (
vllm/model_executor/layers/fused_moe/runner/moe_runner.py
+1
-12
@@ -26,18 +26,7 @@ class MoERunner(ABC):
26
self,
27
hidden_states: torch.Tensor,
28
router_logits: torch.Tensor,
29
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
30
- raise NotImplementedError
31
-
32
- @abstractmethod
33
- def must_reduce_shared_expert_outputs(self) -> bool:
34
- raise NotImplementedError
35
-
36
- @abstractmethod
37
- def maybe_all_reduce_tensor_model_parallel(
38
- self,
39
- final_hidden_states: torch.Tensor,
40
- ):
29
+ ) -> torch.Tensor:
30
raise NotImplementedError
31
32
@abstractmethod
vllm/model_executor/layers/fused_moe/runner/moe_runner_base.py
+175
-86
@@ -81,7 +81,9 @@ def _resolve_layer_name(layer_name: str | LayerName) -> str:
81
82
# Note: _moe_forward and _moe_forward_shared should not contain any
83
# implementation details, They should merely pass along control to
84
-# the runner's 'forward_dispatch' method.
84
+# the runner's '_forward_dispatch' method.
85
+# These functions should never be called directly since they do not
86
+# include all the functionality of the MoE layer.
87
def _moe_forward(
88
hidden_states: torch.Tensor,
89
router_logits: torch.Tensor,
@@ -89,7 +91,7 @@ def _moe_forward(
91
layer_name: _layer_name_type,
92
) -> torch.Tensor:
93
layer = get_layer_from_name(_resolve_layer_name(layer_name))
92
- return layer.runner.forward_dispatch(
94
+ return layer.runner._forward_dispatch(
95
layer,
96
hidden_states,
97
router_logits,
@@ -113,7 +115,7 @@ def _moe_forward_shared(
115
layer_name: _layer_name_type,
116
) -> tuple[torch.Tensor, torch.Tensor]:
117
layer = get_layer_from_name(_resolve_layer_name(layer_name))
116
- return layer.runner.forward_dispatch(
118
+ return layer.runner._forward_dispatch(
119
layer,
120
hidden_states,
121
router_logits,
@@ -143,7 +145,7 @@ def _moe_forward_shared_fake(
145
direct_register_custom_op(
146
op_name="moe_forward",
147
op_func=_moe_forward,
146
- mutates_args=["hidden_states"], # is this still true?
148
+ mutates_args=["hidden_states"],
149
fake_impl=_moe_forward_fake,
150
tags=(torch.Tag.needs_fixed_stride_order,),
151
)
@@ -157,6 +159,15 @@ direct_register_custom_op(
159
)
160
161
162
+def _unpack(
163
+ result: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
164
+) -> tuple[torch.Tensor | None, torch.Tensor]:
165
+ if isinstance(result, tuple):
166
+ return result
167
+ else:
168
+ return (None, result)
169
+
170
+
171
class MoERunnerBase(MoERunner):
172
"""
173
Abstract base class providing common functionality for MoE runner implementations.
@@ -174,7 +185,6 @@ class MoERunnerBase(MoERunner):
185
allowing flexibility in the actual MoE computation implementation.
186
187
Key abstract methods that subclasses must implement:
177
- - reduce_results: Determines whether results should be reduced across ranks
188
- _forward_impl: The core MoE computation logic specific to each runner type
189
"""
190
@@ -187,17 +197,23 @@ class MoERunnerBase(MoERunner):
197
gate: torch.nn.Module | None,
198
shared_experts: torch.nn.Module | None,
199
quant_method: FusedMoEMethodBase,
190
- reduce_results: bool,
200
enable_dbo: bool,
201
+ routed_output_transform: torch.nn.Module | None = None,
202
+ routed_scaling_factor: float = 1.0,
203
):
204
super().__init__()
205
self.moe_config = moe_config
206
self.router = router
207
self.routed_input_transform = routed_input_transform
208
+ self.routed_output_transform = routed_output_transform
209
+ self.routed_scaling_factor = routed_scaling_factor
210
self.gate = gate
211
self.quant_method = quant_method
199
- self._reduce_results = reduce_results
212
self.enable_dbo = enable_dbo
213
+ self._fused_output_is_reduced = (
214
+ self.quant_method.moe_kernel is not None
215
+ and self.quant_method.moe_kernel.output_is_reduced()
216
+ )
217
218
self._shared_experts: SharedExperts | None = None
219
if shared_experts is not None:
@@ -209,7 +225,6 @@ class MoERunnerBase(MoERunner):
225
# called, i.e. by a MK or by the MoERunner.
226
# Once the MK can be created upfront, we can just pass in the proper
227
# flags derived from the quant_method's MK.
212
- reduce_results=reduce_results,
228
quant_method=quant_method,
229
enable_dbo=enable_dbo,
230
)
@@ -217,7 +232,7 @@ class MoERunnerBase(MoERunner):
232
# Needed for string -> FusedMoE layer lookup in custom ops.
233
self.layer_name = layer_name
234
220
- self.forward_entry = self._select_forward()
235
+ self._forward_entry = self._select_forward()
236
237
def _select_forward(self) -> Callable:
238
if current_platform.is_tpu() or current_platform.is_cpu():
@@ -245,38 +260,6 @@ class MoERunnerBase(MoERunner):
260
def is_internal_router(self) -> bool:
261
return self.gate is not None
262
248
- @property
249
- @abstractmethod
250
- def reduce_results(self) -> bool:
251
- raise NotImplementedError
252
-
253
- def must_reduce_shared_expert_outputs(self) -> bool:
254
- """
255
- The shared_experts are typically computed using the RowParallelLinear
256
- layer. The result of this function is typically used as
257
- the reduce_results argument to the module.
258
- When just tensor-parallel is used, it is not required to reduce
259
- the shared_experts results immediately. Instead we reduce at the
260
- once at the end of the MoE op. (Refer to DeepSeekV2MoE module)
261
- With EP and all2all kernels - this is no longer viable as all
262
- GPU ranks in DP, produce the complete set of hidden_states.
263
- Therefore it is required that we reduce the shared_experts output
264
- early.
265
- """
266
- return (
267
- self.quant_method.moe_kernel is not None
268
- and self.quant_method.moe_kernel.output_is_reduced()
269
- )
270
-
271
- def maybe_all_reduce_tensor_model_parallel(self, final_hidden_states: torch.Tensor):
272
- """
273
- Some combine kernels reduce across GPU ranks by default.
274
- """
275
- if self.must_reduce_shared_expert_outputs():
276
- return final_hidden_states
277
- else:
278
- return tensor_model_parallel_all_reduce(final_hidden_states)
279
-
263
def apply_routed_input_transform(
264
self, hidden_states: torch.Tensor
265
) -> tuple[torch.Tensor, torch.Tensor | None]:
@@ -286,10 +269,6 @@ class MoERunnerBase(MoERunner):
269
is saved separately so shared experts get [S, hidden_size] while
270
routed experts get the transformed [S, moe_latent_size].
271
289
- TODO: For latent MoE bandwidth optimization, fc2_latent_proj could be
290
- moved inside SharedFusedMoE to all-reduce on the smaller latent
291
- dimension.
292
-
272
Returns (possibly transformed) hidden states and the input for shared
273
experts (or None if there are no shared experts).
274
"""
@@ -306,33 +285,79 @@ class MoERunnerBase(MoERunner):
285
hidden_states if self._shared_experts is not None else None,
286
)
287
309
- def _maybe_reduce_output(
288
+ def apply_routed_output_transform(
289
self,
311
- states: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
312
- trunc_sizes: list[int],
313
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
314
- def trunc(x: torch.Tensor, trunc_size: int) -> torch.Tensor:
315
- return x[..., :trunc_size]
290
+ fused_output: torch.Tensor,
291
+ ) -> torch.Tensor:
292
+ """Apply transform to routed expert output (e.g., latent to full dim).
293
+
294
+ Used by latent MoE models (e.g., NemotronH) where routed experts
295
+ operate in a compressed latent space and need projection back to
296
+ the full hidden dimension before combining with shared expert output.
297
+ """
298
+ if self.routed_output_transform is not None:
299
+ r = self.routed_output_transform(fused_output)
300
+ fused_output = r[0] if isinstance(r, tuple) else r
301
+ return fused_output
302
317
- def reduce_and_trunc(x: torch.Tensor, trunc_size: int) -> torch.Tensor:
318
- return trunc(self.maybe_all_reduce_tensor_model_parallel(x), trunc_size)
303
+ def _maybe_apply_routed_scale_to_output(
304
+ self,
305
+ shared_output: torch.Tensor | None,
306
+ fused_output: torch.Tensor,
307
+ ) -> tuple[torch.Tensor | None, torch.Tensor]:
308
+ """Apply routed_scaling_factor to the output with FP16 overflow
309
+ protection.
310
+
311
+ Scale the fused expert output by routed_scaling_factor. For FP16,
312
+ avoid overflow by dividing shared_output by the scale instead
313
+ (the decoder layer compensates with matching divisions).
314
+ """
315
+ if self.routed_scaling_factor != 1.0:
316
+ if fused_output.dtype != torch.float16:
317
+ fused_output *= self.routed_scaling_factor
318
+ elif shared_output is not None:
319
+ shared_output *= 1.0 / self.routed_scaling_factor
320
+ return shared_output, fused_output
321
+
322
+ def _maybe_reduce_shared_expert_output(
323
+ self,
324
+ shared_output: torch.Tensor | None,
325
+ ) -> torch.Tensor | None:
326
+ """All-reduce shared expert output when the combine kernel already
327
+ reduced fused output.
328
+
329
+ This is the "early" all-reduce path. When the combine kernel produces
330
+ already-reduced fused output, shared output must be reduced separately
331
+ to match.
332
+ """
333
+ if self._fused_output_is_reduced:
334
+ assert shared_output is not None
335
+ shared_output = tensor_model_parallel_all_reduce(shared_output)
336
+ return shared_output
337
338
+ def _maybe_reduce_final_output(
339
+ self,
340
+ states: torch.Tensor,
341
+ trunc_size: int,
342
+ ) -> torch.Tensor:
343
+ """Truncate padded dimensions and all-reduce the combined output.
344
+
345
+ This is the "late" all-reduce path. When neither fused nor shared
346
+ output was individually reduced, the combined sum is all-reduced
347
+ here. Skipped when sequence-parallel is active (SP handles its
348
+ own reduction) or when the early path already reduced both outputs.
349
+ """
350
+ # We don't need to reduce the final output if:
351
+ # - We are not running with TP or DP
352
+ # - The MK already reduced the fused output itself.
353
if (
354
not self.moe_config.is_sequence_parallel
322
- and self.reduce_results
355
and (self.moe_config.tp_size > 1 or self.moe_config.ep_size > 1)
356
+ and not self._fused_output_is_reduced
357
):
325
- func = reduce_and_trunc
326
- else:
327
- func = trunc
358
+ states = tensor_model_parallel_all_reduce(states)
359
329
- if isinstance(states, tuple):
330
- return tuple(
331
- [func(s, trunc_size) for s, trunc_size in zip(states, trunc_sizes)]
332
- )
333
- else:
334
- assert len(trunc_sizes) == 1
335
- return func(states, trunc_sizes[0])
360
+ return states[..., :trunc_size]
361
362
def _encode_layer_name(self) -> str | LayerName:
363
if _USE_LAYERNAME:
@@ -349,7 +374,15 @@ class MoERunnerBase(MoERunner):
374
self,
375
shared_experts_input: torch.Tensor | None,
376
hidden_states: torch.Tensor,
352
- ) -> tuple[torch.Tensor, list[int]]:
377
+ ) -> tuple[torch.Tensor, int]:
378
+ """Pad hidden_states to moe_config.hidden_dim and compute the
379
+ original dimension for later truncation.
380
+
381
+ For latent MoE, the routed hidden_states may be smaller than
382
+ hidden_dim. Padding ensures uniform tensor sizes through the
383
+ fused MoE kernel. The returned trunc_size is used by
384
+ _maybe_reduce_final_output to strip the padding from the result.
385
+ """
386
shared_experts_hidden_dim = (
387
shared_experts_input.shape[-1] if shared_experts_input is not None else 0
388
)
@@ -365,10 +398,10 @@ class MoERunnerBase(MoERunner):
398
value=0.0,
399
)
400
368
- if self._shared_experts is not None:
369
- orig_hidden_dims = [shared_experts_hidden_dim, transformed_hidden_dim]
401
+ if self.routed_output_transform is not None and shared_experts_hidden_dim > 0:
402
+ orig_hidden_dims = shared_experts_hidden_dim
403
else:
371
- orig_hidden_dims = [transformed_hidden_dim]
404
+ orig_hidden_dims = transformed_hidden_dim
405
406
return hidden_states, orig_hidden_dims
407
@@ -388,6 +421,12 @@ class MoERunnerBase(MoERunner):
421
router_logits: torch.Tensor,
422
shared_experts_input: torch.Tensor | None,
423
) -> tuple[torch.Tensor | None, torch.Tensor]:
424
+ """Run expert routing and the fused MoE kernel via the quant method.
425
+
426
+ Orchestrates shared expert execution (before/after), expert selection
427
+ via the router, and the actual fused MoE computation. Returns
428
+ (shared_expert_output, fused_expert_output).
429
+ """
430
# Run this before quant_method to avoid inplace issues.
431
# TODO(bnell): probably not needed anymore since inplace is
432
# disabled when shared experts are present.
@@ -428,6 +467,13 @@ class MoERunnerBase(MoERunner):
467
)
468
469
def _sequence_parallel_context(self):
470
+ """Return a context manager for sequence-parallel token
471
+ redistribution.
472
+
473
+ When sequence parallelism is active, returns a context that handles
474
+ local size tracking for proper token scatter/gather. Otherwise
475
+ returns a no-op context.
476
+ """
477
ctx = get_forward_context()
478
return (
479
ctx.dp_metadata.sp_local_sizes(self.moe_config.sp_size)
@@ -448,22 +494,25 @@ class MoERunnerBase(MoERunner):
494
495
def _maybe_add_zero_expert_output(
496
self,
451
- result: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
452
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
497
+ result: torch.Tensor,
498
+ ) -> torch.Tensor:
499
+ """Add the zero expert's contribution to the final result.
500
+
501
+ When a ZeroExpertRouter is used, it computes a bias-like output
502
+ from the "zero expert" that is added to the combined routed+shared
503
+ expert output.
504
+ """
505
if isinstance(self.router, ZeroExpertRouter):
506
zero_expert_output = self.router.zero_expert_output
507
assert zero_expert_output is not None
456
- if isinstance(result, tuple):
457
- result = (result[0], result[1] + zero_expert_output)
458
- else:
459
- result = result + zero_expert_output
508
+ result = result + zero_expert_output
509
return result
510
511
def forward(
512
self,
513
hidden_states: torch.Tensor,
514
router_logits: torch.Tensor,
466
- ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
515
+ ) -> torch.Tensor:
516
"""Invoke the fused moe layer.
517
518
Input:
@@ -472,55 +521,88 @@ class MoERunnerBase(MoERunner):
521
522
Output:
523
- The new hidden_states.
475
- or
476
- - A tuple of (shared experts output, new hidden_states).
524
525
Calling sequence
526
- forward
480
- - self.forward_entry (_moe_forward or _moe_forward_shared custom op)
481
- - forward_dispatch
527
+ - self._forward_entry (_moe_forward or _moe_forward_shared custom op)
528
+ - _forward_dispatch
529
- _forward_impl
530
531
Note: The existence of _moe_forward and _moe_forward_shared custom ops are due
532
to the following reasons:
533
1. the chunking loop in ChunkingMoERunner._forward_impl cannot be compiled by
534
torch.compile
488
- 2. pytorch cannot handle union types in custom op signatures so _moe_forward
489
- and _moe_forward_shared must be split.
535
+ 2. pytorch cannot handle union types in custom op signatures so
536
+ _moe_forward and _moe_forward_shared must be split.
537
538
If ChunkingMoERunner._forward_impl can be implemented via torch.scan we can
539
potentially get rid of _moe_forward and _moe_forward_shared and collapse the
540
whole sequence into the 'forward' method.
541
"""
542
496
- # Apply transform for routed experts (e.g., latent projection for latent MoE)
543
+ # Apply transform for routed experts (e.g., latent projection
544
+ # for latent MoE)
545
hidden_states, shared_experts_input = self.apply_routed_input_transform(
546
hidden_states
547
)
548
501
- hidden_states, og_hidden_dims = self._maybe_pad_hidden_states(
549
+ hidden_states, og_hidden_dim = self._maybe_pad_hidden_states(
550
shared_experts_input,
551
hidden_states,
552
)
553
506
- fused_output = self.forward_entry(
554
+ result = self._forward_entry(
555
hidden_states,
556
router_logits,
557
shared_experts_input,
558
self._encode_layer_name(),
559
)
560
513
- result = self._maybe_reduce_output(fused_output, og_hidden_dims)
561
+ #
562
+ # Note: there are two all-reduce points below. They are mutually
563
+ # exclusive, controlled by _fused_output_is_reduced
564
+ # - When True: the combine kernel already reduced fused_output,
565
+ # so we reduce shared_output here to match, then skip the
566
+ # all-reduce in _maybe_reduce_final_output.
567
+ # - When False: neither output is reduced yet, so we combine
568
+ # them first and all-reduce the sum in _maybe_reduce_final_output.
569
+
570
+ # Extract outputs from result
571
+ shared_output, fused_output = _unpack(result)
572
+
573
+ # If combine kernel already reduced fused, reduce shared to match.
574
+ # See note above re: the two all-reduce points.
575
+ shared_output = self._maybe_reduce_shared_expert_output(shared_output)
576
+
577
+ shared_output, fused_output = self._maybe_apply_routed_scale_to_output(
578
+ shared_output, fused_output
579
+ )
580
+
581
+ # Apply output transform (e.g. latent -> full dim)
582
+ fused_output = self.apply_routed_output_transform(fused_output)
583
+
584
+ if shared_output is not None:
585
+ result = shared_output + fused_output
586
+ else:
587
+ result = fused_output
588
+
589
+ result = self._maybe_reduce_final_output(result, og_hidden_dim)
590
591
return self._maybe_add_zero_expert_output(result)
592
517
- def forward_dispatch(
593
+ def _forward_dispatch(
594
self,
595
layer: torch.nn.Module,
596
hidden_states: torch.Tensor,
597
router_logits: torch.Tensor,
598
shared_experts_input: torch.Tensor | None,
599
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
600
+ """Entry point called by the custom op to run the MoE computation.
601
+
602
+ Handles pre-dispatch setup (gate application, external shared expert
603
+ triggering, quant config init) then delegates to _forward_impl within
604
+ the sequence-parallel context.
605
+ """
606
# TODO(bnell): this can be removed after MK migration is complete.
607
layer.ensure_moe_quant_config_init()
608
@@ -549,4 +631,11 @@ class MoERunnerBase(MoERunner):
631
router_logits: torch.Tensor,
632
shared_experts_input: torch.Tensor | None,
633
) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
634
+ """Core MoE computation to be implemented by subclasses.
635
+
636
+ Performs expert routing, fused MoE kernel execution, and shared
637
+ expert computation. Returns a single tensor (fused output only)
638
+ or a tuple of (shared_output, fused_output) when shared experts
639
+ are present.
640
+ """
641
raise NotImplementedError
vllm/model_executor/layers/fused_moe/runner/moe_runner_factory.py
+4
-2
@@ -29,8 +29,9 @@ def create_moe_runner(
29
gate: torch.nn.Module | None,
30
shared_experts: SharedExperts | None,
31
quant_method: FusedMoEMethodBase,
32
- reduce_results: bool,
32
enable_dbo: bool,
33
+ routed_output_transform: torch.nn.Module | None = None,
34
+ routed_scaling_factor: float = 1.0,
35
) -> MoERunner:
36
return DefaultMoERunner(
37
layer_name,
@@ -40,6 +41,7 @@ def create_moe_runner(
41
gate,
42
shared_experts,
43
quant_method,
43
- reduce_results,
44
enable_dbo,
45
+ routed_output_transform=routed_output_transform,
46
+ routed_scaling_factor=routed_scaling_factor,
47
)
vllm/model_executor/layers/fused_moe/runner/shared_experts.py
-18
@@ -5,10 +5,6 @@ from enum import IntEnum
5
import torch
6
7
import vllm.envs as envs
8
-from vllm.distributed import (
9
- get_tensor_model_parallel_world_size,
10
- tensor_model_parallel_all_reduce,
11
-)
8
from vllm.logger import init_logger
9
from vllm.model_executor.layers.fused_moe.config import (
10
FusedMoEConfig,
@@ -48,7 +44,6 @@ class SharedExperts:
44
layer: torch.nn.Module,
45
moe_config: FusedMoEConfig,
46
quant_method: QuantizeMethodBase,
51
- reduce_results: bool,
47
enable_dbo: bool,
48
):
49
from vllm.model_executor.layers.fused_moe.fused_moe_method_base import (
@@ -68,7 +63,6 @@ class SharedExperts:
63
self._layer = layer
64
self._moe_config = moe_config
65
self._quant_method = quant_method
71
- self._reduce_results = reduce_results
66
67
# Allow disabling of the separate shared experts stream for
68
# debug purposes.
@@ -139,18 +133,6 @@ class SharedExperts:
133
134
return output
135
142
- def _maybe_reduce_shared_out(self, shared_out: torch.Tensor) -> torch.Tensor:
143
- # Reduce shared expert outputs if necessary, since the MLP
144
- # should have been created with reduce_results=False.
145
- if (
146
- self._reduce_results
147
- and self._quant_method.moe_kernel is not None
148
- and self._quant_method.moe_kernel.output_is_reduced()
149
- and get_tensor_model_parallel_world_size() > 1
150
- ):
151
- shared_out = tensor_model_parallel_all_reduce(shared_out)
152
- return shared_out
153
-
136
@property
137
def _output_idx(self) -> int:
138
return dbo_current_ubatch_id() if self.enable_dbo else 0
vllm/model_executor/layers/fused_moe/shared_fused_moe.py
+2
-6
@@ -18,12 +18,8 @@ class SharedFusedMoE(FusedMoE):
18
self,
19
hidden_states: torch.Tensor,
20
router_logits: torch.Tensor,
21
- ) -> tuple[torch.Tensor, torch.Tensor]:
22
- result = super().forward(
21
+ ) -> torch.Tensor:
22
+ return super().forward(
23
hidden_states=hidden_states,
24
router_logits=router_logits,
25
)
26
- if self.shared_experts is None:
27
- return None, result
28
- else:
29
- return result
vllm/model_executor/models/AXK1.py
+5
-30
@@ -100,7 +100,7 @@ class AXK1MoE(nn.Module):
100
self.tp_size = get_tensor_model_parallel_world_size()
101
self.tp_rank = get_tensor_model_parallel_rank()
102
103
- self.routed_scaling_factor = config.routed_scaling_factor
103
+ self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
104
105
self.ep_group = get_ep_group().device_group
106
self.ep_rank = get_ep_group().rank_in_group
@@ -170,7 +170,6 @@ class AXK1MoE(nn.Module):
170
top_k=config.num_experts_per_tok,
171
hidden_size=config.hidden_size,
172
intermediate_size=config.moe_intermediate_size,
173
- reduce_results=False,
173
renormalize=config.norm_topk_prob,
174
quant_config=quant_config,
175
use_grouped_topk=True,
@@ -180,9 +179,8 @@ class AXK1MoE(nn.Module):
179
scoring_func=config.scoring_func,
180
# we do scaling outside, set factor to 1.0 to avoid double mul
181
# aiter applies routed_scaling_factor internally
183
- routed_scaling_factor=1.0
184
- if not self.is_rocm_aiter_moe_enabled
185
- else self.routed_scaling_factor,
182
+ routed_scaling_factor=self.routed_scaling_factor,
183
+ apply_routed_scale_to_output=not self.is_rocm_aiter_moe_enabled,
184
e_score_correction_bias=self.gate.e_score_correction_bias,
185
enable_eplb=self.enable_eplb,
186
num_redundant_experts=self.n_redundant_experts,
@@ -204,43 +202,20 @@ class AXK1MoE(nn.Module):
202
hidden_states = sequence_parallel_chunk(hidden_states)
203
204
if self.experts.is_internal_router:
207
- # In this case, the gate/router runs inside the FusedMoE class
208
- fused_moe_out = self.experts(
205
+ final_hidden_states = self.experts(
206
hidden_states=hidden_states, router_logits=hidden_states
207
)
208
else:
212
- # router_logits: (num_tokens, n_experts)
209
router_logits, _ = self.gate(hidden_states)
214
- fused_moe_out = self.experts(
210
+ final_hidden_states = self.experts(
211
hidden_states=hidden_states, router_logits=router_logits
212
)
213
218
- shared_output, final_hidden_states = fused_moe_out
219
- if self.shared_experts is None:
220
- assert shared_output is None
221
-
222
- # Fix FP16 overflow
223
- # See AXK1DecoderLayer for more details.
224
- if hidden_states.dtype != torch.float16:
225
- if not self.is_rocm_aiter_moe_enabled:
226
- final_hidden_states *= self.routed_scaling_factor
227
- elif self.shared_experts is not None:
228
- assert shared_output is not None
229
- shared_output *= 1.0 / self.routed_scaling_factor
230
-
231
- if self.shared_experts is not None:
232
- assert shared_output is not None
233
- final_hidden_states += shared_output
234
-
214
if self.is_sequence_parallel:
215
final_hidden_states = tensor_model_parallel_all_gather(
216
final_hidden_states, 0
217
)
218
final_hidden_states = final_hidden_states[:num_tokens]
240
- elif self.tp_size > 1:
241
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
242
- final_hidden_states
243
- )
219
220
return final_hidden_states.view(num_tokens, hidden_dim)
221
vllm/model_executor/models/afmoe.py
+1
-12
@@ -131,7 +131,6 @@ class AfmoeMoE(nn.Module):
131
top_k=config.num_experts_per_tok,
132
hidden_size=config.hidden_size,
133
intermediate_size=config.moe_intermediate_size,
134
- reduce_results=False,
134
renormalize=self.route_norm if self.score_func == "sigmoid" else False,
135
quant_config=quant_config,
136
use_grouped_topk=True,
@@ -152,20 +151,10 @@ class AfmoeMoE(nn.Module):
151
152
router_logits = self.gate(hidden_states.to(dtype=torch.float32))
153
155
- fused_moe_out = self.experts(
154
+ final_hidden_states = self.experts(
155
hidden_states=hidden_states, router_logits=router_logits
156
)
157
159
- if self.shared_experts is not None:
160
- shared_output, final_hidden_states = fused_moe_out
161
- final_hidden_states = final_hidden_states + shared_output
162
- else:
163
- final_hidden_states = fused_moe_out
164
- if self.tp_size > 1:
165
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
166
- final_hidden_states
167
- )
168
-
158
return final_hidden_states.view(num_tokens, hidden_dim)
159
160
vllm/model_executor/models/aria.py
+1
-7
@@ -283,7 +283,6 @@ class AriaTextMoELayer(nn.Module):
283
hidden_size=config.hidden_size,
284
intermediate_size=config.intermediate_size,
285
quant_config=quant_config,
286
- reduce_results=True,
286
prefix=f"{prefix}.experts",
287
)
288
@@ -301,12 +300,7 @@ class AriaTextMoELayer(nn.Module):
300
301
router_output = torch.nn.functional.linear(hidden_states, self.router_weight)
302
304
- sparse_expert_output = self.experts(hidden_states, router_output)
305
-
306
- if self.shared_experts is not None:
307
- return sparse_expert_output[0] + sparse_expert_output[1]
308
- else:
309
- return sparse_expert_output
303
+ return self.experts(hidden_states, router_output)
304
305
306
class AriaTextDecoderLayer(LlamaDecoderLayer):
vllm/model_executor/models/bailing_moe.py
+1
-16
@@ -291,7 +291,6 @@ class BailingMoE(nn.Module):
291
top_k=self.top_k,
292
hidden_size=self.hidden_size,
293
intermediate_size=config.moe_intermediate_size,
294
- reduce_results=False,
294
renormalize=self.norm_expert_prob,
295
quant_config=quant_config,
296
prefix=f"{prefix}.experts",
@@ -301,6 +300,7 @@ class BailingMoE(nn.Module):
300
topk_group=self.topk_group,
301
use_grouped_topk=self.use_grouped_topk,
302
router_logits_dtype=self.router_dtype,
303
+ routed_scaling_factor=self.routed_scaling_factor,
304
)
305
306
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
@@ -314,21 +314,6 @@ class BailingMoE(nn.Module):
314
final_hidden_states = self.experts(
315
hidden_states=hidden_states, router_logits=router_logits
316
)
317
-
318
- if self.shared_experts is not None:
319
- shared_output, final_hidden_states = final_hidden_states
320
- else:
321
- shared_output = None
322
-
323
- final_hidden_states *= self.routed_scaling_factor
324
-
325
- if shared_output is not None:
326
- final_hidden_states = final_hidden_states + shared_output
327
-
328
- if self.tp_size > 1:
329
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
330
- final_hidden_states
331
- )
317
return final_hidden_states.view(num_tokens, hidden_size)
318
319
vllm/model_executor/models/bailing_moe_linear.py
+2
-17
@@ -358,7 +358,6 @@ class BailingMoeV25(nn.Module):
358
top_k=self.top_k,
359
hidden_size=self.hidden_size,
360
intermediate_size=config.moe_intermediate_size,
361
- reduce_results=False,
361
renormalize=self.norm_expert_prob,
362
quant_config=quant_config,
363
prefix=f"{prefix}.experts",
@@ -368,6 +367,8 @@ class BailingMoeV25(nn.Module):
367
topk_group=self.topk_group,
368
use_grouped_topk=self.use_grouped_topk,
369
router_logits_dtype=self.router_dtype,
370
+ routed_scaling_factor=self.routed_scaling_factor,
371
+ apply_routed_scale_to_output=True,
372
)
373
374
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
@@ -383,22 +384,6 @@ class BailingMoeV25(nn.Module):
384
hidden_states=hidden_states, router_logits=router_logits
385
)
386
386
- # Handle tuple return from SharedFusedMoE
387
- if self.shared_experts is not None:
388
- shared_output, final_hidden_states = final_hidden_states
389
- else:
390
- shared_output = None
391
-
392
- final_hidden_states *= self.routed_scaling_factor
393
-
394
- if shared_output is not None:
395
- final_hidden_states = final_hidden_states + shared_output
396
-
397
- if self.tp_size > 1:
398
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
399
- final_hidden_states
400
- )
401
-
387
return final_hidden_states.view(num_tokens, hidden_size)
388
389
vllm/model_executor/models/dbrx.py
-1
@@ -85,7 +85,6 @@ class DbrxExperts(FusedMoE):
85
hidden_size=config.d_model,
86
intermediate_size=config.ffn_config.ffn_hidden_size,
87
params_dtype=params_dtype,
88
- reduce_results=True,
88
renormalize=True,
89
quant_config=quant_config,
90
tp_size=get_tensor_model_parallel_world_size(),
vllm/model_executor/models/deepseek_v2.py
+4
-30
@@ -318,7 +318,6 @@ class DeepseekV2MoE(nn.Module):
318
top_k=config.num_experts_per_tok,
319
hidden_size=config.hidden_size,
320
intermediate_size=config.moe_intermediate_size,
321
- reduce_results=False,
321
renormalize=config.norm_topk_prob,
322
quant_config=quant_config,
323
use_grouped_topk=True,
@@ -326,11 +325,9 @@ class DeepseekV2MoE(nn.Module):
325
topk_group=getattr(config, "topk_group", 1),
326
prefix=f"{prefix}.experts",
327
scoring_func=getattr(config, "scoring_func", "softmax"),
329
- # we do scaling outside, set factor to 1.0 to avoid double mul
328
# aiter applies routed_scaling_factor internally
331
- routed_scaling_factor=1.0
332
- if not self.is_rocm_aiter_moe_enabled
333
- else self.routed_scaling_factor,
329
+ routed_scaling_factor=self.routed_scaling_factor,
330
+ apply_routed_scale_to_output=not self.is_rocm_aiter_moe_enabled,
331
e_score_correction_bias=self.gate.e_score_correction_bias,
332
enable_eplb=self.enable_eplb,
333
num_redundant_experts=self.n_redundant_experts,
@@ -363,43 +360,20 @@ class DeepseekV2MoE(nn.Module):
360
hidden_states = sequence_parallel_chunk(hidden_states)
361
362
if self.experts.is_internal_router:
366
- # In this case, the gate/router runs inside the FusedMoE class
367
- fused_moe_out = self.experts(
363
+ final_hidden_states = self.experts(
364
hidden_states=hidden_states, router_logits=hidden_states
365
)
366
else:
371
- # router_logits: (num_tokens, n_experts)
367
router_logits, _ = self.gate(hidden_states)
373
- fused_moe_out = self.experts(
368
+ final_hidden_states = self.experts(
369
hidden_states=hidden_states, router_logits=router_logits
370
)
371
377
- shared_output, final_hidden_states = fused_moe_out
378
- if self.shared_experts is None:
379
- assert shared_output is None
380
-
381
- # Fix FP16 overflow
382
- # See DeepseekV2DecoderLayer for more details.
383
- if hidden_states.dtype != torch.float16:
384
- if not self.is_rocm_aiter_moe_enabled:
385
- final_hidden_states *= self.routed_scaling_factor
386
- elif self.shared_experts is not None:
387
- assert shared_output is not None
388
- shared_output *= 1.0 / self.routed_scaling_factor
389
-
390
- if self.shared_experts is not None:
391
- assert shared_output is not None
392
- final_hidden_states += shared_output
393
-
372
if self.is_sequence_parallel:
373
final_hidden_states = tensor_model_parallel_all_gather(
374
final_hidden_states, 0
375
)
376
final_hidden_states = final_hidden_states[:num_tokens]
399
- elif self.tp_size > 1:
400
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
401
- final_hidden_states
402
- )
377
378
return final_hidden_states.view(num_tokens, hidden_dim)
379
vllm/model_executor/models/dots1.py
+3
-13
@@ -37,7 +37,6 @@ from vllm.config import CacheConfig, ModelConfig, VllmConfig
37
from vllm.distributed import (
38
get_pp_group,
39
get_tensor_model_parallel_world_size,
40
- tensor_model_parallel_all_reduce,
40
)
41
from vllm.model_executor.layers.activation import SiluAndMul
42
from vllm.model_executor.layers.attention import Attention
@@ -120,7 +119,6 @@ class Dots1MoE(nn.Module):
119
prefix: str = "",
120
):
121
super().__init__()
123
- self.tp_size = get_tensor_model_parallel_world_size()
122
self.routed_scaling_factor = config.routed_scaling_factor
123
self.n_shared_experts = config.n_shared_experts
124
@@ -163,7 +161,6 @@ class Dots1MoE(nn.Module):
161
top_k=config.num_experts_per_tok,
162
hidden_size=config.hidden_size,
163
intermediate_size=config.moe_intermediate_size,
166
- reduce_results=False,
164
renormalize=config.norm_topk_prob,
165
quant_config=quant_config,
166
use_grouped_topk=True,
@@ -171,9 +168,9 @@ class Dots1MoE(nn.Module):
168
topk_group=config.topk_group,
169
prefix=f"{prefix}.experts",
170
scoring_func=config.scoring_func,
174
- # we do scaling outside, set factor to 1.0 to avoid double mul
175
- routed_scaling_factor=1.0,
171
e_score_correction_bias=self.gate.e_score_correction_bias,
172
+ routed_scaling_factor=self.routed_scaling_factor,
173
+ apply_routed_scale_to_output=True,
174
)
175
176
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
@@ -182,16 +179,9 @@ class Dots1MoE(nn.Module):
179
180
router_logits, _ = self.gate(hidden_states)
181
185
- shared_out, routed_out = self.experts(
182
+ final_hidden_states = self.experts(
183
hidden_states=hidden_states, router_logits=router_logits
184
)
188
- if self.shared_experts is not None:
189
- final_hidden_states = (routed_out + shared_out) * self.routed_scaling_factor
190
- else:
191
- final_hidden_states = routed_out * self.routed_scaling_factor
192
-
193
- if self.tp_size > 1:
194
- final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
185
return final_hidden_states.view(num_tokens, hidden_dim)
186
187
vllm/model_executor/models/ernie45_moe.py
-11
@@ -194,7 +194,6 @@ class Ernie4_5_MoeMoE(nn.Module):
194
top_k=config.moe_k,
195
hidden_size=config.hidden_size,
196
intermediate_size=config.moe_intermediate_size,
197
- reduce_results=False,
197
renormalize=True,
198
quant_config=quant_config,
199
prefix=f"{prefix}.experts",
@@ -215,16 +214,6 @@ class Ernie4_5_MoeMoE(nn.Module):
214
hidden_states=hidden_states, router_logits=router_logits
215
)
216
218
- if self.has_shared_experts:
219
- final_hidden_states = final_hidden_states[0] + final_hidden_states[1]
220
- else:
221
- final_hidden_states = final_hidden_states[1]
222
-
223
- if self.tp_size > 1:
224
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
225
- final_hidden_states
226
- )
227
-
217
return final_hidden_states.view(orig_shape)
218
219
vllm/model_executor/models/ernie45_vl_moe.py
+5
-30
@@ -263,7 +263,6 @@ class Ernie4_5_VLMoeMoE(nn.Module):
263
top_k=config.moe_k,
264
hidden_size=config.hidden_size,
265
intermediate_size=config.moe_intermediate_size[0],
266
- reduce_results=False,
266
renormalize=True,
267
quant_config=quant_config,
268
e_score_correction_bias=self.e_score_correction_bias[0],
@@ -301,7 +300,6 @@ class Ernie4_5_VLMoeMoE(nn.Module):
300
top_k=config.moe_k,
301
hidden_size=config.hidden_size,
302
intermediate_size=config.moe_intermediate_size[1],
304
- reduce_results=False,
303
renormalize=True,
304
quant_config=quant_config,
305
e_score_correction_bias=self.e_score_correction_bias[1],
@@ -342,9 +340,6 @@ class Ernie4_5_VLMoeMoE(nn.Module):
340
visual_token_mask = visual_token_mask.repeat(1, self.hidden_size).bool()
341
text_token_mask = ~visual_token_mask
342
final_experts_hidden_states = torch.zeros_like(hidden_states)
345
- final_shared_output = (
346
- torch.zeros_like(hidden_states) if self.has_shared_experts else None
347
- )
343
344
text_hidden_states = hidden_states[text_token_mask].reshape(
345
-1, self.hidden_size
@@ -356,26 +351,20 @@ class Ernie4_5_VLMoeMoE(nn.Module):
351
text_router_logits, _ = self.text_experts_gate(
352
text_hidden_states.to(dtype=torch.float32)
353
)
359
- text_shared_output, text_experts_output = self.text_experts(
354
+ text_output = self.text_experts(
355
hidden_states=text_hidden_states, router_logits=text_router_logits
356
)
362
- final_experts_hidden_states[text_token_mask] = text_experts_output.flatten()
363
- if self.has_shared_experts:
364
- final_shared_output[text_token_mask] = text_shared_output.flatten()
357
+ final_experts_hidden_states[text_token_mask] = text_output.flatten()
358
359
vision_router_logits, _ = self.vision_experts_gate(
360
vision_hidden_states.to(dtype=torch.float32)
361
)
369
- vision_shared_output, vision_experts_output = self.vision_experts(
362
+ vision_output = self.vision_experts(
363
hidden_states=vision_hidden_states, router_logits=vision_router_logits
364
)
372
- final_experts_hidden_states[visual_token_mask] = (
373
- vision_experts_output.flatten()
374
- )
375
- if self.has_shared_experts:
376
- final_shared_output[visual_token_mask] = vision_shared_output.flatten()
365
+ final_experts_hidden_states[visual_token_mask] = vision_output.flatten()
366
378
- final_hidden_states = (final_shared_output, final_experts_hidden_states)
367
+ final_hidden_states = final_experts_hidden_states
368
else:
369
# only text modal input
370
text_router_logits, _ = self.text_experts_gate(
@@ -386,20 +375,6 @@ class Ernie4_5_VLMoeMoE(nn.Module):
375
hidden_states=hidden_states, router_logits=text_router_logits
376
)
377
389
- if self.has_shared_experts:
390
- # for shared_experts model
391
- final_hidden_states = final_hidden_states[0] + final_hidden_states[1]
392
- else:
393
- # for not shared_experts model
394
- final_hidden_states = final_hidden_states[1]
395
-
396
- if self.tp_size > 1:
397
- final_hidden_states = (
398
- self.text_experts.maybe_all_reduce_tensor_model_parallel(
399
- final_hidden_states
400
- )
401
- )
402
-
378
return final_hidden_states.view(orig_shape)
379
380
vllm/model_executor/models/exaone_moe.py
+18
-28
@@ -31,6 +31,7 @@ from vllm.distributed import (
31
get_tensor_model_parallel_world_size,
32
)
33
from vllm.model_executor.layers.fused_moe import FusedMoE
34
+from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE
35
from vllm.model_executor.layers.layernorm import RMSNorm
36
from vllm.model_executor.layers.linear import ReplicatedLinear
37
from vllm.model_executor.layers.logits_processor import LogitsProcessor
@@ -116,12 +117,26 @@ class ExaoneMoe(nn.Module):
117
self.physical_expert_start + self.n_local_physical_experts
118
)
119
119
- self.experts = FusedMoE(
120
+ if getattr(config, "num_shared_experts", 0) > 0:
121
+ intermediate_size = config.moe_intermediate_size * config.num_shared_experts
122
+ self.shared_experts = ExaoneMoeGatedMLP(
123
+ hidden_size=config.hidden_size,
124
+ intermediate_size=intermediate_size,
125
+ hidden_act=config.hidden_act,
126
+ quant_config=quant_config,
127
+ reduce_results=False,
128
+ prefix=f"{prefix}.shared_experts",
129
+ )
130
+ else:
131
+ self.shared_experts = None
132
+
133
+ self.experts = SharedFusedMoE(
134
+ shared_experts=self.shared_experts,
135
+ gate=self.gate,
136
num_experts=self.n_routed_experts,
137
top_k=config.num_experts_per_tok,
138
hidden_size=config.hidden_size,
139
intermediate_size=config.moe_intermediate_size,
124
- reduce_results=False,
140
renormalize=config.norm_topk_prob,
141
quant_config=quant_config,
142
use_grouped_topk=True,
@@ -135,41 +150,16 @@ class ExaoneMoe(nn.Module):
150
num_redundant_experts=self.n_redundant_experts,
151
)
152
138
- if getattr(config, "num_shared_experts", 0) > 0:
139
- intermediate_size = config.moe_intermediate_size * config.num_shared_experts
140
- self.shared_experts = ExaoneMoeGatedMLP(
141
- hidden_size=config.hidden_size,
142
- intermediate_size=intermediate_size,
143
- hidden_act=config.hidden_act,
144
- quant_config=quant_config,
145
- reduce_results=self.experts.must_reduce_shared_expert_outputs(),
146
- prefix=f"{prefix}.shared_experts",
147
- )
148
- else:
149
- self.shared_experts = None
150
-
153
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
154
# NOTE: hidden_states can have either 1D or 2D shape.
155
orig_shape = hidden_states.shape
156
hidden_dim = hidden_states.shape[-1]
157
hidden_states = hidden_states.view(-1, hidden_dim)
158
157
- # router_logits: (num_tokens, n_experts)
158
- router_logits, _ = self.gate(hidden_states)
159
-
159
final_hidden_states = self.experts(
161
- hidden_states=hidden_states, router_logits=router_logits
160
+ hidden_states=hidden_states, router_logits=hidden_states
161
)
162
164
- if self.shared_experts is not None:
165
- shared_output = self.shared_experts(hidden_states)
166
- final_hidden_states = final_hidden_states + shared_output
167
-
168
- if self.tp_size > 1:
169
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel( # noqa E501
170
- final_hidden_states
171
- )
172
-
163
return final_hidden_states.view(orig_shape)
164
165
vllm/model_executor/models/flex_olmo.py
-1
@@ -76,7 +76,6 @@ class FlexOlmoMoE(nn.Module):
76
top_k=hf_config.num_experts_per_tok,
77
hidden_size=hf_config.hidden_size,
78
intermediate_size=hf_config.intermediate_size,
79
- reduce_results=True,
79
renormalize=False,
80
quant_config=None,
81
tp_size=tp_size,
vllm/model_executor/models/gemma4.py
-1
@@ -349,7 +349,6 @@ class Gemma4MoE(nn.Module):
349
"moe_intermediate_size",
350
getattr(config, "expert_intermediate_size", None),
351
),
352
- reduce_results=True,
352
renormalize=True,
353
quant_config=quant_config,
354
prefix=f"{prefix}.experts",
vllm/model_executor/models/glm4_moe.py
+3
-18
@@ -184,7 +184,6 @@ class Glm4MoE(nn.Module):
184
top_k=config.num_experts_per_tok,
185
hidden_size=config.hidden_size,
186
intermediate_size=config.moe_intermediate_size,
187
- reduce_results=False,
187
renormalize=config.norm_topk_prob,
188
quant_config=quant_config,
189
use_grouped_topk=True,
@@ -192,8 +191,8 @@ class Glm4MoE(nn.Module):
191
topk_group=config.topk_group,
192
prefix=f"{prefix}.experts",
193
scoring_func="sigmoid",
195
- # we do scaling outside, set factor to 1.0 to avoid double mul
196
- routed_scaling_factor=1.0,
194
+ routed_scaling_factor=self.routed_scaling_factor,
195
+ apply_routed_scale_to_output=True,
196
e_score_correction_bias=self.gate.e_score_correction_bias,
197
enable_eplb=self.enable_eplb,
198
num_redundant_experts=self.n_redundant_experts,
@@ -207,23 +206,9 @@ class Glm4MoE(nn.Module):
206
# router_logits: (num_tokens, n_experts)
207
router_logits = self.gate(hidden_states.to(dtype=torch.float32))
208
210
- fused_moe_out = self.experts(
209
+ final_hidden_states = self.experts(
210
hidden_states=hidden_states, router_logits=router_logits
211
)
213
-
214
- if self.shared_experts is not None:
215
- shared_output, final_hidden_states = fused_moe_out
216
- assert shared_output is not None
217
- final_hidden_states = (
218
- final_hidden_states * self.routed_scaling_factor + shared_output
219
- )
220
- else:
221
- final_hidden_states = fused_moe_out * self.routed_scaling_factor
222
-
223
- if self.tp_size > 1:
224
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
225
- final_hidden_states
226
- )
212
return final_hidden_states.view(num_tokens, hidden_dim)
213
214
vllm/model_executor/models/gpt_oss.py
-1
@@ -189,7 +189,6 @@ class MLPBlock(torch.nn.Module):
189
top_k=config.num_experts_per_tok,
190
hidden_size=config.hidden_size,
191
intermediate_size=config.intermediate_size,
192
- reduce_results=True,
192
renormalize=True,
193
quant_config=quant_config,
194
prefix=f"{prefix}.experts",
vllm/model_executor/models/granitemoe.py
-1
@@ -104,7 +104,6 @@ class GraniteMoeMoE(nn.Module):
104
hidden_size=hidden_size,
105
intermediate_size=intermediate_size,
106
params_dtype=params_dtype,
107
- reduce_results=True,
107
renormalize=True,
108
quant_config=quant_config,
109
tp_size=tp_size,
vllm/model_executor/models/grok1.py
-1
@@ -209,7 +209,6 @@ class Grok1MoE(nn.Module):
209
hidden_size=hidden_size,
210
intermediate_size=intermediate_size,
211
params_dtype=params_dtype,
212
- reduce_results=True,
212
renormalize=renormalize,
213
quant_config=quant_config,
214
tp_size=tp_size,
vllm/model_executor/models/hunyuan_v1.py
-7
@@ -39,7 +39,6 @@ from vllm.distributed import (
39
get_ep_group,
40
get_pp_group,
41
get_tensor_model_parallel_world_size,
42
- tensor_model_parallel_all_reduce,
42
)
43
from vllm.model_executor.layers.activation import SiluAndMul
44
from vllm.model_executor.layers.attention import Attention
@@ -445,7 +444,6 @@ class HunYuanSparseMoeBlock(nn.Module):
444
top_k=top_k,
445
hidden_size=config.hidden_size,
446
intermediate_size=intermediate_size,
448
- reduce_results=False,
447
renormalize=top_k > 1,
448
quant_config=quant_config,
449
prefix=f"{prefix}.experts",
@@ -464,11 +462,6 @@ class HunYuanSparseMoeBlock(nn.Module):
462
final_hidden_states = self.experts(
463
hidden_states=hidden_states, router_logits=router_logits
464
)
467
- if self.shared_mlp is not None:
468
- final_hidden_states = final_hidden_states[0] + final_hidden_states[1]
469
-
470
- if self.tp_size > 1:
471
- final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
465
466
return final_hidden_states.view(orig_shape)
467
vllm/model_executor/models/interns1_pro.py
-1
@@ -176,7 +176,6 @@ class InternS1ProMoeSparseMoeBlock(nn.Module):
176
top_k=config.num_experts_per_tok,
177
hidden_size=config.hidden_size,
178
intermediate_size=config.moe_intermediate_size,
179
- reduce_results=True,
179
renormalize=config.norm_topk_prob,
180
quant_config=quant_config,
181
prefix=f"{prefix}.experts",
vllm/model_executor/models/jamba.py
-1
@@ -90,7 +90,6 @@ class JambaMoE(nn.Module):
90
self.intermediate_size,
91
tp_size=tp_size,
92
params_dtype=params_dtype,
93
- reduce_results=True,
93
renormalize=False,
94
use_grouped_topk=False,
95
quant_config=quant_config,
vllm/model_executor/models/kimi_linear.py
+20
-26
@@ -11,11 +11,10 @@ from vllm.config import CacheConfig, ModelConfig, ParallelConfig, VllmConfig
11
from vllm.distributed import (
12
get_pp_group,
13
get_tensor_model_parallel_world_size,
14
- tensor_model_parallel_all_reduce,
14
)
15
from vllm.logger import init_logger
16
from vllm.model_executor.layers.activation import SiluAndMul
18
-from vllm.model_executor.layers.fused_moe import FusedMoE
17
+from vllm.model_executor.layers.fused_moe import SharedFusedMoE
18
from vllm.model_executor.layers.kda import KimiDeltaAttention
19
from vllm.model_executor.layers.layernorm import RMSNorm
20
from vllm.model_executor.layers.linear import (
@@ -132,12 +131,25 @@ class KimiMoE(nn.Module):
131
132
self.gate.e_score_correction_bias = nn.Parameter(torch.empty(num_experts))
133
135
- self.experts = FusedMoE(
134
+ if self.num_shared_experts is not None:
135
+ intermediate_size = moe_intermediate_size * self.num_shared_experts
136
+ self.shared_experts = KimiMLP(
137
+ hidden_size=config.hidden_size,
138
+ intermediate_size=intermediate_size,
139
+ hidden_act=config.hidden_act,
140
+ quant_config=quant_config,
141
+ reduce_results=False,
142
+ prefix=f"{prefix}.shared_experts",
143
+ )
144
+ else:
145
+ self.shared_experts = None
146
+
147
+ self.experts = SharedFusedMoE(
148
+ shared_experts=self.shared_experts,
149
num_experts=num_experts,
150
top_k=config.num_experts_per_token,
151
hidden_size=hidden_size,
152
intermediate_size=moe_intermediate_size,
140
- reduce_results=False,
153
renormalize=moe_renormalize,
154
quant_config=quant_config,
155
use_grouped_topk=config.use_grouped_topk,
@@ -146,34 +158,16 @@ class KimiMoE(nn.Module):
158
prefix=f"{prefix}.experts",
159
scoring_func=config.moe_router_activation_func,
160
e_score_correction_bias=self.gate.e_score_correction_bias,
161
+ routed_scaling_factor=self.routed_scaling_factor,
162
)
163
151
- if self.num_shared_experts is not None:
152
- intermediate_size = moe_intermediate_size * self.num_shared_experts
153
- self.shared_experts = KimiMLP(
154
- hidden_size=config.hidden_size,
155
- intermediate_size=intermediate_size,
156
- hidden_act=config.hidden_act,
157
- quant_config=quant_config,
158
- reduce_results=False,
159
- prefix=f"{prefix}.shared_experts",
160
- )
161
-
164
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
165
num_tokens, hidden_size = hidden_states.shape
166
hidden_states = hidden_states.view(-1, hidden_size)
165
- if self.num_shared_experts is not None:
166
- shared_output = self.shared_experts(hidden_states)
167
router_logits, _ = self.gate(hidden_states)
168
- final_hidden_states = (
169
- self.experts(hidden_states=hidden_states, router_logits=router_logits)
170
- * self.routed_scaling_factor
168
+ final_hidden_states = self.experts(
169
+ hidden_states=hidden_states, router_logits=router_logits
170
)
172
- if shared_output is not None:
173
- final_hidden_states = final_hidden_states + shared_output
174
-
175
- if self.tp_size > 1:
176
- final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
171
return final_hidden_states.view(num_tokens, hidden_size)
172
173
@@ -482,7 +476,7 @@ class KimiLinearModel(nn.Module):
476
if self.config.is_moe:
477
# Params for weights, fp8 weight scales, fp8 activation scales
478
# (param_name, weight_name, expert_id, shard_id)
485
- expert_params_mapping = FusedMoE.make_expert_params_mapping(
479
+ expert_params_mapping = SharedFusedMoE.make_expert_params_mapping(
480
self,
481
ckpt_gate_proj_name="w1",
482
ckpt_down_proj_name="w2",
vllm/model_executor/models/lfm2_moe.py
+3
-9
@@ -150,7 +150,6 @@ class Lfm2MoeSparseMoeBlock(nn.Module):
150
top_k=config.num_experts_per_tok,
151
hidden_size=config.hidden_size,
152
intermediate_size=config.moe_intermediate_size,
153
- reduce_results=False,
153
renormalize=config.norm_topk_prob,
154
quant_config=quant_config,
155
use_grouped_topk=True, # needed for softmax score func
@@ -161,6 +160,7 @@ class Lfm2MoeSparseMoeBlock(nn.Module):
160
num_redundant_experts=self.n_redundant_experts,
161
scoring_func="sigmoid",
162
e_score_correction_bias=self.gate.e_score_correction_bias,
163
+ routed_scaling_factor=self.routed_scaling_factor,
164
)
165
166
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
@@ -170,16 +170,10 @@ class Lfm2MoeSparseMoeBlock(nn.Module):
170
171
# router_logits: (num_tokens, n_experts)
172
router_logits, _ = self.gate(hidden_states)
173
- final_hidden_states = (
174
- self.experts(hidden_states=hidden_states, router_logits=router_logits)
175
- * self.routed_scaling_factor
173
+ final_hidden_states = self.experts(
174
+ hidden_states=hidden_states, router_logits=router_logits
175
)
176
178
- if self.tp_size > 1:
179
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel( # noqa E501
180
- final_hidden_states
181
- )
182
-
177
return final_hidden_states.view(orig_shape)
178
179
vllm/model_executor/models/llama4.py
+1
-7
@@ -135,7 +135,6 @@ class Llama4MoE(nn.Module):
135
custom_routing_function=Llama4MoE.custom_routing_function,
136
intermediate_size=intermediate_size_moe,
137
apply_router_weight_on_input=True,
138
- reduce_results=False,
138
renormalize=False,
139
quant_config=quant_config,
140
prefix=f"{prefix}.experts",
@@ -151,19 +150,14 @@ class Llama4MoE(nn.Module):
150
151
router_logits, _ = self.router(hidden_states)
152
154
- shared_out, routed_out = self.experts(
153
+ experts_out = self.experts(
154
hidden_states=hidden_states,
155
router_logits=router_logits,
156
)
158
- experts_out = routed_out + shared_out
157
158
if self.is_sequence_parallel:
159
experts_out = tensor_model_parallel_all_gather(experts_out, 0)
160
experts_out = experts_out[:num_tokens]
163
- elif self.tp_size > 1:
164
- experts_out = self.experts.maybe_all_reduce_tensor_model_parallel(
165
- experts_out
166
- )
161
162
return experts_out
163
vllm/model_executor/models/longcat_flash.py
-1
@@ -300,7 +300,6 @@ class LongcatMoe(nn.Module):
300
top_k=top_k,
301
hidden_size=hidden_size,
302
intermediate_size=intermediate_size,
303
- reduce_results=True,
303
params_dtype=params_dtype,
304
renormalize=False,
305
quant_config=quant_config,
vllm/model_executor/models/mimo_v2_flash.py
-1
@@ -162,7 +162,6 @@ class MiMoV2MoE(nn.Module):
162
top_k=config.num_experts_per_tok,
163
hidden_size=config.hidden_size,
164
intermediate_size=config.moe_intermediate_size,
165
- reduce_results=True,
165
renormalize=config.norm_topk_prob,
166
quant_config=quant_config,
167
prefix=f"{prefix}.experts",
vllm/model_executor/models/minimax_m2.py
-5
@@ -36,7 +36,6 @@ from vllm.config import CacheConfig, ModelConfig, VllmConfig
36
from vllm.distributed import (
37
get_pp_group,
38
get_tensor_model_parallel_world_size,
39
- tensor_model_parallel_all_reduce,
39
)
40
from vllm.model_executor.layers.attention import Attention
41
from vllm.model_executor.layers.fused_moe import FusedMoE
@@ -104,7 +103,6 @@ class MiniMaxM2MoE(nn.Module):
103
e_score_correction_bias=self.e_score_correction_bias,
104
hidden_size=config.hidden_size,
105
intermediate_size=config.intermediate_size,
107
- reduce_results=False,
106
renormalize=True,
107
quant_config=quant_config,
108
prefix=f"{prefix}.experts",
@@ -134,9 +132,6 @@ class MiniMaxM2MoE(nn.Module):
132
final_hidden_states = self.experts(
133
hidden_states=hidden_states, router_logits=router_logits
134
)
137
- final_hidden_states = final_hidden_states
138
- if self.tp_size > 1:
139
- final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
135
136
return final_hidden_states.view(num_tokens, hidden_dim)
137
vllm/model_executor/models/minimax_text_01.py
-1
@@ -162,7 +162,6 @@ class MiniMaxText01MoE(nn.Module):
162
hidden_size=self.hidden_size,
163
intermediate_size=self.intermediate_size * self.tp_size,
164
params_dtype=self.params_dtype,
165
- reduce_results=True,
165
renormalize=True,
166
quant_config=self.quant_config,
167
tp_size=self.tp_size,
vllm/model_executor/models/mixtral.py
-1
@@ -132,7 +132,6 @@ class MixtralMoE(nn.Module):
132
hidden_size=hidden_size,
133
intermediate_size=intermediate_size,
134
params_dtype=params_dtype,
135
- reduce_results=True,
135
renormalize=True,
136
quant_config=quant_config,
137
tp_size=tp_size,
vllm/model_executor/models/nemotron_h.py
+4
-25
@@ -216,7 +216,6 @@ class NemotronHMoE(nn.Module):
216
top_k=config.num_experts_per_tok,
217
hidden_size=self.moe_hidden_size,
218
intermediate_size=config.moe_intermediate_size,
219
- reduce_results=False,
219
renormalize=config.norm_topk_prob,
220
quant_config=quant_config,
221
use_grouped_topk=True,
@@ -231,6 +230,9 @@ class NemotronHMoE(nn.Module):
230
num_redundant_experts=self.n_redundant_experts,
231
is_sequence_parallel=self.is_sequence_parallel,
232
routed_input_transform=self.fc1_latent_proj,
233
+ routed_output_transform=self.fc2_latent_proj,
234
+ routed_scaling_factor=self.routed_scaling_factor,
235
+ apply_routed_scale_to_output=True,
236
router_logits_dtype=self.gate.out_dtype,
237
)
238
@@ -244,38 +246,15 @@ class NemotronHMoE(nn.Module):
246
# router_logits: (num_tokens, n_experts)
247
router_logits, _ = self.gate(hidden_states)
248
247
- # SharedFusedMoE handles:
248
- # - shared experts (with original hidden_states)
249
- # - routed_input_transform (fc1_latent_proj) for latent MoE
250
- # - multistream parallelism between shared and routed experts
251
- shared_output, final_hidden_states = self.experts(
249
+ final_hidden_states = self.experts(
250
hidden_states=hidden_states, router_logits=router_logits
251
)
252
255
- # Fix FP16 overflow
256
- # See DeepseekV2DecoderLayer for more details.
257
- if hidden_states.dtype != torch.float16:
258
- final_hidden_states *= self.routed_scaling_factor
259
- elif self.shared_experts is not None:
260
- shared_output *= 1.0 / self.routed_scaling_factor
261
-
262
- # TODO: See SharedFusedMoE.apply_routed_input_transform
263
- # for bandwidth optimization
264
- if self.use_latent_moe:
265
- final_hidden_states, _ = self.fc2_latent_proj(final_hidden_states)
266
-
267
- if self.shared_experts is not None:
268
- final_hidden_states += shared_output
269
-
253
if self.is_sequence_parallel:
254
final_hidden_states = tensor_model_parallel_all_gather(
255
final_hidden_states, 0
256
)
257
final_hidden_states = final_hidden_states[:num_tokens]
275
- elif self.tp_size > 1:
276
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
277
- final_hidden_states
278
- )
258
259
return final_hidden_states.view(num_tokens, hidden_dim)
260
vllm/model_executor/models/olmoe.py
-1
@@ -98,7 +98,6 @@ class OlmoeMoE(nn.Module):
98
top_k=top_k,
99
hidden_size=hidden_size,
100
intermediate_size=intermediate_size,
101
- reduce_results=True,
101
renormalize=False,
102
quant_config=quant_config,
103
tp_size=tp_size,
vllm/model_executor/models/openpangu.py
+3
-22
@@ -206,7 +206,6 @@ class OpenPanguMoE(nn.Module):
206
top_k=config.num_experts_per_tok,
207
hidden_size=config.hidden_size,
208
intermediate_size=config.moe_intermediate_size,
209
- reduce_results=False,
209
renormalize=config.norm_topk_prob,
210
quant_config=quant_config,
211
use_grouped_topk=True,
@@ -214,8 +213,8 @@ class OpenPanguMoE(nn.Module):
213
topk_group=1,
214
prefix=f"{prefix}.experts",
215
scoring_func="sigmoid",
217
- # we do scaling outside, set factor to 1.0 to avoid double mul
218
- routed_scaling_factor=1.0,
216
+ routed_scaling_factor=self.routed_scaling_factor,
217
+ apply_routed_scale_to_output=True,
218
e_score_correction_bias=self.gate.e_score_correction_bias,
219
enable_eplb=self.enable_eplb,
220
num_redundant_experts=self.n_redundant_experts,
@@ -234,33 +233,15 @@ class OpenPanguMoE(nn.Module):
233
234
router_logits, _ = self.gate(hidden_states)
235
237
- fused_moe_out = self.experts(
236
+ final_hidden_states = self.experts(
237
hidden_states=hidden_states, router_logits=router_logits
238
)
239
241
- shared_output, final_hidden_states = fused_moe_out
242
- if self.shared_experts is None:
243
- assert shared_output is None
244
-
245
- if hidden_states.dtype != torch.float16:
246
- final_hidden_states *= self.routed_scaling_factor
247
- elif self.shared_experts is not None:
248
- assert shared_output is not None
249
- shared_output *= 1.0 / self.routed_scaling_factor
250
-
251
- if self.shared_experts is not None:
252
- assert shared_output is not None
253
- final_hidden_states += shared_output
254
-
240
if self.is_sequence_parallel:
241
final_hidden_states = tensor_model_parallel_all_gather(
242
final_hidden_states, 0
243
)
244
final_hidden_states = final_hidden_states[:num_tokens]
260
- elif self.tp_size > 1:
261
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
262
- final_hidden_states
263
- )
245
246
return final_hidden_states.view(num_tokens, hidden_dim)
247
vllm/model_executor/models/param2moe.py
+1
-15
@@ -359,7 +359,6 @@ class Param2MoEMoEBlock(nn.Module):
359
top_k=self.top_k,
360
hidden_size=self.hidden_size,
361
intermediate_size=config.moe_intermediate_size,
362
- reduce_results=False,
362
renormalize=self.norm_expert_prob,
363
quant_config=quant_config,
364
prefix=f"{prefix}.experts",
@@ -388,24 +387,11 @@ class Param2MoEMoEBlock(nn.Module):
387
self.gate.weight.float(),
388
).to(hidden_states.dtype)
389
391
- final_hidden = self.experts(
390
+ expert_output = self.experts(
391
hidden_states=hidden_states,
392
router_logits=router_logits,
393
)
394
396
- if self.shared_experts is not None:
397
- shared_output, expert_output = final_hidden
398
- else:
399
- shared_output, expert_output = None, final_hidden
400
-
401
- if shared_output is not None:
402
- expert_output = expert_output + shared_output
403
-
404
- if self.tp_size > 1:
405
- expert_output = self.experts.maybe_all_reduce_tensor_model_parallel(
406
- expert_output
407
- )
408
-
395
return expert_output.view(num_tokens, hidden_dim)
396
397
vllm/model_executor/models/phimoe.py
-1
@@ -281,7 +281,6 @@ class PhiMoE(nn.Module):
281
hidden_size=hidden_size,
282
intermediate_size=intermediate_size,
283
params_dtype=params_dtype,
284
- reduce_results=True,
284
renormalize=False,
285
quant_config=quant_config,
286
tp_size=tp_size,
vllm/model_executor/models/qwen2_moe.py
-7
@@ -170,7 +170,6 @@ class Qwen2MoeSparseMoeBlock(nn.Module):
170
top_k=config.num_experts_per_tok,
171
hidden_size=config.hidden_size,
172
intermediate_size=config.moe_intermediate_size,
173
- reduce_results=False,
173
renormalize=config.norm_topk_prob,
174
quant_config=quant_config,
175
prefix=f"{prefix}.experts",
@@ -187,12 +186,6 @@ class Qwen2MoeSparseMoeBlock(nn.Module):
186
final_hidden_states = self.experts(
187
hidden_states=hidden_states, router_logits=router_logits
188
)
190
- if self.shared_expert is not None:
191
- final_hidden_states = final_hidden_states[0] + final_hidden_states[1]
192
- if self.tp_size > 1:
193
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel( # noqa E501
194
- final_hidden_states
195
- )
189
190
return final_hidden_states.view(orig_shape)
191
vllm/model_executor/models/qwen3_moe.py
+1
-9
@@ -212,7 +212,6 @@ class Qwen3MoeSparseMoeBlock(nn.Module):
212
top_k=config.num_experts_per_tok,
213
hidden_size=config.hidden_size,
214
intermediate_size=config.moe_intermediate_size,
215
- reduce_results=False,
215
renormalize=config.norm_topk_prob,
216
quant_config=quant_config,
217
prefix=f"{prefix}.experts",
@@ -234,22 +233,15 @@ class Qwen3MoeSparseMoeBlock(nn.Module):
233
234
# router_logits: (num_tokens, n_experts)
235
router_logits, _ = self.gate(hidden_states)
237
- shared_out, fused_out = self.experts(
236
+ final_hidden_states = self.experts(
237
hidden_states=hidden_states, router_logits=router_logits
238
)
240
- final_hidden_states = (
241
- shared_out + fused_out if shared_out is not None else fused_out
242
- )
239
240
if self.is_sequence_parallel:
241
final_hidden_states = tensor_model_parallel_all_gather(
242
final_hidden_states, 0
243
)
244
final_hidden_states = final_hidden_states[:num_tokens]
249
- elif self.tp_size > 1:
250
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel( # noqa E501
251
- final_hidden_states
252
- )
245
246
# return to 1d if input is 1d
247
return final_hidden_states.squeeze(0) if is_input_1d else final_hidden_states
vllm/model_executor/models/qwen3_next.py
-8
@@ -153,7 +153,6 @@ class Qwen3NextSparseMoeBlock(nn.Module):
153
top_k=config.num_experts_per_tok,
154
hidden_size=config.hidden_size,
155
intermediate_size=config.moe_intermediate_size,
156
- reduce_results=False,
156
renormalize=getattr(config, "norm_topk_prob", True),
157
quant_config=quant_config,
158
prefix=f"{prefix}.experts",
@@ -183,18 +182,11 @@ class Qwen3NextSparseMoeBlock(nn.Module):
182
hidden_states=hidden_states, router_logits=router_logits
183
)
184
186
- if self.shared_expert is not None:
187
- final_hidden_states = final_hidden_states[0] + final_hidden_states[1]
188
-
185
if self.is_sequence_parallel:
186
final_hidden_states = tensor_model_parallel_all_gather(
187
final_hidden_states, 0
188
)
189
final_hidden_states = final_hidden_states[:num_tokens]
194
- elif self.tp_size > 1:
195
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel( # noqa E501
196
- final_hidden_states
197
- )
190
191
return final_hidden_states.view(orig_shape)
192
vllm/model_executor/models/sarvam.py
+1
-15
@@ -341,7 +341,6 @@ class SarvamMLAMoE(nn.Module):
341
top_k=self.top_k,
342
hidden_size=self.hidden_size,
343
intermediate_size=config.moe_intermediate_size,
344
- reduce_results=False,
344
renormalize=self.norm_expert_prob,
345
quant_config=quant_config,
346
prefix=f"{prefix}.experts",
@@ -370,20 +369,7 @@ class SarvamMLAMoE(nn.Module):
369
router_logits=router_logits,
370
)
371
373
- if self.shared_experts is not None:
374
- shared_output, expert_output = final_hidden
375
- else:
376
- shared_output, expert_output = None, final_hidden
377
-
378
- if shared_output is not None:
379
- expert_output = expert_output + shared_output
380
-
381
- if self.tp_size > 1:
382
- expert_output = self.experts.maybe_all_reduce_tensor_model_parallel(
383
- expert_output
384
- )
385
-
386
- return expert_output.view(num_tokens, hidden_dim)
372
+ return final_hidden.view(num_tokens, hidden_dim)
373
374
375
class SarvamMLABlock(nn.Module):
vllm/model_executor/models/step3_text.py
-4
@@ -14,7 +14,6 @@ from vllm.config import CacheConfig, ModelConfig, VllmConfig
14
from vllm.distributed import (
15
get_pp_group,
16
get_tensor_model_parallel_world_size,
17
- tensor_model_parallel_all_reduce,
17
)
18
from vllm.logger import init_logger
19
from vllm.model_executor.layers.activation import SiluAndMul
@@ -71,7 +70,6 @@ class FusedMoEBlock(nn.Module):
70
top_k=config.moe_top_k,
71
hidden_size=config.hidden_size,
72
intermediate_size=config.moe_intermediate_size,
74
- reduce_results=False,
73
renormalize=config.norm_expert_weight,
74
quant_config=quant_config,
75
prefix=f"{prefix}.experts",
@@ -94,8 +92,6 @@ class FusedMoEBlock(nn.Module):
92
final_hidden_states = self.experts(
93
hidden_states=hidden_states, router_logits=router_logits
94
)
97
- if self.tp_size > 1:
98
- final_hidden_states = tensor_model_parallel_all_reduce(final_hidden_states)
95
96
return final_hidden_states.view(orig_shape)
97
vllm/model_executor/models/step3p5.py
+3
-18
@@ -379,7 +379,6 @@ class FusedMoEBlock(nn.Module):
379
top_k=config.moe_top_k,
380
hidden_size=config.hidden_size,
381
intermediate_size=config.moe_intermediate_size,
382
- reduce_results=False,
382
renormalize=config.norm_expert_weight,
383
quant_config=quant_config,
384
activation=activation,
@@ -397,30 +396,16 @@ class FusedMoEBlock(nn.Module):
396
hidden_states = hidden_states.view(-1, hidden_dim)
397
398
if self.experts.is_internal_router:
400
- # In this case, the gate/router runs inside the FusedMoE class
401
- fused_moe_out = self.experts(
399
+ final_hidden_states = self.experts(
400
hidden_states=hidden_states, router_logits=hidden_states
401
)
402
else:
405
- # router_logits: (num_tokens, n_experts)
403
+ # TODO(bnell): this gate could be moved into the FusedMoE?
404
router_logits, _ = self.gate(hidden_states)
407
- fused_moe_out = self.experts(
405
+ final_hidden_states = self.experts(
406
hidden_states=hidden_states, router_logits=router_logits
407
)
408
411
- shared_output, final_hidden_states = fused_moe_out
412
- if self.share_expert is None:
413
- assert shared_output is None
414
-
415
- if self.share_expert is not None:
416
- assert shared_output is not None
417
- final_hidden_states += shared_output
418
-
419
- if self.tp_size > 1:
420
- final_hidden_states = self.experts.maybe_all_reduce_tensor_model_parallel(
421
- final_hidden_states
422
- )
423
-
409
return final_hidden_states.view(num_tokens, hidden_dim)
410
411
vllm/model_executor/models/transformers/moe.py
+3
-27
@@ -204,8 +204,6 @@ class MoEMixin(MixtureOfExperts):
204
)
205
assert intermediate_size is not None
206
207
- # If there are shared experts, the results are
208
- # reduced after mlp.forward() not inside FusedMoE
207
num_shared_experts = getattr_iter(
208
text_config,
209
[
@@ -214,17 +212,6 @@ class MoEMixin(MixtureOfExperts):
212
],
213
0,
214
)
217
- reduce_results = num_shared_experts == 0
218
-
219
- def add_all_reduce(mlp: nn.Module):
220
- """Adds an all-reduce to the output of `mlp.forward()`."""
221
-
222
- class MLPWithAllReduce(mlp.__class__):
223
- def forward(self, *args, **kwargs):
224
- output = super().forward(*args, **kwargs)
225
- return self.experts.maybe_all_reduce_tensor_model_parallel(output)
226
-
227
- mlp.__class__ = MLPWithAllReduce
215
216
# Unused kwargs since we use custom_routing_function:
217
# - `scoring_func` and `e_score_correction_bias` only used for grouped
@@ -289,14 +276,11 @@ class MoEMixin(MixtureOfExperts):
276
if "bias" in experts_param_name:
277
has_bias = True
278
break
292
- # Double check there are no shared experts
293
- nonlocal reduce_results
294
- if reduce_results:
279
+ # If the config does not specify num_shared_experts, but
280
+ # the model has shared experts, we assume there is one.
281
+ if self.num_shared_experts == 0:
282
for mlp_param_name, _ in mlp.named_parameters():
283
if "shared_expert" in mlp_param_name:
297
- reduce_results = False
298
- # If the config does not specify num_shared_experts, but
299
- # the model has shared experts, we assume there is one.
284
self.num_shared_experts = 1
285
break
286
# Replace experts module with FusedMoE
@@ -305,7 +289,6 @@ class MoEMixin(MixtureOfExperts):
289
top_k=top_k,
290
hidden_size=hidden_size,
291
intermediate_size=intermediate_size,
308
- reduce_results=reduce_results,
292
renormalize=renormalize,
293
# Hard coded because topk happens in Transformers
294
use_grouped_topk=False,
@@ -326,13 +309,6 @@ class MoEMixin(MixtureOfExperts):
309
self.moe_layers.append(fused_experts)
310
self.expert_weights.append(fused_experts.get_expert_weights())
311
self.num_moe_layers += 1
329
- # If results are not all-reduced in FusedMoE, ensure they
330
- # are all-reduced at the end of mlp.forward() if tensor
331
- # parallel or expert parallel is enabled
332
- if not reduce_results and (
333
- fused_experts.tp_size > 1 or fused_experts.ep_size > 1
334
- ):
335
- add_all_reduce(mlp)
312
else:
313
_recursive_replace(child_module, prefix=qual_name)
314