perf: optimize fused AllReduce + RMSNorm (custom_all_reduce) - #3817
perf: optimize fused AllReduce + RMSNorm (custom_all_reduce)#3817ftyghome wants to merge 7 commits into
Conversation
…k-reduce ar_fusion_epilogue_block_reduce: warpReduce (ds_bpermute) -> DPP multithread_reduce.
reduce_scatter_cross_device_store: use FLAT per-thread fp32 to reduce across ngpus.
local_device_load_rmsnorm: warpReduce (ds_bpermute) -> DPP wave_reduce.
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
|
There was a problem hiding this comment.
Pull request overview
This PR optimizes the fused custom AllReduce + RMSNorm implementation by switching several intra-wave and cross-rank reduction steps to shared DPP-based reduction primitives and by simplifying the 2-stage reduce-scatter path to remove LDS staging and block-level synchronization.
Changes:
- Switch 1-stage decode epilogue block reduction to
multithread_reduce(DPP-based) fromhip_reduce.h. - Rewrite 2-stage stage-1
reduce_scatter_cross_device_storeas a flat per-pack reduce (no LDS /__syncthreads()). - Switch 2-stage stage-2 RMSNorm square-sum reduction to the shared
wave_reduceprimitive and adjust stage-1 launch config to 256 threads.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| int rs_packs = size / (pack_size * world_size_); | ||
| dim3 rs_block(256); | ||
| dim3 rs_grid(std::min((rs_packs + 255) / 256, 80)); |
|
This is a follow-up to the previous closed PR: #3462. That PR became outdated and cannot be reopened, so we created this new PR instead. Since #3458 has been merged, the 1stage improvements here can now be reflected in the actual model inference path. We also found a more stable way to measure the per-op AllReduce kernel latency, which should make the performance results easier to validate. |
|
Hi all, The failing CI seems can be resolved by rebase this branch with upstream. Can anyone help rerun the CI? Thanks. |
|
Hi all, The CI is failing due to an env error. Can anyone help rerun this? Thanks! |
|
Closing this — we're not planning to pursue this further for now. Thanks for the reviews! |
Motivation
This PR makes three small updates to the custom fused AllReduce + RMSNorm kernels:
Technical Details
1stage
ar_fusion_epilogue_block_reducenow usesmultithread_reducefromhip_reduce.hinstead of the genericwarpReduceimplementation. This switches the intra-wave reduction from theds_bpermuteXOR butterfly to the existing DPP tree reduce.2stage
reduce-scatter.
reduce_scatter_cross_device_storeis rewritten as a flat reduce-scatter. Each thread reduces one pack across all input ranks in fp32, downcasts the result to bf16, and writes it to every rank's temporary buffer directly. This removes the previous LDS staging and__syncthreads()from the warp-per-rank implementation. The reduce order overptrs[0..ngpus-1]is preserved, so the result remains bit-identical to the previous path.stage-1 launch config. The stage-1 launch now uses block size 256 instead of 512. This gives small-
mdecode shapes more parallelism across CUs.stage-2 RMSNorm reduce.
local_device_load_rmsnormnow useswave_reducefromhip_reduce.hfor the RMSNorm square-sum reduction. This keeps the reduction implementation consistent with the shared DPP primitive. In testing, this change alone is performance-neutral.This PR does not change the one-stage/two-stage selection heuristic. The thresholds discussed in #3458 can therefore still be used as-is.
Test Plan
Test environment:
Benchmark:
The benchmark script (test_fused_ar_rmsnorm_perf.py is inspired by the script posted by @TennyWang1223 in #3458, with a few changes to improve stability.
To reduce host launch overhead and cross-rank rendezvous jitter, 2000 op calls are captured into one CUDA graph, and a single replay is timed with CUDA events. The reported latency is the minimum over 80 replays, taking the max latency across ranks. GPU clocks were pinned with:
Reproduce commands:
TP_LIST={4,8} AITER_AR_1STAGE={0,1} PYTHONPATH=$PWD \ python <script_path>Restore GPU clocks after benchmarking:
Test Results
For the CDF plots below, curves closer to the upper-left corner indicate better performance.
One-stage path
Two-stage path
Under the selection thresholds from #3458 (one-stage for m <= 32 at TP4 and m <= 16 at TP8), the latency-critical small-m decode shapes are served by the one-stage path, which this PR speeds up. The shapes where the two-stage path regresses largely fall in that one-stage range, so the practical impact is minimal; on the larger shapes that do use the two-stage path the change is a 2.5% net gain.
This PR does not change the 1stage/2stage selection heuristic, so the threshold from #3458 remains applicable.