Skip to content

🚨 deepgemm_megamoe EP implementation for DeepGEMM Mega MoE - #49606

Open
IlyasMoutawwakil wants to merge 58 commits into
mainfrom
ep-passthrough-strategy
Open

IlyasMoutawwakil wants to merge 58 commits into
mainfrom
ep-passthrough-strategy

Conversation

@IlyasMoutawwakil

@IlyasMoutawwakil IlyasMoutawwakil commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

🤗 Transformers CI

Depends on #49429.

DeepGEMM Mega MoE fuses the token dispatch, the experts and the combine in one kernel. It needs global expert ids, unmasked routing weights and the EP process group. Until now it only got them through the megamoe_router and megamoe_experts styles. On the default ep_dispatch_experts plan, which DeepSeek V4 ships, loading with experts_implementation="deepgemm_megamoe" failed at the first forward (DeepGEMM Mega MoE requires a process_group).

This PR adds a "deepgemm_megamoe" EP implementation. "auto" runs it for experts loaded with experts_implementation="deepgemm_megamoe", since they can only run through it; another explicit implementation raises. It requires ep_size == tp_size, like the old styles.

🚨 Breaking: the megamoe_router and megamoe_experts parallel styles are removed. A plan that names them should use ep_dispatch_experts on the experts and drop the router rule; loading with experts_implementation="deepgemm_megamoe" selects the implementation.

How it runs

The kernel treats each rank's rows as that rank's own tokens. Here every rank holds the same batch.

  • Each rank hands the kernel an equal slice of the batch. The last slice is padded with skipped slots.
  • The slices' outputs are all-gathered back, so every token goes through the experts once.
  • The symmetric-memory buffers are kept per EP group and shared by every layer. Before, each experts module allocated its own.

Example with 4 tokens on 2 ranks:

GPU                    0                      1
batch           [t0, t1, t2, t3]       [t0, t1, t2, t3]
kernel input        [t0, t1]               [t2, t3]
kernel output       [y0, y1]               [y2, y3]
after all-gather         [y0, y1, y2, y3] on both ranks

Changes

File Change
distributed/expert_parallel.py MegaMoeEP, registered as "deepgemm_megamoe". It passes the EP group to the experts as process_group. AutoEP builds it for Mega MoE experts.
integrations/deepgemm.py The Mega MoE forward keeps its symmetric buffers per EP group (process_group.group_name) and shape, shared by every layer. destroy_megamoe_buffers() frees them, like DeepEP's destroy_deepep_buffers() (#49638).
integrations/tensor_parallel.py, integrations/finegrained_fp8.py Removed: the megamoe_router and megamoe_experts styles, and the FP8 plan rewrites to them.
Docs A "deepgemm_megamoe" row in the EP implementation table. The two styles leave the parallel styles page.

Tests

Test Result
tests/expert_parallel, 2 ranks on gloo. The new Mega MoE test checks that "auto" runs it and "all_to_all" is refused, the padded slices, global ids, the EP group and the gathered output; the DeepGEMM kernel tests check the buffers are cached per EP group. 8 passed
TP and EP model tests of Mixtral, Qwen3-MoE, GPT-OSS and DeepSeek-V3, on CPU 273 passed
The DeepGEMM Mega MoE tests pass the cache and check the buffer lands in it. 2 passed

🤖 Generated with Claude Code

@HuggingFaceDocBuilderDev

HuggingFaceDocBuilderDev commented Oct 14, 2026 •

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

Pages changed in this PR:

@IlyasMoutawwakil IlyasMoutawwakil changed the title passthrough expert-parallel strategy for DeepGEMM Mega MoE deepgemm_megamoe expert-parallel strategy for DeepGEMM Mega MoE Oct 14, 2026
@IlyasMoutawwakil
IlyasMoutawwakil force-pushed the ep-passthrough-strategy branch 2 times, most recently from 1bfd600 to 6ca9df4 Compare October 16, 2026 06:51
@IlyasMoutawwakil IlyasMoutawwakil changed the title deepgemm_megamoe expert-parallel strategy for DeepGEMM Mega MoE deepgemm_megamoe EP implementation for DeepGEMM Mega MoE Oct 16, 2026
IlyasMoutawwakil and others added 2 commits November 2, 2026 05:51
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
num_topk=num_top_k,
num_experts=num_global_experts,
num_max_tokens_per_rank=num_tokens,
num_max_tokens_per_rank=1 << (num_tokens - 1).bit_length(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to do that but not before ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't strictly have to; it bounds the reallocations. Before, each experts module kept its own buffer, sized to the batch, so every new largest batch reallocated it, once per layer. Now every layer of an EP group shares one buffer per shape, and allocating it is a collective rendezvous over symmetric memory, so it's sized to the next power of two: a batch that grows (prompts of different lengths, more sequences) reallocates a few times instead of at every new size, for at most twice the memory.

| :--- | :--- | :--- |
| `"all_to_all"`, see [token dispatch](#token-dispatch) | Works for any layout, including `ep_size > tp_size`. Only the routed tokens move. | Two all-to-all collectives and one host sync per layer, so it does not capture in a CUDA graph. |
| `"all_reduce"` | No tokens move and no host sync, so decode captures in a CUDA graph. | Requires `ep_size=tp_size`. Every rank runs its experts over the whole batch, so its cost grows with the batch. |
| `"deepgemm_megamoe"` | DeepGEMM Mega MoE fuses the dispatch, the experts and the combine in one kernel. Every layer of an EP group shares its symmetric-memory buffers. | Requires `ep_size=tp_size`, and FP8 activations with FP4 experts on SM100+. Inference only. |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how should we decide with auto which one to use at inference between all_reduce and deepgemm_megamoe ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepgemm_megamoe ep impl is locked at load time if the model uses deepgemm_megamoe experts impl, it's not part of the auto dispatch

        # DeepGEMM Mega MoE exchanges the tokens inside its kernel, through the `deepgemm_megamoe` implementation's buffers
        distributed_config = getattr(model.config, "distributed_config", None)
        if (
            getattr(model.config, "_experts_implementation", None) == "deepgemm_megamoe"
            and distributed_config is not None
        ):
            if distributed_config.ep_implementation not in ("auto", "deepgemm_megamoe"):
                raise ValueError(
                    "DeepGEMM Mega MoE exchanges tokens inside its kernel and only runs with "
                    f'`ep_implementation="deepgemm_megamoe"`, got '
                    f"`ep_implementation={distributed_config.ep_implementation!r}`."
                )
            distributed_config.ep_implementation = "deepgemm_megamoe"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep shouldnt be a decision for auto only for specific usecases, something we can share as recipe for specific models

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update: moved this out of the quantizer, AutoEP now builds MegaMoeEP itself when the experts use deepgemm_megamoe. it's forced by the experts impl (they can't run any other way), not a perf choice, so it fits auto. an explicit other ep_implementation raises in install_forward

IlyasMoutawwakil and others added 5 commits November 4, 2026 12:19
`AllReduceEP` had nothing to dispatch and `AutoEP` bypassed both steps, so the
two-step contract didn't fit. An `EPImplementation` now implements `forward`
and runs the local experts with a shared `run_experts` (local DTensor params,
empty rows kept connected to backward). `AllToAllEP` keeps its two exchanges
as private `_send` / `_receive` steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every `EPImplementation` defines `__call__` (AutoEP already did), and the base
class keeps `run_experts`, now owning the local DTensor params context.
`_all_reduce_backward` is gone: its call sites check
`torch.is_grad_enabled() and x.requires_grad` before `_AllReduceBackward.apply`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `EpDispatchExpertsParallel.ep_implementations` maps the three built-in
  names to their classes, replacing `EPImplementationInterface` /
  `ALL_EP_IMPLEMENTATIONS`; docs and the config docstring point to it.
- `AllToAllEP`'s halves are `_dispatch_tokens` and `_combine_tokens` again.
- `EpDispatchExpertsParallel.should_use_local_tensors` is back.
- Tests, from review: one parametrized check per implementation and mode
  (train, inference, both checkpoint modes) against one process, a separate
  check of AutoEP's choice, and install_forward's refusals.

Co-authored-by: Ferdinand Mom <47445085+3outeille@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`_same_ranks` had one caller; the comparison of the TP and EP groups' ranks
now sits where `shares_batch` is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ations`

`deepgemm_megamoe` joins the dict on `EpDispatchExpertsParallel` that replaces
`EPImplementationInterface`; its test keeps the new parametrized ones and uses
local inputs. From review: the class is `MegaMoeEP`, and the DeepGEMM error
says "implementation".

Co-authored-by: Ferdinand Mom <47445085+3outeille@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vasqu

vasqu commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Actual question but what is the benefit of keeping deepgemm? I would expect we switch to deep ep anyways and deepgemm is hard to maintain with lots of restrictions.

Imo, it might be more worth it to drop hard restrictions like these? Or am i too eager to drop it lol

@IlyasMoutawwakil

Copy link
Copy Markdown
Member Author

deepgemm might still be the best way to serve w4a8 mxfp4 experts (dsv4), once we have deepEP and fused moe kernels, we can compare and drop it if it loses 🫣

@vasqu

vasqu commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

ok fair enough, i just hate deepgemm atp (sorry not sorry 😂)

IlyasMoutawwakil and others added 2 commits November 6, 2026 10:37
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…xperts class in tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vasqu vasqu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments but overall fine

| :--- | :--- | :--- |
| `"all_to_all"`, see [token dispatch](#token-dispatch) | Works for any layout, including `ep_size > tp_size`. Only the routed tokens move. | Two all-to-all collectives and one host sync per layer, so it does not capture in a CUDA graph. |
| `"all_reduce"` | No tokens move and no host sync, so decode captures in a CUDA graph. | Requires `ep_size=tp_size`. Every rank runs its experts over the whole batch, so its cost grows with the batch. |
| `"deepgemm_megamoe"` | DeepGEMM Mega MoE fuses the dispatch, the experts and the combine in one kernel. Every layer of an EP group shares its symmetric-memory buffers. | Requires `ep_size=tp_size`, and FP8 activations with FP4 experts on SM100+. Inference only. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep shouldnt be a decision for auto only for specific usecases, something we can share as recipe for specific models

Comment on lines -146 to -147
| `RouterParallelMegaMoe` | Router variant for DeepGEMM Mega MoE, which dispatches experts inside the kernel which wants the router output untouched. |
| `MoeTensorParalellMegaMoeExperts` | Inference-only experts layer for DeepGEMM Mega MoE. Skips the gradient syncs and passes the process group into the module so the kernel can set up its shared buffers on the first forward. |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

breaking 🚨 but for good reasons

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, added 🚨 to the title and a breaking note in the description

"""

# TODO: Mega MoE is built for ranks holding different batches (data-parallel attention + EP), where neither the
# slicing nor the gather is needed. Until inference supports data parallelism, it runs on the shared batch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh interesting so we can make this less restrictive under DP? guess we need to check the dp mesh additionally then (additional kwarg)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, megamoe is built for dp attention + ep where each rank has its own batch, then neither the slice nor the gather is needed. inference has no dp yet, so it stays a todo until it does, and that's where the dp mesh check would go

# slicing nor the gather is needed. Until inference supports data parallelism, it runs on the shared batch.
needs_shared_batch = True
# The kernel's symmetric-memory buffers, by EP group. They are slow to allocate, so every layer of a group shares
# them, as the layers run one after another. `MegaMoeEP.symm_buffers.clear()` frees them.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a dedicated function, id expect deep ep to have something similar so anything that clears all buffers

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, destroy_megamoe_buffers() in integrations/deepgemm.py, same as deepep's destroy_deepep_buffers(). the buffers live there now, per ep group, so MegaMoeEP doesn't hold them anymore

Comment on lines +434 to +437
with _use_local_dtensor_params(self.experts):
local_output = self.experts_forward(
hidden_states[rows], top_k_index[rows], top_k_weights[rows], ep_group, self.group_symm_buffers
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we maybe just adjust the experts forward to allow

  1. rows
  2. arbitrary kwargs (like the buffers)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

partly: it now only passes process_group= by name, the megamoe forward looks up its buffers by group itself. i'd keep the other experts forwards as they are though, the extra arg only exists for the megamoe kernel, and the rows are just a slice of the inputs so nothing to add there

getattr(model.config, "_experts_implementation", None) == "deepgemm_megamoe"
and distributed_config is not None
):
if distributed_config.ep_implementation not in ("auto", "deepgemm_megamoe"):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we be stricter and disallow auto?

@IlyasMoutawwakil IlyasMoutawwakil Nov 12, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd keep auto: loading with experts_implementation="deepgemm_megamoe" should just work without also setting ep_implementation. AutoEP now builds MegaMoeEP itself for those experts (no more config rewrite in the quantizer), and any other explicit implementation raises when the plan is applied

IlyasMoutawwakil and others added 4 commits November 9, 2026 18:41
…face registry, doc notes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y-rows case in `AllToAllEP`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o ep-passthrough-strategy

# Conflicts:
#	docs/source/en/expert_parallelism.md
#	src/transformers/distributed/expert_parallel.py
Base automatically changed from ep-masked-when-shared-batch to main November 12, 2026 10:33
…_megamoe_buffers()`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IlyasMoutawwakil IlyasMoutawwakil changed the title deepgemm_megamoe EP implementation for DeepGEMM Mega MoE 🚨 deepgemm_megamoe EP implementation for DeepGEMM Mega MoE Nov 12, 2026
# Conflicts:
#	docs/source/en/expert_parallelism.md
#	src/transformers/distributed/configuration_utils.py
#	src/transformers/distributed/expert_parallel.py
#	tests/expert_parallel/test_expert_parallel.py
#	tests/quantization/finegrained_fp8/test_fp8.py
@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: finegrained_fp8

IlyasMoutawwakil and others added 3 commits November 12, 2026 11:47
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…o longer rewrites `ep_implementation`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants