Skip to content

🚨 Remove the ep_router expert-parallel style - #49615

Open
IlyasMoutawwakil wants to merge 15 commits into
ep-mxfp4-gpt-ossfrom
ep-remove-router
Open

IlyasMoutawwakil wants to merge 15 commits into
ep-mxfp4-gpt-ossfrom
ep-remove-router

Conversation

@IlyasMoutawwakil

@IlyasMoutawwakil IlyasMoutawwakil commented Oct 14, 2026 •

Copy link
Copy Markdown
Member

🤗 Transformers CI

Depends on #49613.

After #49607 and #49613, no model's EP plan uses the router-masked plan (ep_router on the router, moe_tp_experts on the experts). ep_dispatch_experts covers it: DistributedConfig(ep_implementation="all_reduce") gives the same masking and all-reduce (#49429). So this PR removes the ep_router style.

Changes

  • distributed/expert_parallel.py: EpRouterParallel and its "ep_router" registration are removed.
  • distributed/mixin.py:
    • The masked-EP branch is removed.
    • A plan that names ep_router raises an error that points to ep_dispatch_experts and ep_implementation="all_reduce".
    • So does an ep_plan that names moe_tp_experts: without ep_router it would apply nothing and leave the experts unsharded. moe_tp_experts keeps working from tp_plan.
  • distributed/configuration_utils.py: the ep_size=tp_size check of the masked plan is removed. AllReduceEP checks it.
  • distributed/tensor_parallel.py: moe_tp_experts loses its is_expert_parallel flag, which no caller passes. It also loses the install_forward override that only passed that flag.
  • integrations/tensor_parallel.py: the RouterParallel and EpRouterParallel aliases are removed.
  • Llama 4:
    • Its EP plan is removed. It mixed TP styles with an ep_router rule and had no experts rule, so it never applied. With ep_size > 1, attention and the experts stayed replicated.
    • Its experts take only hidden_states, so they cannot use ep_dispatch_experts. ep_size > 1 now raises the "no ep_plan" error.
  • Docs:
    • The masking rows and the ep_router plan examples are replaced with ep_implementation="all_reduce".
    • EpRouterParallel leaves the parallel styles table.

🚨 Breaking changes

  • The ep_router style is removed. A plan that names it raises an error with the replacement.
  • moe_tp_experts in an ep_plan no longer selects masked EP; it raises an error with the replacement. In a tp_plan it works as before.
  • EpRouterParallel and its RouterParallel alias are removed.
  • Llama 4 has no EP plan. ep_size > 1 raises instead of silently replicating.

Tests

Test Result
tests/expert_parallel and tests/tensor_parallel on CPU, including both legacy plans being refused 32 passed
TP and EP tests of Llama 4, Mixtral, GPT-OSS, Qwen3-MoE, DeepSeek-V3 and Gemma 4 on CPU 432 passed, 1 error: a Gemma 4 processor test needs tiktoken
ruff, the noisy-comment, modular and copies checks clean

🤖 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 Remove the ep_router expert-parallel style Remove the legacy ep_router expert-parallel plan Oct 16, 2026

@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.

Not sure whether to fully remove or keep a BC reference at least? and warn when it is used -> remap to dispatch

Every model's experts run on `ep_dispatch_experts`, except Llama 4's, whose EP plan matched no expert-parallel mode
and was never applied; its `ep_router` rule goes too. `DistributedConfig(ep_strategy="all_reduce")` gives the same
masking and all-reduce, and a plan that still names `ep_router` now says so. `moe_tp_experts` stays for
tensor-parallel experts; only its unused expert-parallel flag goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IlyasMoutawwakil and others added 4 commits October 27, 2026 15:42
The class, its registration and the docs describing the router-masked plan were still there.
A plan that names `ep_router` now raises an error pointing to `ep_dispatch_experts` and
`ep_implementation="all_reduce"` instead of a bare KeyError. `moe_tp_experts` loses its
`is_expert_parallel` flag (no caller passes it) and the `install_forward` override that only
existed to pass it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/source/en/expert_parallelism.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@IlyasMoutawwakil IlyasMoutawwakil changed the title Remove the legacy ep_router expert-parallel plan 🚨 Remove the ep_router expert-parallel style Nov 2, 2026
@3outeille

Copy link
Copy Markdown
Member

I think it's best to fully remove it otherwise that will create so much confusion

@3outeille 3outeille left a comment

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.

small nit but lgtm

"layers.*.feed_forward.gate_proj": "colwise",
"layers.*.feed_forward.up_proj": "colwise",
"layers.*.feed_forward.down_proj": "rowwise",
"layers.*.feed_forward.router": "ep_router",

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.

is is the only file that has ep_router left ?

@IlyasMoutawwakil IlyasMoutawwakil Nov 5, 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.

No: ep_router was in nine model configs (Beam, DiffusionGemma, Gemma 4, HY-V4, Inkling, Llama 4, Mellum, MiMo-V2-Flash, Qwen3-MoE), and this PR removes it from all of them, modular files included. Llama 4 loses its whole base_model_ep_plan (4bad170). After this PR the only ep_router left in the repo is the error mixin.py raises for a user plan that still names it.

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.

can we not dispatch on llama4's moe instead?

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.

not without refactoring llama4's moe: its experts take every token already scaled by its router score (hidden_states only), not (hidden_states, top_k_index, top_k_weights), so there's nothing for dispatch to route. moving it to the standard experts interface is a follow-up

@qgallouedec qgallouedec mentioned this pull request Nov 4, 2026
The base inlined the router's backward all-reduce check; this branch removes the
`ep_router` style, so the class stays gone. In `MoeExpertsParallel`, the routing
weights' backward all-reduce is the inline check, without the expert-parallel
condition this branch removes.

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.

Mostly agree here, it's better to remove this indeed otherwise we keep some weird references overall

Comment thread docs/source/en/expert_parallelism.md Outdated
| Configuration | Result |
| :--- | :--- |
| `DistributedConfig(tp_size=4, fsdp_size=2, ep_size=4)` | Each TP group of four shares one batch. Training dispatches tokens, each rank taking a quarter of the batch; inference uses `"all_reduce"`. A masked `ep_plan` uses masking and all-reduce throughout. |
| `DistributedConfig(tp_size=4, fsdp_size=2, ep_size=4)` | Each TP group of four shares one batch. Training dispatches tokens, each rank taking a quarter of the batch; inference uses `"all_reduce"`; `ep_implementation="all_reduce"` uses it throughout. |

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.

setting ...

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

Comment thread docs/source/en/expert_parallelism.md Outdated
| `DistributedConfig(tp_size=1, fsdp_size=8, ep_size=4)` | Dispatch with an independent batch on each rank, no slicing, and experts FSDP-sharded across pairs of ranks. |
| `DistributedConfig(tp_size=2, fsdp_size=4, ep_size=4)` | Dispatch with a TP pair per batch, each pair slicing its batch in two; two batches per expert group. |
| `DistributedConfig(tp_size=8, ep_size=8)` | Every rank shares one batch. Training dispatches tokens, each rank taking an eighth of the batch; inference uses `"all_reduce"`. A masked `ep_plan` uses masking and all-reduce throughout. |
| `DistributedConfig(tp_size=8, ep_size=8)` | Every rank shares one batch. Training dispatches tokens, each rank taking an eighth of the batch; inference uses `"all_reduce"`; `ep_implementation="all_reduce"` uses it throughout. |

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.

same here

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

Comment thread docs/source/en/expert_parallelism.md Outdated
## Combining with FSDP2

Tensor and expert parallelism shard the weights across `tp`, but the optimizer state and the modules without a rule are still replicated on every rank of the group, which limits how large a model you can train. Add [FSDP2](./fsdp) on a second mesh dimension with `fsdp_size`. With masking and all-reduce, keep `ep_size=tp_size` and pass `ep_plan={"layers.*.mlp.gate": "ep_router", "layers.*.mlp.experts": "moe_tp_experts"}``.
Tensor and expert parallelism shard the weights across `tp`, but the optimizer state and the modules without a rule are still replicated on every rank of the group, which limits how large a model you can train. Add [FSDP2](./fsdp) on a second mesh dimension with `fsdp_size`. With masking and all-reduce, keep `ep_size=tp_size` and pass `ep_implementation="all_reduce"`.

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.

is this still correct we shard across their own meshes tp/ep no?

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.

right, fixed: they shard across their own meshes, tp and ep

Comment on lines -326 to -328
if {"ep_router", "moe_tp_experts"}.issubset(ep_plan.values()):
# Legacy masked EP: the EP group is the TP group, every rank keeps every token.
model = apply_tensor_parallelism(model, tp_mesh, ep_plan)

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.

this is a weird artifect ig if we keep moe tp experts on tp plan it still works as expected only adding it to ep plan is properly ignored right?

this is somewhat breaking as well so best to mention this change more explicitly

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, in the tp plan it works as before. in the ep plan it only meant something next to ep_router (the masked path), and alone it would now apply nothing and leave the experts unsharded, so it raises and points to ep_dispatch_experts / ep_implementation="all_reduce". added it to the breaking changes in the description, with a test

if torch.is_grad_enabled() and top_k_weights.requires_grad:
top_k_weights = _AllReduceBackward.apply(top_k_weights, tp_group)
if torch.is_grad_enabled() and top_k_weights.requires_grad:
top_k_weights = _AllReduceBackward.apply(top_k_weights, tp_group)

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.

we should use our helper here then depending on what we do on 1 in the stack

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, it calls _AllReduceBackward.apply unconditionally now, the grad check lives inside it since #49429

"layers.*.feed_forward.gate_proj": "colwise",
"layers.*.feed_forward.up_proj": "colwise",
"layers.*.feed_forward.down_proj": "rowwise",
"layers.*.feed_forward.router": "ep_router",

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.

can we not dispatch on llama4's moe instead?

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

Copy link
Copy Markdown
Contributor

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

run-slow: llama4

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.

4 participants