Skip to content

Commit 9a1309e

Browse files
xrennvidiaclaude
andcommitted
Honor per-module quantization override in the fused GroupedMLP path
The TE op-fuser builds its quantizers from the ambient autocast recipe, so a --te-precision-config-file override that keeps the experts quantized but with a different recipe (e.g. MXFP8 MTP experts under a global NVFP4 recipe) was ignored by the fused kernels: forward ran under NVFP4 while the module and its GTP gather were MXFP8, and backward then failed in the NVFP4 amax lookup. Enter the module's own quantization autocast around the fused call, exactly as TEGroupedLinear.forward does on the unfused path. fc1 and fc2 must carry the same override since fusion spans both. A bf16 override still disables fusion. Requires the matching Transformer Engine change that quantizes GTP-gathered BF16 weights inside the fused grouped MLP. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 6e7e6ed commit 9a1309e

1 file changed

Lines changed: 31 additions & 5 deletions

File tree

‎megatron/core/transformer/moe/experts.py‎

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -339,10 +339,12 @@ def __init__(
339339
assert (
340340
self._is_fused_impl_supported()
341341
), "Fused GroupedMLP is not supported for this configuration."
342-
# The fused grouped-MLP kernels are FP8/NVFP4-only and take their recipe from the global
343-
# autocast state, so they would ignore a --te-precision-config-file override and quantize
344-
# anyway -- silently under plain TE, fatally under GTP, whose backward then hands the
345-
# kernel an unquantized weight. Fusion spans fc1 and fc2, so either one opting out ends it.
342+
# The fused grouped-MLP kernels are FP8/NVFP4-only. A --te-precision-config-file override
343+
# that turns the experts bf16 therefore disables fusion; a quantized override (e.g. MXFP8
344+
# experts under a global NVFP4 recipe) keeps fusion and is applied by entering the module's
345+
# own quantization autocast around the fused call (see _fused_quant_context), exactly as
346+
# TEGroupedLinear.forward does on the unfused path. Fusion spans fc1 and fc2, so either one
347+
# opting out ends it.
346348
self._with_fused_impl: bool = (
347349
self.config.use_transformer_engine_op_fuser
348350
and self.linear_fc1.will_execute_quantized(is_context_quantized=True)
@@ -789,6 +791,30 @@ def _ensure_main_grad_for_fused_impl(self) -> None:
789791
self._ensure_main_grad(self.linear_fc1)
790792
self._ensure_main_grad(self.linear_fc2)
791793

794+
def _fused_quant_context(self):
795+
"""Quantization autocast for the fused grouped-MLP call.
796+
797+
The TE op fuser builds its quantizers from the ambient autocast recipe, not from the
798+
per-module ``te_quant_params`` that ``TEGroupedLinear.forward`` would apply on the unfused
799+
path. Enter the same per-module context here so a --te-precision-config-file override
800+
(e.g. MXFP8 experts under a global NVFP4 recipe) is honored by the fused kernels and their
801+
backward. Fusion spans fc1 and fc2, so both must carry the same override.
802+
"""
803+
from megatron.core.extensions.transformer_engine import (
804+
_get_fp8_autocast_for_quant_params,
805+
)
806+
807+
qparams_fc1 = getattr(self.linear_fc1, "te_quant_params", None)
808+
qparams_fc2 = getattr(self.linear_fc2, "te_quant_params", None)
809+
if qparams_fc1 is None and qparams_fc2 is None:
810+
return nullcontext()
811+
if qparams_fc1 != qparams_fc2:
812+
raise RuntimeError(
813+
"Fused GroupedMLP requires linear_fc1 and linear_fc2 to share the same "
814+
f"quantization override; got {qparams_fc1} and {qparams_fc2}."
815+
)
816+
return _get_fp8_autocast_for_quant_params(qparams_fc1, self.training)
817+
792818
def _fused_forward(
793819
self,
794820
permuted_local_hidden_states: torch.Tensor,
@@ -878,7 +904,7 @@ def _fused_forward(
878904
fused_group_mlp_manager = off_interface(
879905
fine_grained_activation_offloading, permuted_local_hidden_states, offload_name
880906
)
881-
with fused_group_mlp_manager as permuted_local_hidden_states:
907+
with self._fused_quant_context(), fused_group_mlp_manager as permuted_local_hidden_states:
882908
# NCCL-EP zero-copy is active exactly when ``output_buffer`` is not None, and then the
883909
# fused-MLP input aliases the persistent symm buffer (also the fc2 output combine
884910
# reads), whose storage is non-resizable — so skip the force-release in that case.

0 commit comments

Comments
 (0)