Add a partial split to split_reduce for large reductions - #5219
Open
pfultz2 wants to merge 12 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR enhances MIGraphX’s GPU split_reduce strategy by introducing a two-stage “partial reduction” split for large reductions that previously could not be split (or could only be split via atomics), improving occupancy and enabling better fusion behavior for trailing pointwise ops.
Changes:
- Implement a partial split path in
split_reducethat reshapes inputs to create grouped partial reductions, then completes with a second reduction. - Derive split heuristics from GPU device properties (resident workgroups and last-level cache size), including a new HSA cache-size query with HIP fallback.
- Extend
fused_reduce::compute_shapeto support multi-output submodules (tuple output) and add/expand unit tests covering decision paths and multi-output behavior.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/split_reduce.cpp | Expands unit tests to cover partial vs atomic selection, batch/size heuristics, trailing-op fusion/inlining, and multi-output behavior. |
| src/targets/gpu/target.cpp | Computes device-derived fuse_pointwise_reduce thresholds (resident workgroups, cache-derived split size). |
| src/targets/gpu/include/migraphx/gpu/hsa_chiplet.hpp | Adds exported API to query last-level cache size via HSA. |
| src/targets/gpu/include/migraphx/gpu/device_description.hpp | Adds last_level_cache_size to the GPU device description. |
| src/targets/gpu/include/migraphx/gpu/context.hpp | Exposes get_max_workgroups() and get_last_level_cache_size() on hip_device. |
| src/targets/gpu/hsa_chiplet.cpp | Implements cached HSA queries for chiplet count and last-level cache size (with Windows stub). |
| src/targets/gpu/device_description.cpp | Populates last_level_cache_size from HSA, falling back to HIP device properties. |
| src/split_reduce.cpp | Adds partial split implementation, selection heuristics, and updated split/inline plumbing for trailing modules. |
| src/include/migraphx/split_reduce.hpp | Documents new knobs/heuristics for split_reduce. |
| src/include/migraphx/fuse_pointwise_reduce.hpp | Plumbs new split-reduce knobs through fuse_pointwise_reduce. |
| src/fuse_reduce.cpp | Updates fused_reduce::compute_shape to support multi-output submodules (tuple shape). |
| src/fuse_pointwise_reduce.cpp | Wires new split_reduce parameters into the fuse_pointwise_reduce pass pipeline. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+42
to
+46
| /// with another reduction over the groups. For reductions larger than the | ||
| /// split_size, the atomic-based split_fused_reduce can be used instead, | ||
| /// which splits any elementwise operators into separate operators as well | ||
| /// due to needing global synchronization. When both thresholds are | ||
| /// applicable, prefer_partial_reduce selects which one is used. |
Comment on lines
+60
to
+62
| result.last_level_cache_size = get_hsa_last_level_cache_size(device); | ||
| if(result.last_level_cache_size == 0) | ||
| result.last_level_cache_size = std::max(props.l2CacheSize, 0); |
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
The
split_reducepass could only split large reductions using the atomic-basedsplit_fused_reduce, which is limited toreduce_sumon float/half (atomic max/min/prodon other types are not supported), and which forces any surrounding elementwise operators
into separate kernels since the atomic assignment needs global synchronization.
Large reductions of other types (
reduce_max,reduce_mean, etc.) were therefore neversplit at all, running as a single workgroup per output. On small batches this leaves most
of the GPU idle, and once the row no longer fits in the last-level cache (or the register
limits force the
block_largefallback), a single workgroup cannot stream the reductionefficiently regardless of the batch.
Technical Details
Partial reduction. For an eligible
fused_reduce, the reduce axis is split intogroups by reshaping the inputs (
{M, N}becomes{M, G, N/G}). A firstfused_reducecomputes a partial reduction over the contiguous
N/Gelements of each group — so readsstay coalesced — and a second
fused_reducecompletes it by reducing over theGgroups.This works for any
reduce_*operator whose result can be completed by a second reductionof the same kind (argmin/argmax are excluded), on any data type. The group axis is chosen
per reduce axis with
split_dim, preferring the innermost axis on ties, and scaled so theremaining reduction drops below
lower_split_size.Trailing operators. When there are enough reduction outputs to stream the full-sized
result (
min_fused_outputs, an eighth of the resident workgroups), the trailing pointwiseoperators are fused into the completion kernel to avoid another launch. With fewer
outputs, the completion kernel would starve the device writing the full-sized output, so
the reduction is completed alone (the subwave algorithm packs several small reductions per
wavefront) and the trailing operators are inlined into the parent module as a fully
parallel pointwise kernel.
Heuristics. New
split_reduceknobs control when each strategy applies:lower_split_size— threshold to use the partial reduction when the batch is belowlower_max_batch.upper_split_size— beyond this the reduction is too large for a single workgroup(the resident rows overflow the last-level cache), so a split happens regardless of
the batch.
lower_max_batch— below the upper threshold, a batch at least this large already hasenough parallelism with one workgroup per output, so no split is done.
prefer_partial_reduce— when both the atomic and partial thresholds are met, selectswhich is used. Launch-bound tensors (not enough total work for
lower_max_batchworkgroups of
lower_split_sizeelements) still prefer the single-kernel atomic split.Hardware-derived constraints. The GPU target now computes these thresholds from the
device instead of using fixed defaults:
lower_max_batchfrom the number of residentworkgroups (
get_max_workgroups), andupper_split_sizefrom the last-level cache sizedivided across the resident rows. The last-level cache size is queried via HSA
(
HSA_AGENT_INFO_CACHE_SIZE, taking the last non-zero level) alongside the existingchiplet-count query, falling back to HIP's
l2CacheSizewhen HSA is unavailable.Other changes.
fused_reduce::compute_shapenow supports submodules with multiple outputs, returninga tuple shape.
test/split_reduce.cppcover each decision path: partial vs. atomicselection, multi-axis splits, launch-bound fallbacks, mandatory splits beyond
upper_split_size, trailing-operator fusion vs. inlining, and unsplittable groupfactors falling back to the atomic split.
test_split_reduce_maxandtest_reduce_multi_outcheck numericalresults for a split
reduce_maxand a multi-output fused reduction.Changelog Category
Add a
CHANGELOG.mdentry for any option other thanNot ApplicableFollow the LLVM AI Tool Use Policy for contributions using AI.