Repository navigation
[https://nvbugs/6919428][fix] Keep DeepSeek-V4 TP decode all-reduces off NCCL - #20233
Merged
chenfeiz0326 merged 4 commits intoOct 15, 2026
Merged
Conversation
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.
Description
Fixes https://nvbugs/6919428, a perf regression on GB300 DeepSeek-V4-Pro FP4 TEP8 gen_only. Mean gen-worker device step time rose from 14.68 to 17.39 ms (+18.5%) after #19679 upgraded the base image to PyTorch 26.09, which brings in NCCL 2.31.2.
Root cause. When TP spans two nodes and the allreduce strategy is AUTO, each DeepSeek-V4 decoder layer issued two NCCL all-reduces per decode step:
AllReduceFusionOp.RMS_NORM).MNNVLAllReducehas no plainRMS_NORMpattern, so this call fell back to NCCL plus a separate RMSNorm kernel.dtype, soAllReducenever constructedMNNVLAllReduce.These decode messages are 57 KB (4 tokens × 7168 × bf16). That is below
runNCCLAllReduceSymmetric's input-registration threshold of about 228 KiB at 8 ranks, so the input is never window-registered. NCCL 2.30.7 still ran these calls on its symmetricAllReduce_AGxLLMC_Rkernel (about 14.8 µs). NCCL 2.31.2 picks 1-channelRING/LLwithsymKernelId = Unknown(about 32.9 µs). That adds roughly 2.2 ms per decode step, which accounts for the regression. Preloading NCCL 2.31.2 into the old 26.08 image, with the old wheel, reproduces the whole regression. Rolling NCCL back is not an option, because PyTorch 26.09 requires 2.31 symbols.Fix. This PR changes only DeepSeek-V4 modeling:
o_b_proj(project_sparse_attn_outputignoresall_reduce_params). The pre-MoE all-reduce therefore summed a tensor that was already replicated, scaling it bytp_sizejust before an RMSNorm that cancels the scale. DeepSeek-V4.1 already disables it for the same reason (modeling_deepseekv41.py: "Attention completes TP reduction before mHC; do not reduce the FFN input again.").disable_attn_allreduceis unchanged: oncePRE_MOE_FUSIONisFalse, its existing expression produces the same value as before.dtype=config.torch_dtypeto theDeepseekV4MoEAllReduce. Under AUTO, a multi-node TP group now takesMNNVLAllReduce, aso_b_projalready does.With both changes, the DeepSeek-V4 TP decode path issues no NCCL all-reduce, so it no longer depends on which kernel NCCL selects. Removing the redundant collective also makes decode faster than the pre-regression baseline. Numerics change slightly: RMSNorm's
epsused to act on atp_size-scaled input, which is no longer the case.Not in scope, and worth separate follow-ups:
MNNVLAllReducesupport for plainRMS_NORM(the kernel already haskARRMSNorm).NCCL_SYMMETRIC.Test Coverage
tests/unittest/_torch/modeling/test_modeling_deepseekv4.py. This file is already inl0_b200.ymlandl0_dgx_b200.yml.test_deepseek_v4_forward_moe_does_not_reduce_replicated_input, parametrized over TP1, TP4 and TP4+ADP, with eager fusion on and off. It checks thatPRE_MOE_FUSIONstays off and thatforward_MoEissues no all-reduce on the replicated input.test_deepseek_v4_decoder_forward_requests_attention_reduction, over the same cases. It runsDeepseekV4DecoderLayer.forwardwith a recording attention stub. It checks that attention is asked to reduce exactly when TP needs it, that the MoE output reduction is still requested, and that no other all-reduce is issued.test_deepseek_v4_forward_moe_normalizes_unscaled_inputruns a TP4 layer with the realRMSNormand a largeeps. It checks that the MoE input is the RMSNorm of the unscaled replicated input, not of atp_size-scaled copy.test_deepseek_v4_moe_allreduce_is_mnnvl_eligiblechecks that the MoEAllReducereceives the model dtype.fca0803CI wheel, the file's 61 non-GPU tests pass with this PR's model file. With the base model file, the four TP4-with-eager-fusion and MoE-dtype cases fail.test_modeling_deepseekv41.pydecoder-reduction tests (does_not_reduce_replicated_input,pre_fusion_requires_unnormalized_input) still pass. I ran them, plus the new tests, on the GenPostMergeBuilds 2911 wheel with this PR'smodeling_deepseekv4.pyoverlaid: 9 + 8 passed.accuracy/test_llm_api_pytorch.py::TestDeepSeekV4Flash::test_tep_mtp_separate_draft_kv_cache(TEP4 + MTP,enable_attention_dp=False, which is the decoder path this PR changes). Passed on the 2911 wheel with this PR's file: GSM8K 95.30 vs 95.07 for unpatched main (reference 95.11); MTP acceptance length 1.9584 vs 1.9581 (min 1.860).aggr_upload-gen_only_no_context-gb300_deepseek-v4-pro-fp4_8k1k_con8_ctx1_dep4_gen4_tep8_eplb0_mtp3_ccb-NIXL. Each arm uses a CI-built wheel with its own harness and pinned image. The good and bad anchors are the L0_PostMerge 3010 and 3012 artifacts that produced the bug's numbers; main is GenPostMergeBuilds 2911. Patched arms overlay the modifiedmodeling_deepseekv4.pyinto the installed wheel.cc63e66(26.08, NCCL 2.30.7)c490057(26.09, NCCL 2.31.2)c490057+ this fixfca0803(GenPostMergeBuilds 2911)fca0803+ this PR's fileOn main, the same wheel and image give 19.01 → 14.34 ms mean (−24.6%) and 17.15 → 13.82 ms median (−19.4%). The fixed build also beats the pre-regression good commit by about 6% on the median. Some reps on the 26.09 image include bursts of slow iterations (> 20 ms), which inflate the mean but not the median; the fix leaves them in place.
PR Checklist
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.🤖 Generated with Claude Code
Dev Engineer Review
DeepseekV4MoEnow passesconfig.torch_dtypetoAllReduce, allowingAUTOto select MNNVL where supported.DeepseekV4DecoderLayerno longer issues a pre-MoE all-reduce. Attention’s output projection already completes the TP reduction. The change also removes the prior environment-variable and attention-DP controls for pre-MoE fusion.RMS_NORMsupport in MNNVL and NCCL’s small-message registration threshold remain out of scope.QA Engineer Review
tests/unittest/_torch/modeling/test_modeling_deepseekv4.py. New tests cover unscaled-input normalization, attention reduction requests across TP and attention-DP configurations, and MoEAllReducedtype and backend eligibility.tests/integration/test_lists/test-db/l0_b200.ymlandl0_dgx_b200.yml.l0_rtx_pro_6000.ymllists the specifictest_deepseek_v4_sanity[fp8-ds-mla]test. No matching manual-QA list entry was found.Per-File QA Perspective
tensorrt_llm/_torch/models/modeling_deepseekv4.py: Verify that eligible multi-node TP groups select MNNVL and that removing the pre-MoE reduction preserves expected outputs. Check accuracy with RMSNorm receiving the unscaled input.tests/unittest/_torch/modeling/test_modeling_deepseekv4.py: Tests cover normalization input scaling, reduction requests, and MoE all-reduce configuration. The module is listed in B200 CI lists; one sanity test is listed for RTX Pro 6000, and no matching manual-QA entry was found.