[Bugfix] Restore moe_forward output shape invariant on TRTLLM MXFP4 path (#41646)

Signed-off-by: Stefano Castagnetta <scastagnetta@nvidia.com>
Co-authored-by: Yongye Zhu <zyy1102000@gmail.com>
This commit is contained in:
Stefano Castagnetta
2026-05-07 15:26:06 -07:00
committed by GitHub
co-authored by Yongye Zhu
parent c1819ca283
commit 54f548e9e5
@@ -93,6 +93,7 @@ def _moe_forward(
shared_experts_input: torch.Tensor | None,
input_ids: torch.Tensor | None,
layer_name: _layer_name_type,
hidden_dim_unpadded: int,
) -> torch.Tensor:
layer = get_layer_from_name(_resolve_layer_name(layer_name))
return layer.runner._forward_impl(
@@ -110,7 +111,14 @@ def _moe_forward_fake(
shared_experts_input: torch.Tensor | None,
input_ids: torch.Tensor | None,
layer_name: _layer_name_type,
hidden_dim_unpadded: int,
) -> torch.Tensor:
# `hidden_dim_unpadded > 0` only on the TRT-LLM MXFP4 path, where the
# real kernel writes narrower than `hidden_states.shape[-1]`. Plumbed
# as an op arg (not peeked from the layer registry) to keep the fake
# a pure shape function of its inputs and preserve subgraph dedup.
if hidden_dim_unpadded > 0:
return hidden_states.new_empty((*hidden_states.shape[:-1], hidden_dim_unpadded))
return torch.empty_like(hidden_states)
@@ -120,6 +128,7 @@ def _moe_forward_shared(
shared_experts_input: torch.Tensor | None,
input_ids: torch.Tensor | None,
layer_name: _layer_name_type,
hidden_dim_unpadded: int,
) -> tuple[torch.Tensor, torch.Tensor]:
layer = get_layer_from_name(_resolve_layer_name(layer_name))
return layer.runner._forward_impl(
@@ -137,13 +146,17 @@ def _moe_forward_shared_fake(
shared_experts_input: torch.Tensor | None,
input_ids: torch.Tensor | None,
layer_name: _layer_name_type,
hidden_dim_unpadded: int,
) -> tuple[torch.Tensor, torch.Tensor]:
# Output shapes:
# - fused_out: same as hidden_states (routed experts use transformed size)
# - shared_out: same as shared_experts_input if provided, else same as
# hidden_states
# (For latent MoE: shared experts use original hidden_size, not latent size)
fused_out = torch.empty_like(hidden_states)
# `fused_out`: see `_moe_forward_fake` for hidden_dim_unpadded semantics.
# `shared_out`: matches `shared_experts_input` if provided (latent MoE),
# else `hidden_states`.
if hidden_dim_unpadded > 0:
fused_out = hidden_states.new_empty(
(*hidden_states.shape[:-1], hidden_dim_unpadded)
)
else:
fused_out = torch.empty_like(hidden_states)
if shared_experts_input is not None:
shared_out = torch.empty_like(shared_experts_input)
else:
@@ -389,6 +402,29 @@ class MoERunner(MoERunnerInterface):
return "from_forward_context"
return self.layer_name
def _trtllm_mxfp4_unpadded_dim(self) -> int:
"""Return ``hidden_dim_unpadded`` when the active backend is TRT-LLM
MXFP4 (whose kernel writes narrower than the padded
``hidden_states.shape[-1]``), else 0. Other MXFP4 backends (notably
Cutlass MXFP4 MXFP8) write the full padded width, so
``moe_config.hidden_dim_unpadded`` alone is insufficient: it encodes
the model's logical hidden, not whether the kernel narrows. Computed
caller-side and passed as an op arg; doing the isinstance check
inside the fake would specialize per ``layer_name`` and break
subgraph dedup for identical-architecture models (e.g. Phi-MoE).
"""
from vllm.model_executor.layers.fused_moe.experts.trtllm_mxfp4_moe import (
TrtLlmMxfp4ExpertsBase,
)
moe_kernel = getattr(self._quant_method, "moe_kernel", None)
fused_experts = getattr(
getattr(moe_kernel, "impl", None), "fused_experts", None
)
if isinstance(fused_experts, TrtLlmMxfp4ExpertsBase):
return self.moe_config.hidden_dim_unpadded or self.moe_config.hidden_dim
return 0
def _maybe_pad_hidden_states(
self,
shared_experts_input: torch.Tensor | None,
@@ -585,6 +621,7 @@ class MoERunner(MoERunnerInterface):
shared_experts_input,
input_ids,
self._encode_layer_name(),
self._trtllm_mxfp4_unpadded_dim(),
)
#