Repository navigation
Conversation
…egaMoE replica mapping Add an opt-in "symm_mem" EPLB communicator for EP groups that share one NVLink domain (MNNVL across nodes). The per-layer receive buffer (expert_buffer) is allocated once in CUDA symmetric memory with a single rendezvous, and senders copy expert rows from their live weights straight into the receiver's buffer row. Sender and receiver derive that row from the global placements with the same helper, in both the default and the batched migration path. Each execute() is fenced by two CPU barriers only, since GPU spin barriers can deadlock MegaMoE cooperative launches. The backend needs no memory beyond the existing expert_buffer and is rejected on elastic EP. Fuse the EPLB logical-to-physical replica mapping and expert-load recording into both DeepGEMM MegaMoE input-staging kernels (MXFP8/BF16 and NVFP4). This drops the separate mapping launch and padding/cast copies per MoE layer. The replica choice and load recording live in one Triton helper shared with the router's eplb_map_to_physical_and_record kernel, so both paths pick the same physical ids. Routed-experts capture still sees logical ids, and other MoE layers keep the router path. Async EPLB: - Commit the per-layer maps with pinned non-blocking copies, and fence expert_buffer reuse on the worker thread instead of the main thread. - Raise on every rank when the worker failed on any rank instead of stalling silently. - Log each rearrangement's duration. Also: - Size logical_to_physical_map to num_redundant_experts + 1 slots unless elastic EP can change the physical expert count. - Vectorize compute_logical_maps, which runs on the main thread per layer. - Run the symm_mem execution tests in the 8xH100 NVLink distributed job. - Mark the DeepSeek V4.1 decoder-replay batch's padding and dummy rows as padding, so MoE neither computes them nor records them as expert load. Co-authored-by: Juntian Liu <Juntianl777@gmail.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bowen Wang <bowen@inferact.ai>
- Read balancedness stats back with a non-blocking copy and log them, in order, once each copy lands, instead of a blocking .tolist() that drains the whole queued forward every log_balancedness_interval steps. - Write the shared should_record flag only when its value flips, instead of one fill kernel per step. - Run the per-step async "all ranks ready" check during transfer windows on cpu_all_reduce_ (recursive doubling over gloo P2P) instead of a gloo ring all-reduce. Failed and pending ranks are counted with a SUM, so a worker failure on any rank still raises on every rank. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bowen Wang <bowen@inferact.ai>
Contributor
|
Documentation preview: https://vllm--62009.org.readthedocs.build/en/62009/ |
abmfy
marked this pull request as ready for review
October 16, 2026 11:52
abmfy
requested review from
AndreasKaratzas,
DarkLight1337,
Harry-Chen,
ProExpertProg,
WoosukKwon,
houseroad,
khluu,
mgoin,
pavanimajety,
robertgshaw2-redhat,
tlrmchlsmth,
wzhao18,
yewentao256,
youkaichao,
ywang96 and
zyongye
as code owners
October 16, 2026 11:52
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.
Overview
This PR makes EPLB cheaper for DeepSeek-V4.1-Flash, running DP attention with EP MegaMoE on multi-node NVLink. It does three things:
symm_memweight-migration backend;Not a duplicate. No open or merged PR adds a symmetric-memory EPLB backend or fuses the MegaMoE mapping. #61176 and #61839 enable EPLB for the ROCm AITER MegaMoE backend. Related: #61767 (
cpu_all_reduce_, reused here), #57549, #56716, #61643.Claims
symm_mem: senders push expert rows into the receiver'sexpert_buffer, which is allocated in CUDA symmetric memory. There is no staging buffer, and it needs no memory beyondexpert_buffer. Cross-node peer copies run at about 800 GB/s, against about 88 GB/s for NIXL, and a 40-layer rearrangement transfers in 0.3–0.6 s at DEP32.eplb_map_to_physical_and_recordexactly.Validation
Hardware: GB200 NVL72, 4 GPUs per node.
Serving setup: DSpark with 5 draft tokens, MRV2, FULL CUDA graphs, async scheduling.
EPLB setup:
{"num_redundant_experts": <DP>, "window_size": 1000, "step_interval": 3000, "use_async": true, "communicator": "symm_mem"}, i.e. one redundant expert per rank.Workload: decode only, coding-agent prompts with mean ISL ≈ 85k (prefix-cached), 16k output tokens with
ignore_eos, and requests pinned evenly to DP ranks. Throughput is the output rate over the window in which every request is decoding.Off and on arms ran interleaved (ABAB) on the same nodes. These runs used a branch of nightly fe4df3d (2026-10-12) with this change set and #61767, before the rebase and without the second commit.
Per-step breakdown, DEP32 with 128 req/rank:
The GPU never waits on the host, so the MegaMoE saving turns into step time. MegaMoE is about 40% of the step, which caps the end-to-end gain.
Microbenchmarks (GB200):
Fused EPLB staging, one MoE layer, hidden 5120, top-k 6, 384 logical experts. Each arm is timed as a CUDA graph replay, median of 200. Outputs, ids and load counts match main bit for bit in every case.
torch.where+ map kernel + stagingThat is about 0.2 ms per decode step across 40 MoE layers.
Weight-migration bandwidth, 2 nodes, one MegaMoE layer of 25 experts (470 MB):
symm_mempeer copies, intra- or cross-node over multi-node NVLinkUCX_TLS=cuda_copy,cuda_ipc,tcpMegaMoE kernel time under serving load, DEP16 with 72 req/rank, layers 2–40: p50 per call drops 12.6–14.0% across three profiled runs (222→194 µs). Layer 1 is excluded because it absorbs cross-rank arrival skew.
Tests on this branch, one GB200 node with 4 GPUs. They ran before the rebase onto 64175d3; the 5 newer commits on main touch none of these files.
tests/kernels/moe/test_deep_gemm_mega_moe.py(incl.test_prepare_megamoe_eplb_matches_router_under_graph_replay)tests/kernels/moe/test_routing.py -k eplbtests/distributed/test_eplb_execute.pytests/distributed/test_eplb_execute.py -k nixl, NIXL installedtests/distributed/test_eplb_utils.py,test_eplb_migration_scheduler.pytests/models/test_deepseek_v41_replay_batch.py,test_deepseek_v41_decoder_replay_layers.pyGSM8K on DeepSeek-V4.1-Flash, DEP4 (one node):
num_redundant_experts=16,window_size=100,step_interval=300. Every arm rearranged all 40 layers four times during the eval.python tests/evals/gsm8k/gsm8k_eval.py --port 8000 --num-shots 5 --num-questions 1319 --max-concurrency 64symm_memsymm_mem,VLLM_GPU_SYNC_CHECK=errornixlThe EPLB-off and
nixlarms ran on the commit before the final cleanup, which does not touch either path. Within one node,symm_memand NIXL (CUDA IPC) move weights at a similar rate;symm_mem's advantage is across nodes over multi-node NVLink.Details
symm_membackenduint8buffer per EP group, set up with a single rendezvous and carved into per-tensor views. The backend checks the NVLink/MNNVL domain before allocating.add_recvis a no-op. Push backends get every rank's rows through a newrecv_rowsargument toset_transfer_context. This argument is optional and other backends ignore it.execute()runs two CPU barriers. GPU spin barriers can deadlock against MegaMoE's cooperative launches.Fused mapping:
fused_moe/eplb_triton.pyholds the replica hash, map lookup and gated load count. The MXFP8/BF16 and NVFP4 staging kernels call it, and so does the router kernel. Routed-experts capture still sees logical ids.Async EPLB
expert_bufferreuse.should_record_tensoris rewritten only when its value flips.cpu_all_reduce_. A gloo ring all-reduce costs several ms per step at DP16/DP32.Also
logical_to_physical_mapis sized to R+1 slots unless elastic EP is on.compute_logical_mapsis vectorized.CI: the
symm_memexecution tests need NVLink peers. They are added to the optional 8×H100 "Distributed DP + EP" job; the 4×L4 EPLB job skips them. The MegaMoE parity test needs SM100.Limitations
Pull Request Checklist
I used vLLM's
/pr-checklistskill. (Mandatory for agents, optional for humans).AI assistance was used during the creation of this PR.
Design Fit: Minimizes impact on core components, reuses existing functionality, and justifies added complexity.
Testing and Validation: Validates the change and ensures any added tests are meaningful and reliable, with CI coverage or documented CI resource constraints and validation performed outside CI.
Code Quality and Style: Keeps code and comments clear and concise, and updates relevant documentation and examples.
Pull Request Contents: Includes a brief summary and relevant links, supports claims with evidence, explains root causes and implementation trade-offs, and follows the contributing guide.
🤖 Generated with Claude Code