Skip to content

Add MXFP8 fused-MLP overrides in torchtitan - #4257

Open
wolfcomos wants to merge 8 commits into
pytorch:mainfrom
wolfcomos:swiglu-mxfp8-upstream
Open

Add MXFP8 fused-MLP overrides in torchtitan#4257
wolfcomos wants to merge 8 commits into
pytorch:mainfrom
wolfcomos:swiglu-mxfp8-upstream

Conversation

@wolfcomos

@wolfcomos wolfcomos commented Aug 20, 2026

Copy link
Copy Markdown

Summary

Adds an opt-in override module, torchtitan/overrides/mxfp8_fused_mlp.py, that runs a FeedForward / routed-experts MLP entirely in MXFP8 with a fused SwiGLU boundary: one composite autograd function computes w13 GEMM -> fused SwiGLU + dual MXFP8 quantization -> w2 GEMM (dense and grouped variants), using torchao's fused gated-activation kernel (pytorch/ao#4743) for the activation boundary and torchao's MXFP8 grouped/scaled-mm internals for the GEMMs. Compared to the unfused MXFP8 path, the fused boundary eliminates the standalone activation-quantization casts (1 fwd + 1 bwd fused op instead of 6 + 6 standalone cast kernels per MLP) and never materializes the BF16 post-activation tensor.

The integration:

  • MXFP8FusedMLP and MXFP8FusedGroupedMLP subclass the stock FeedForward / GroupedExperts and keep stock parameters (w1/w2/w3); the two projection weights are packed into the kernel's fused layout at forward time. No parameter surgery, no state-dict hooks, no init/sharding remaps — checkpoints and fresh initialization are bitwise-identical to stock modules (asserted by a shipped test).
  • Two @override factories: mxfp8_fused_mlp (targets FeedForward.Config) and mxfp8_fused_grouped_mlp (targets RoutedExperts.Config, which owns both the token dispatcher and the inner experts — the factory swaps the dispatcher to the padded variant its kernels require, pad_multiple=128).
  • Fail-loud, no silent fallback: non-stock or already-converted configs, DTensor dense activations, biased projections, and missing kernels raise with actionable messages.
  • Zero changes to existing files' behavior: overrides/fused_swiglu.py, components/quantization/mx.py, and the quantization converters are untouched, and this module imports nothing from the BF16 fused-swiglu override. Enabling the override is the only opt-in; additional MXFP8 fusion paths (e.g. fully fused grouped MLPs) extend this module with their own composite and factory.

Usage

# dense (llama3 debugmodel example flavor)
--override.imports torchtitan.overrides.mxfp8_fused_mlp.mxfp8_fused_mlp

# routed experts (deepseek_v3 debugmodel example flavor; needs EP>=2 so the token
# dispatcher produces the padded expert-major layout the kernels consume)
--override.imports torchtitan.overrides.mxfp8_fused_mlp.mxfp8_fused_grouped_mlp

Example flavors: llama3_debugmodel_mxfp8_fused_mlp, deepseek_v3_debugmodel_mxfp8_fused_mlp (attention/lm_head stay BF16 — composable with MXFP8LinearConverter on those modules independently).

Numerics and tests

  • Shipped: tests/unit_tests/test_mxfp8_fused_mlp.py — 8 CPU-runnable tests (config-tree transform, apply_overrides, meta-device build, dispatcher pad_multiple, factory fail-loud, checkpoint keys == stock, and a seeded fresh-init state-dict bitwise comparison against the stock modules), validated with GPUs visible, with CUDA_VISIBLE_DEVICES="", and on stock PyPI torchao without a GPU (all 8 execute).
  • Because parameters and checkpoint keys are stock, checkpoints interchange freely with unfused modules in both directions.
  • GPU numerics are validated in NVIDIA-internal Blackwell CI and maintained there (upstream CI has no SM100): 25-test suite on GB200 covering bitwise forward parity between the fused kernel and standalone-cast quantization, SQNR tracking against the per-GEMM MXFP8 and BF16 references, torch.compile with unbacked routing-dependent token counts, dispatcher pad-row/tail inertness, and profiler op-count contracts.
  • Kernel-level numerics (bitwise forward, one-code-bounded backward at ~6e-7 of elements, root-caused to the kernel's correctly-rounded sigmoid + FMA contraction) are documented in [mxfp8 training] Add a fused gated-activation (SwiGLU) + MXFP8 quantization kernel ao#4743.

🤖 Generated with Claude Code

…ories)

overrides/mxfp8_fused_swiglu.py hosts two composite autograd functions that
run the whole SwiGLU MLP in MXFP8 (RCEIL e8m0 scales, block 32): dense
x -> w13 GEMM -> SwiGLU -> w2 GEMM and the grouped-experts equivalent, with
the activation boundary quantized either by the unified SwiGLU+MXFP8 CuTeDSL
kernel (fuse_activation=True) or by standalone BF16 + cast kernels (the two
modes are bitwise-identical in forward). Two @OverRide factories wire them in:
mxfp8_fused_swiglu builds MXFP8FusedSwiGLU (dense FeedForward) and
mxfp8_fused_grouped_experts builds MXFP8FusedGroupedExperts, swapping the
token dispatcher for the padded variant (pad_multiple=128) the kernels
require. Both inherit the fused-w13 parameter, stock-layout checkpoints, and
sharding remaps from the existing fused_swiglu module classes.

There is no silent fallback: unavailable kernels, DTensor operands, non-BF16
dtypes, or shapes violating the kernels' 128-alignment contract raise
actionable errors (routing-dependent token counts remain torch._check
deferred asserts under compile); the factories fail loud on non-stock configs
so they cannot silently compose with the MXFP8 quantization converters.

No changes to overrides/fused_swiglu.py or components/quantization. One
example debugmodel flavor per model family (llama3 dense, deepseek_v3 grouped
with expert_parallel_degree=2 for the padded EP dispatch). Tests: one
CPU-runnable wiring suite (config-tree transforms, padded-dispatcher swap,
factory fail-loud rejections, meta-device builds; the SM100 gate is patched
out since hardware is irrelevant to the transforms). GPU numerics
(fused-vs-unfused bitwise forward parity, reference tracking, compile with
unbacked token counts, padding inertness) are validated on GB200 in
NVIDIA-internal CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wolfcomos and others added 5 commits August 20, 2026 19:12
Rename the grouped override class MXFP8FusedGroupedExperts ->
MXFP8FusedGroupedMLP and the module mxfp8_fused_swiglu.py ->
mxfp8_fused_mlp.py (it hosts both the dense SwiGLU and grouped MLP
composites). forward keeps the plumbing -- DTensor localization, BF16
casts, expert-offset construction, output-dtype restoration -- and
delegates the complete numerical grouped MLP to the new protected
_run_grouped_mlp hook, the one seam a future fully fused
grouped GEMM + SwiGLU + dual MXFP8 quantization backend overrides.

No behavior change: the hook passes the composite exactly the operands
forward always built; the dense path, the w13 (E, F, 2, D) layout, the
pad_multiple=128 dispatcher contract, and the checkpoint/init/sharding
hooks are untouched. Public factory names are unchanged; only the
module path in override import strings moves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revert mxfp8_fused_mlp.py -> mxfp8_fused_swiglu.py and
MXFP8FusedGroupedMLP -> MXFP8FusedGroupedExperts (smallest diff against
the original PR; override import strings and configs are byte-identical
again), and describe _run_grouped_mlp accurately as the
numerical/autograd backend seam: future implementations may also
require backend-specific parameter layout, checkpoint hooks, dispatcher
padding, and factory validation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the class-docstring paragraph (the hook docstring already states
the seam) and the two multi-line test comments the test names cover.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Collapse the grouped path to factory -> module -> autograd Function:
MXFP8FusedGroupedExperts.forward now runs _validate_grouped_inputs and
_MXFP8SwiGLUGroupedMLP.apply directly, and the _run_grouped_mlp hook
and the standalone mxfp8_swiglu_grouped_mlp_w13 wrapper (no external
callers) are removed. A future fused backend branches on its mode
inside forward. Dense path unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MXFP8FusedGroupedExperts.forward is the only caller of
_validate_grouped_inputs and already guarantees plain local BF16
tensors in the module's own shapes, so the DTensor, device/ndim, dtype,
and shape-consistency checks are unreachable; what remains is the
environment gate, the config-dim 128-multiple gate, and the
routing-dependent token-count conditions. Error messages now name
MXFP8FusedGroupedExperts instead of the removed functional wrapper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wolfcomos added a commit to wolfcomos/torchtitan that referenced this pull request Aug 24, 2026
Rework the mxfp8_grouped_experts override to the self-contained pattern
of pytorch#4257: it now targets the STOCK RoutedExperts.Config (stock
GroupedExperts inner) and owns the whole routed-expert contract itself
instead of piggybacking on the MXFP8 grouped-experts converter.

- The factory installs the pad_multiple=256 dispatcher itself: a
  TorchAOTokenDispatcher.Config is retargeted in place, the stock
  all-to-all is swapped via swap_token_dispatcher, and anything else
  raises (the 256-row cuDNN FE contract is only validated for the
  TorchAO padded dispatcher).
- No silent fallback: every gate that used to decline-and-warn now
  raises an actionable ValueError at config-application time (missing
  torchao ops, non-SM100 hardware, converter-quantized or subclassed
  experts, dims off the 128-alignment contract). exact=True is dropped;
  the subclass raise happens inside the factory.
- deepseek_v3_{debugmodel,16b}_mxfp8_grouped_mlp are rebuilt standalone:
  dense MXFP8LinearConverter only, no grouped-experts converter; the
  override supplies the fused composite and the padded dispatcher. The
  _p256 baseline arms are untouched.
- Fix a latent DTensor crash in _make_w13_init at EP>1 fresh init: each
  half is now initialized in place through a strided 32-block sub-view
  of w13 (the save hook's view), keeping shard-distinct, globally
  consistent draws through the DTensor RNG tracker. Plain-tensor
  temporaries would draw identical values on every rank (torchtitan
  seeds all non-PP ranks the same), silently duplicating experts across
  EP/FSDP shards; every in-tree w1_EFD/w3_EFD initializer is a fixed-std
  trunc_normal_, so the blocked sub-view geometry is safe.
- Tests: decline tests become raise tests; new coverage for the
  subclass raise, unsupported dims, the hybridep dispatcher refusal,
  ops-unavailable, and 16B A/B arm knob/dispatcher parity.

18/18 unit tests green on 1xGB200; flake8 clean; 5-step EP=2 fresh-init
smoke converges with the override applied on all layers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wolfcomos
wolfcomos force-pushed the swiglu-mxfp8-upstream branch from 89fcb0f to 04d8f32 Compare August 24, 2026 08:37
MXFP8FusedGroupedMLP now subclasses stock GroupedExperts directly: the
FusedGroupedExperts inheritance is replaced by self-owned w13 registration,
stock-layout checkpoint hooks, and param-init/sharding remap helpers, so
future MXFP8 fusion paths extend this module without that dependency (none
are wired here). Everything renames under the "MXFP8 fused MLP" umbrella
(module, classes, factories, debugmodel flavors); the dense class keeps its
small FusedSwiGLU base. Bitwise-verified on both paths (5-step deterministic
NGPU=2 runs, losses and grad norms identical to the previous revision).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both override classes now keep the stock parameters (w1/w2/w3 dense,
w1_EFD/w2_EDF/w3_EFD grouped) and stack the gate/up weights into the
composite's w13 operand at forward time, byte-identical to the old
merge-hook mapping; the module no longer depends on fused_swiglu.
Ckpt-paired 2-GPU runs match the fused-param arm bitwise at print
precision (grouped exactly; dense exactly once grad clipping is inert),
and fresh-init state dicts match the stock modules bitwise.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@wolfcomos wolfcomos changed the title Add SwiGLU fused MXFP8 quant overrides in torchtitan Add MXFP8 fused-MLP overrides in torchtitan Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants