[feat][PCP] Enable PCP MoE-Merge+TBO for DeepSeek V4#1553
Open
yitingw1 wants to merge 22 commits into
Open
Conversation
P0: Relax PCP+TBO raise in llm_engine.py — only block TBO-decode+PCP; prefill-only TBO now allowed. P1 (coordinated split, b1 approach): - pcp_utils.py: add num_ubatches param to pcp_pad_len (N·pcp alignment) - model_runner.py: _preprocess overrides TBO eligibility with 1/pcp local token count; Option A gate (n_prefill % 2*pcp == 0) avoids dummy tokens; prepare_inputs builds ubatch_slices in 1/pcp space with correct request boundaries; run_model does PCP split before UBatchWrapper, updates context.positions and forward_vars["positions"/"cu_seqlens_q"]; out-of- graph pcp_allgather_rerange after UBatchWrapper. - ubatch_wrapper.py: _make_ubatch_context carries input_ids per-ubatch slice for hash-MoE (moe_pcp_merge mode B). - deepseek_v4.py: ForCausalLM.forward skips PCP split and final pcp_allgather_rerange when tbo_active(); per-ubatch input_ids allgather for hash-MoE. - deepseek_v4_attn.py: build_ubatch_prefill_metadata uses real_num_tokens for _attach_v4_per_fwd_meta; Option B (arbitrary length) planned as per-ubatch reindex — see PCP_TBO.md §11. P2: moe_pcp_merge_forward inserts TBO yield/switch around pcp_allgather_rankmajor and pcp_reduce_scatter. Attention-internal PCP collectives (compressor kv_score, kv, positions allgather) wrapped with tbo_yield_and_switch / tbo_switch_to_compute_sync to serialize RCCL submissions across TBO ubatch threads. Add ATOM_TBO_DEBUG env var and per-collective debug logging in pcp_utils.py for locating RCCL hang root cause (see PCP_TBO.md §9.5). Verified: warmup ✓, gsm8k=0.86 (Option A mixed path), server stable. Option B (arbitrary length) deferred — requires per-ubatch reindex. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
… into enable_deepseek_PCP_merge_TBO
Contributor
🏷️ CI GuideRuns automatically on every eligible PR before approval:
Heavy model tests:
|
| and not batch.is_dummy_run | ||
| and getattr(self, "_pcp_tbo_balanced_active", False) | ||
| ) | ||
| if _pcp_tbo_balanced: |
Collaborator
There was a problem hiding this comment.
can we make prepare_inputs more clean? Line 1897~1938 PCP+tbo related in a new function?
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.
Motivation
PCP (Prefill Context Parallel, #1220) splits the prefill token sequence across the
PCP group but left MoE running per-rank on each
1/pcpshard, so MoE weights were replicated across PCP ranks. This PR adds a MoE-merge mode that folds the PCP dimension into the MoE tp/ep sharding, and enables PCP + TBO to overlap the extra MoE communication that introduces during prefill.ATOM_PCP_MOE_MERGE=1(default): all-gather hidden1/pcp → fullbefore MoE and slice back after, so MoE weights are sharded across PCP ranks (lower per-GPU MoE memory) at the cost of one hidden gather/scatter per MoE layer.--enable-tbo(prefill) overlaps that gather/scatter with compute via two-batch overlap, using a request-boundary micro-batch split.Known Limitations
-tp 1; withtp > 1it may hang under certain conditions (long sequence + high concurrency), so it is not yet supported.ATOM_PCP_MOE_MERGE=1; auto-disabled with a warning when merge is off.--enable-tbo all) and PCP + DP-attention are unsupported.Test Plan
Serve DeepSeek-V4 on 8 GPUs and compare long-prefill TTFT / throughput:
Test Result
ATOM_PCP_MOE_MERGE=0/1produce consistent outputs; PCP+TBOprefill matches non-TBO PCP.
MoE-merge performance (
-tp 4 -pcp 2, chunked-prefill OFF, output 1k)benchmark_servingrandom dataset (range_ratio 0.8, ignore-eos), 8×MI308. Cell =Mean TTFT(ms) / Total Tput(tok/s); parentheses = improvement vs the TP8 baseline
(TTFT ↓ reduction, Tput ↑ gain).
Input-length sweep (concurrency 8):
ATOM_PCP_MOE_MERGE=0ATOM_PCP_MOE_MERGE=1Concurrency sweep (input 8k):
ATOM_PCP_MOE_MERGE=0ATOM_PCP_MOE_MERGE=1ATOM_PCP_MOE_MERGE=1shards experts 8-way (fits large models single-node) while staying close to=0; they converge at long sequences where attention O(n²) dominates the extra per-layer all-gather.Heavy-prefill + PCP-degree sweep (input 64k / output 1k, c32, 8×MI308)
mbt=131072, compile + CUDAGraph, chunked-prefill OFF, util 0.8, TP MoE. Only the-tp 1config runs TBO (TBO requires-tp 1).ATOM_PCP_MOE_MERGE=0ATOM_PCP_MOE_MERGE=1ATOM_PCP_MOE_MERGE=1ATOM_PCP_MOE_MERGE=1ATOM_PCP_MOE_MERGE=1+ TBOATOM_PCP_MOE_MERGE=1, not TBO): TP8 → TP1PCP8 gives TTFT ↓56% (81379 → 36132) and Tput ↑96% (5905 → 11572). Higher PCP degree is better under heavy prefill.Decode-length sweep (
-tp 4 -pcp 2, input 32k, c8, 8×MI308)Fixed input 32768 / concurrency 8 / util 0.8 / mbt 131072 / chunked-prefill OFF,
sweeping output length. Cell = Total Tput(tok/s) / Mean TPOT(ms).
ATOM_PCP_MOE_MERGE=0ATOM_PCP_MOE_MERGE=1End-to-end latency (E2E = TTFT + (out−1)×Mean TPOT, seconds):
ATOM_PCP_MOE_MERGE=0ATOM_PCP_MOE_MERGE=1PCP+TBO (prefill overlap)
TBO benefit is hardware-dependent: on MI308 it gives almost no gain (the PCP collectives are too light for the overlap to hide), while on MI355 (stronger compute → prefill compute is shorter → PCP communication is a larger share → more for TBO to overlap) it delivers a clear speedup.
MI355, shipped config
-tp 1 -pcp 8(64k/1k, c32,GPU_MAX_HW_QUEUES=5) — cell =Mean TTFT / Median TTFT / P99 TTFT(ms) / TPOT(ms) / Tput(tok/s):
ATOM_PCP_MOE_MERGE=1ATOM_PCP_MOE_MERGE=1+ TBOAppendix. Technical Details
atom/utils/envs.py: newATOM_PCP_MOE_MERGE(default1); only active whenpcp > 1.atom/models/deepseek_v4.py,atom/model_ops/moe.py: MoE-merge path — all-gather hidden to full sequence before MoE, scatter back to1/pcpafter.atom/model_engine/model_runner.py,atom/model_ops/attentions/deepseek_v4_attn.py,atom/utils/tbo/ubatch_wrapper.py: PCP+TBO prefill grouping and attention metadata. PCP+TBO usesATOM_TBO_PREFILL_TOKEN_SPLIT=0(request-boundary split — each micro-batch is a whole subset of requests), not the default token-midpoint split used without PCP. Reason: PCP round-robin-shards tokens within each request across ranks, so a token-midpoint split would cut across the PCP sharding and desync the micro-batches; splitting on request boundaries keeps each request's tokens together.ATOM_TBO_PREFILL_TOKEN_SPLITtherefore has no effect under PCP.atom/model_engine/llm_engine.py: validation — TBO requiresATOM_PCP_MOE_MERGE=1; auto-disable + warn when merge is off; decode TBO (--enable-tbo all) and DP-attention remain unsupported with PCP.-tp 1: withtp > 1PCP+TBO hangs, so TBO is only allowed whentp == 1(all GPUs go to PCP, e.g.-tp 1 -pcp 8).docs/context_parallel_guide.md: usage, constraints, and the TP=1 / hardware notes.Submission Checklist