Repository navigation
Add the deepep expert-parallel implementation - #49638
Open
IlyasMoutawwakil wants to merge 11 commits into
Open
IlyasMoutawwakil wants to merge 11 commits into
IlyasMoutawwakil wants to merge 11 commits into
Conversation
|
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: |
Collaborator
|
Are we sure the branch is in sync? Seems like a lot of other changes are included that shouldnt be in this prs diff imo But I see this PR as less prio overall: 1-5 are core and need merge, this is a nice perf optim to get after |
…th it where it runs DeepEP's dispatch and combine (kernels-community/deep-ep) run token dispatch without a host sync under CUDA graph capture or torch.compile. `auto` prefers DeepEP for token dispatch at every batch size where it loads, and DeepEP may take more SMs, since nothing overlaps with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IlyasMoutawwakil
force-pushed
the
ep-deepep-strategy
branch
from
October 27, 2026 21:49
8f57f98 to
2f363cd
Compare
# Conflicts: # docs/source/en/expert_parallelism.md # src/transformers/distributed/expert_parallel.py # tests/expert_parallel/test_expert_parallel.py
deepep expert-parallel strategydeepep expert-parallel implementation
Member
Author
|
yes it was behind, merged the updated stack so the diff is only deepep now |
# Conflicts: # docs/source/en/expert_parallelism.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DeepEP gets back its own forward: an earlier merge dropped the base class's dispatch-experts-combine driver, so `ep_implementation="deepep"` and `auto`'s DeepEP path raised NotImplementedError. It now runs like `AllToAllEP` (`_dispatch_tokens` / `_combine_tokens`, the empty-rows case shared through `_run_on_received`), takes the TP mesh it is always given, and registers in `EPImplementationInterface`. The DeepEP tests use the merged `_DummyExperts` and size-1 TP meshes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # src/transformers/distributed/expert_parallel.py # tests/expert_parallel/test_expert_parallel.py
# Conflicts: # src/transformers/distributed/expert_parallel.py
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #49615.
What this adds
A
"deepep"expert-parallel implementation. It runs DeepEP's dispatch and combine, loaded from the Hub (kernels-community/deep-ep), as a newEPImplementationinALL_EP_IMPLEMENTATIONS.torch.compile(torch.cuda.is_current_stream_capturing()/torch.compiler.is_compiling()), it runs without the host sync. The rows are sized for the worst case (every rank sending its whole capacity with all of its top-k experts on this rank), so they cannot overflow. Rows past the received total are zeroed and routed to the sentinel expert, which the experts skip.torch.librarycustom ops with fake implementations and registered autograd, so the implementation trains, captures and compiles (fullgraph=Trueat the experts level). The DeepEP handle can't go through an op, so the ops pass its tensors and rebuild it.combine(..., topk_weights=slot_grads), and the combine's backward dispatches along the forward's layout.transformers.integrations.deepep.destroy_deepep_buffers()frees the buffers.tp_size > 1, the ranks of a TP group each dispatch a disjoint slice of the batch they share, likeAllToAllEP. Both implementations share the slicing and gathering helpers.integrations/deepep.pymirrorsintegrations/deepgemm.py.is_deepep_loadable(raise_error=False)(lru-cached) checks for CUDA, SM ≥ 90, a CUDA toolkit ≥ 13.1 (found by DeepGEMM's_get_cuda_home/_get_nvcc_version, which are reused), exactly one NCCL ≥ 2.32.3 loaded by PyTorch (it reads the runtime version throughncclGetVersion, sincetorch.cuda.nccl.version()is the build-time one), and whether the Hub kernel loads.load_deepep_kernel()raises theImportErrorthat names the missing requirement.ep_implementation="deepep"fails at load with that message.Scope: intranode only (hybrid mode is left at DeepEP's default and is untested across nodes). There is no low-latency mode; it is gone from DeepEP 2.5. FP8 dispatch (
use_fp8_dispatch, half the dispatch bytes for FP8 recipes) is a follow-up.Usage
Requirements: the
kernels-community/deep-epHub kernel (version 1, from kernels-community#1231, not merged yet; until then, the staged build works throughLOCAL_KERNELS=kernels-community/deep-ep=<snapshot of kernels-staging/deep-ep@pr-1231>). You also need the NCCL that PyTorch loads at ≥ 2.32.3 (e.g.nvidia-nccl-cu13==2.32.3), a CUDA toolkit ≥ 13.1 at runtime (nvcc, throughCUDA_HOME, since DeepEP JIT-compiles its kernels), SM90+, and a hidden size that's a multiple of 256 (DeepEP's combine; anything else gets a clearValueError)."auto""auto"now prefers DeepEP wherever it used to pick all-to-all. Whether DeepEP is usable is decided once per process: an NCCL EP group andis_deepep_loadable(). If it isn't,"auto"falls back to all-to-all with a singlelogger.info_once.ep_size == tp_size), inferenceall_reduceall_reducetp_size < ep_size), eagerdeepep(host sync)all_to_alltorch.compiledeepep, sync-freeall_to_all(not capturable)No token threshold: in DeepEP's expanded layout, each rank only runs its own expert routes, so it never loses to the all-to-all by more than run-to-run noise (below ~1024 tokens/rank every eager implementation is host-bound) and wins from ~4096 tokens/rank. A cutoff tuned on one EP size and GPU would not carry over to others.
Benchmarks
Hardware and software: B200 node (NVLink), torch 2.14.0+cu130, NCCL 2.32.3, DeepEP 2.5.0 (staged
kernels-staging/deep-ep@337d4ae, nvcc 13.2), expertsgrouped_mmin bf16.Setup:
30bis Qwen3-30B-A3B (E=128, H=2048, I=768, top-8) andh4096is E=128, H=4096, I=1536, top-8.Bold marks the fastest implementation in each cell.
inference, eager (ms per layer; all_to_all / all_reduce / deepep)
training fwd+bwd, eager (ms per layer; all_to_all / all_reduce / deepep)
decode, CUDA graph (ms per layer; all_to_all / all_reduce / deepep)
Takeaway.
Tests
tests/expert_parallel/test_expert_parallel.py, after merging the updated stack: 12 passed on 2x B200 with the staged kernel. The new tests are:test_is_deepep_loadable: mocked environments, each missing requirement returnsFalse, and withraise_error=Trueraises its message.test_auto_dispatches_with_deepep_where_it_runs: NCCL and loadable picks deepep, otherwise all_to_all;"deepep"raises the missing requirement.test_deepep_matches_one_process(2 GPUs): forward and input/routing-weight gradients match a single process, on a shared batch and on per-rank batches. It also checks that"auto"trains with DeepEP.test_deepep_captures_and_compiles(2 GPUs): a CUDA graph capture after an eager warm-up replays like eager, andtorch.compile(fullgraph=True)matches eager.from_pretrainedwithdistributed_config, bf16, forward and backward on the same inputs), deepep against all_to_all and a single process:(tp, fsdp, ep): 2 GPUs(1,2,2),(2,1,2); 4 GPUs(1,4,4),(2,2,4),(4,1,4),(1,4,2),(2,2,2). Each ran with equal batches and with uneven per-batch lengths (different batch sizes across ranks, uneven TP slices).torch.compile(fullgraph=True)is blocked by FSDP's hooks, not by this implementation; the experts-level fullgraph compile passes.ruff check/ruff format,ty checkon touched files,utils/check_copies.py,utils/check_docstrings.py.🤖 Generated with Claude Code