Skip to content

feat(rocke): resolve gfx942 num_sms from the device for up to 14x mid-batch decode - #9739

Merged
yraparti merged 13 commits into
developfrom
users/avirgoel/ck/attention-num-sms-resolver
Aug 4, 2026
Merged

yraparti merged 13 commits into
developfrom
users/avirgoel/ck/attention-num-sms-resolver

Conversation

@AviralGoelAMD

@AviralGoelAMD AviralGoelAMD commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

What was happening before (plain language)

Attention dispatch has a knob called num_sms — it tells the dispatcher how many compute units (CUs) the GPU has. That number decides two things:

  1. Which kernel to run — a serial 2D kernel, or a 3D split‑KV kernel that splits work across the whole GPU.
  2. How finely the 3D kernel splits the KV cache.

num_sms was hard‑coded to a stale 120, but an MI300X (gfx942) has 304 CUs. So the dispatcher thought the GPU was less than half its real size, and medium/large‑batch decode that should have been split across the device stayed stuck in the slow 2D kernel, leaving most of the GPU idle. Long‑context decode also under‑split.

What we changed

The num_sms device resolver, a direct target_ctas knob, and a segment-count bound:

  1. Resolve num_sms from the real device — only when verified on-box. For a gfx942 request built on a gfx942 box, read the live CU count (304 MI300X / 228 MI300A) → the reroute win. Everywhere else — non-gfx942 arches, or a gfx942 kernel built off-box / with no visible gfx942 device — keep the legacy 120 (matches develop), so a cross-compile never bakes the wrong device's count into the kernel. (Per review: the earlier hardcoded 304 off-box fallback was removed — 304 now comes only from the live query — and the ROCKE_NUM_SMS env pin was removed; pin reproducibility via an explicit num_sms or the target_ctas knob.)
  2. Add a direct target_ctas spec knob. A single device-subscription target (concurrent workgroups) that drives 2D↔3D routing and 3D segmentation. Default 0 → num_sms * 4 (byte-identical to before); set >0 to pin the target directly, so tuners / benchmarks / hipDNN can select the fast path without a device CU count. Consolidated behind one accessor (_effective_target_ctas) so routing and segmentation share one source of truth.
  3. Bound the split‑KV segment count. Raising num_sms alone made the 3D kernel over‑split already‑3D shapes; we clamp the segment count to the pre‑bump baseline where it over‑splits (gfx942 D128 decode ≤16384, and all q>1), leaving the cases that genuinely benefit (D128 kv≥32768, D64, D256) uncapped.

How it acts as an optimization

  • The true CU count lets under‑filled decode grids reroute 2D → 3D, filling all 304 CUs → large speedups.
  • The clamp keeps that win without an over‑split penalty on already‑3D shapes.
  • Correctness is untouched — num_sms changes only launch geometry, never math. Every result below was fp32‑gated.

Benchmarks — GPU: AMD Instinct MI300X (gfx942, 304 CUs), bf16/fp16, before/after = num_sms 120 → 304, fp32‑gated

Measurement provenance (please read): all on‑device numbers were measured on the #9391 (users/arai/attention_sink_decode) branch base, where this work was developed; this PR is now rebased onto develop. Practical implications for a reviewer on develop:

Headline: mid‑batch decode flips — Llama / Qwen (develop‑reproducible; child PR #9742 shapes)

These were stuck in the slow 2D kernel before; the resolver reroutes them to 3D:

shape config speedup
llama8b_dec_b80_kv8192 32/8 D128 14.56×
qwen235b_dec_b100_kv8192 64/4 D128 13.49×
llama8b_dec_b80_kv2048 32/8 D128 13.49×
llama405b_dec_b50_kv8192 128/8 D128 13.47×
llama405b_dec_b50_kv2048 128/8 D128 11.80×
qwen235b_dec_b100_kv2048 64/4 D128 11.74×
llama70b_dec_b67_kv8192 64/8 D128 11.20×
llama70b_dec_b67_kv2048 64/8 D128 10.25×
qwen30b_dec_b133_kv8192 32/4 D64 2.65×
qwen30b_dec_b133_kv2048 32/4 D64 2.59×

Mechanism (profiled on MI300X, llama8b_dec_b80_kv8192, distinct block_table):

num_sms path grid occ/CU VALUBusy HBM/launch achieved BW GPU time
120 2D 512 0.70 0.8% 2573 MiB 0.16 TB/s (3% of 5.3 peak) 17357 µs
304 3D (seg16) 6400 2.73 13.2% 2612 MiB 2.31 TB/s (44% of peak) 1187 µs

The 14.6x is pure device-fill, not fewer bytes (~2.6 GiB moved either way): the 2D grid of 512 leaves the 304 CUs almost idle (occ 0.70, HBM 3% utilized); the reroute to 3D fills the device (grid 6400, occ 2.73, HBM 44%). seg16 confirms the segment clamp is active (not the naive 128). rocprofv3, one representative mid-batch D128 shape — mechanism measured, not inferred.

Prefill / spec‑decode flips (q>1, causal‑fp32‑gated; Llama/Qwen, develop‑reproducible)

shape q config speedup
qwen30b_q4_b67 4 32/4 D64 2.06×
llama405b_q2_b33 2 128/8 D128 1.95×
qwen235b_q2_b67 2 64/4 D128 1.93×
llama70b_q4_b33 4 64/8 D128 1.92×
llama8b_q2_b67 2 32/8 D128 1.82×
qwen235b_q4_b40 4 64/4 D128 1.70×
llama405b_q4_b20 4 128/8 D128 1.68×
qwen30b_q2_b100 2 32/4 D64 1.61×
llama8b_q4_b50 4 32/8 D128 1.44×
llama70b_q2_b50 2 64/8 D128 1.39×

Existing corpus — 38 affected decode shapes (batch 1–2)

Long‑kv / D64 re‑splits (Llama/Qwen, develop‑reproducible):

shape speedup
decode_kv32768 (Qwen‑30B D64) 1.26×
decode_kv16384 (Qwen‑30B D64) 1.21×
llama405b_b1_kv32768 (D128) 1.18×
llama405b_fp16_b1_kv32768 1.06×
decode_kv4096 / kv8192 (D64) 1.01× / 1.00×
llama8b/70b/qwen235b_b1_kv32768 (bf16+fp16, ×6) 0.97–0.99× (noise)

Neutralized by the clamp (kernel byte‑identical → flat, no regression) — 24 shapes: Llama‑8B/70B/405B & Qwen‑235B, D128, kv4096/8192/16384 (bf16+fp16), all 0.97–1.08× (noise on identical kernels).

gpt‑oss sink flips — REQUIRES #9391 (not on develop), measured on the #9391 base:

shape speedup
full_sink_b64_kv2048 1.94×
full_sink_b64_kv8192 1.93×

Regressions

  • None after the fix across 38 corpus + 28 mid‑batch/prefill = 66 shapes on MI300X — zero real regressions, zero correctness failures.
  • The naive version (bump num_sms without the clamp) regressed 21 of 38 shapes, worst 0.50× (2× slower) — exactly the over‑split the clamp fixes.
  • The 0.97–0.99× readings are measurement noise (±~3%): they sit on byte‑identical (clamped) kernels, bf16/fp16 twins disagree run‑to‑run, and a 3× re‑run of the suspect shape read 1.010 / 1.010 / 1.013×.

Tests

Adds tests/dispatch/attention/test_num_sms_resolver.py — 9 CPU tests: resolution order (explicit / gfx942 device / MI300A=228 / other-arch=120), routing scales with num_sms, segments stay bounded, and target_ctas precedence (overrides num_sms*4, flips 2D→3D at fixed num_sms, threads through _problem). Re-verified on MI300X: byte-identity GREEN (66 families), installed-golden PASS, and a same-node A/B confirming the target_ctas path is perf-neutral vs pre-change (0 routing changes, 0.11% median timing diff, mid-batch win intact); the knob validated on silicon (2D→3D at fixed num_sms, output matches the 2D default, 14.1×).

Scope / notes

  • Validated on MI300X via the Python dispatch path. Auto-resolution is gfx942-only (other archs keep 120 — future scope).
  • No environment variables: the ROCKE_NUM_SMS pin was removed per review; pin reproducibility via an explicit num_sms or the target_ctas knob.
  • The production C++ C-ABI engine hardcodes num_sms=120 and has no target_ctas field today (routing decided in Python); mirroring the device resolver + target_ctas there is tracked as a companion follow-up (AICK-1856).
  • The split-KV clamp ceiling (_PRE_BUMP_SMS=120) is kept hardcoded here; parametrizing it as a spec knob (reviewer request) is tracked as AICK-1857.
  • Rebased onto develop; the mid/large‑batch coverage is extended by child PR test(rocke/attention): representative batch-sweep decode shapes for gfx942 #9742.

Changes are behind the feature flag HIPKERNELPROVIDER_ENABLE_ROCKE which is defaulted to OFF.

@AviralGoelAMD
AviralGoelAMD requested review from a team as code owners July 22, 2026 23:11
@AviralGoelAMD
AviralGoelAMD changed the base branch from users/arai/attention_sink_decode to develop July 22, 2026 23:15
@AviralGoelAMD
AviralGoelAMD changed the base branch from develop to users/arai/attention_sink_decode July 22, 2026 23:16
@AviralGoelAMD
AviralGoelAMD marked this pull request as draft July 22, 2026 23:16
@AviralGoelAMD
AviralGoelAMD changed the base branch from users/arai/attention_sink_decode to develop July 22, 2026 23:17
@AviralGoelAMD

Copy link
Copy Markdown
Contributor Author

waiting for #9391 to be merged

@therock-pr-bot

therock-pr-bot Bot commented Jul 22, 2026 •

Copy link
Copy Markdown

✅ All Checks Passed — Ready for Review

Check Status Details
📝 PR Title/Description ✅ Pass —
⛔ Forbidden Files ✅ Pass —
🧪 Unit Test ✅ Pass —
🔎 pre-commit ✅ Pass —
🚫 Draft PR 🔜 To Be Enabled —
🚩 Feature Flag 🔜 To Be Enabled —
📊 Code Coverage 🔜 To Be Enabled —
🤖 therock-pr-bot ✅ Pass —

🎉 All checks passed! This PR is ready for review.

📖 Need help? See the Policy FAQ for details on every check and how to fix failures.

@AviralGoelAMD
AviralGoelAMD changed the base branch from develop to users/arai/attention_sink_decode July 22, 2026 23:23
@AviralGoelAMD
AviralGoelAMD force-pushed the users/avirgoel/ck/attention-num-sms-resolver branch from ca45b15 to 9443b1e Compare July 22, 2026 23:28
@AviralGoelAMD
AviralGoelAMD changed the base branch from users/arai/attention_sink_decode to develop July 22, 2026 23:29
@AviralGoelAMD
AviralGoelAMD marked this pull request as ready for review July 22, 2026 23:38
@AviralGoelAMD AviralGoelAMD changed the title feat(rocke): resolve gfx942 attention num_sms from device + bound split-KV segments feat(rocke): resolve gfx942 num_sms from the device for up to 14x mid-batch decode — with bounded split-KV segments and zero regressions Jul 22, 2026
@AviralGoelAMD AviralGoelAMD changed the title feat(rocke): resolve gfx942 num_sms from the device for up to 14x mid-batch decode — with bounded split-KV segments and zero regressions feat(rocke): resolve gfx942 num_sms from the device for up to 14x mid-batch decode Jul 22, 2026
…it-KV segments

num_sms (the split-KV device-subscription target driving 2D<->3D routing and the
3D segment count) defaulted to a stale 120, under-subscribing gfx942 (MI300X=304
CUs): mid-batch decode stayed in slow 2D and long-kv decode under-split.

Dispatch: resolve num_sms from the live device CU count for gfx942 requests
(AttentionRequest.num_sms default 120->0 sentinel), behind an override seam
(ROCKE_NUM_SMS env > explicit value > device query > 304 fallback). Arch-guarded
so a gfx942 request on another box falls back to the constant. Other archs keep
the legacy 120 (future scope).

Segments: bound _num_segments so the higher CU-count target cannot over-split
already-3D shapes -- clamp to the pre-bump (num_sms=120) per-shape baseline for
gfx942 D128 decode at kv<=16384 and for all q>1 (prefill/spec-decode); D128
kv>=32768, D64, and D256 stay uncapped where the finer split is measured to win.

Validated on MI300X (fp32-gated, before/after 120->304): decode flips win
2.6-14.6x, q>1 flips 1.4-2.1x, long-kv/D64 re-splits 1.06-1.26x, and the
naive-bump regressions (down to 0.50x) are eliminated -- zero regressions across
38 decode + 28 flip/prefill shapes.

Adds tests/dispatch/attention/test_num_sms_resolver.py (CPU: resolution order +
routing/segment effects).
@AviralGoelAMD
AviralGoelAMD force-pushed the users/avirgoel/ck/attention-num-sms-resolver branch from 9443b1e to e9cff18 Compare July 23, 2026 00:39
@therock-pr-bot

Copy link
Copy Markdown

Pre-commit check failed

⛔ pre-commit failed

Please run locally:

  • python -m pip install pre-commit
  • pre-commit install
  • pre-commit run --all-files --show-diff-on-failure

This repo uses .pre-commit-config.yaml.

@codecov

codecov Bot commented Jul 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

❌ Your project check has failed because the head coverage (76.84%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #9739      +/-   ##
===========================================
- Coverage    64.28%   64.12%   -0.16%     
===========================================
  Files         2764     2765       +1     
  Lines       450621   450696      +75     
  Branches     66317    66320       +3     
===========================================
- Hits        289656   288987     -669     
- Misses      140161   140917     +756     
+ Partials     20804    20792      -12     
Flag Coverage Δ *Carryforward flag
TensileLite 34.35% <ø> (ø) Carriedforward from 23678b8
TensileLite-CPP 38.31% <ø> (ø)
TensileLite-Unit 35.57% <ø> (ø)
hipBLAS 90.62% <ø> (ø) Carriedforward from 23678b8
hipBLASLt 34.63% <ø> (ø)
hipCUB 82.68% <ø> (ø) Carriedforward from 23678b8
hipDNN 86.34% <ø> (ø) Carriedforward from 23678b8
hipFFT 48.82% <ø> (+2.22%) ⬆️
hipRAND 76.12% <ø> (ø) Carriedforward from 23678b8
hipSOLVER 69.18% <ø> (ø) Carriedforward from 23678b8
hipSPARSE 86.27% <ø> (ø) Carriedforward from 23678b8
rocBLAS 47.95% <ø> (ø)
rocFFT 46.54% <ø> (-2.49%) ⬇️
rocRAND 57.01% <ø> (ø) Carriedforward from 23678b8
rocSOLVER 76.84% <ø> (ø) Carriedforward from 23678b8
rocSPARSE 72.49% <ø> (ø) Carriedforward from 23678b8
rocThrust 91.36% <ø> (ø) Carriedforward from 23678b8

*This pull request uses carry forward flags. Click here to find out more.
see 50 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@malcolmroberts

Copy link
Copy Markdown
Contributor

Is there any reason that this is flagged rocfft?

…s accessor

Consolidate the two inlined num_sms*4 routing-target sites (select_path
2D<->3D decision and select_3d segment count) into a single
_effective_target_ctas accessor on UnifiedAttentionProblem. Byte-identical
no-op: same value flows to both call sites, so routing, segment counts, and
emitted IR are unchanged. The _num_segments over-split clamp is untouched.

Sets up a single source of truth for the device-subscription target ahead of
adding a direct target_ctas spec knob.
Add a direct device-subscription target override (target_ctas) to
UnifiedAttentionProblem and AttentionRequest. When > 0 it replaces num_sms*4
as the routing/segmentation target via the _effective_target_ctas accessor,
letting tuners/benchmarks pin the target without a device CU count. Default 0
preserves num_sms*4 exactly (byte-identical routing, segments, and emitted IR).

Remove the ROCKE_NUM_SMS environment pin from _resolve_num_cus (and the now
-unused os import); reproducibility comes from an explicit num_sms or the
target_ctas spec override. Resolver order is explicit num_sms -> gfx942 live
device CU count -> 304/120 fallback. The split-KV segment clamp is unchanged.

Tests: drop the env-tier cases, add target_ctas precedence, routing-flip, and
_problem threading coverage.
…ency

Document that target_ctas, like num_sms, is honoured on the Python dispatch
path only. The C++ C-ABI engine (attention_unified_entry.cpp) hardcodes
num_sms=120 and has no target_ctas field, so both need the mirror resolver +
target_ctas there to take effect in C-ABI/hipDNN production (companion change).
The default path (target_ctas=0 -> num_sms*4) stays byte-identical, so nothing
breaks today; this only clarifies the production-path dependency.
…cy 120

A gfx942 request that could not verify the build box was gfx942 previously fell
back to a hardcoded 304 (the MI300X CU count). That baked the wrong device's
count into cross-compiled / off-box kernels (e.g. a gfx942 target that is
actually MI300A, 228 CUs -> over-subscription) and re-introduced a hardcoded
per-device CU literal.

Now the live device CU count is used ONLY when the request targets gfx942 AND
the build box IS gfx942. Every other case -- non-gfx942 arches, gfx942 built
off-box, or no visible gfx942 device -- returns the legacy 120, matching
develop. The _GFX942_NUM_CUS=304 literal is removed (304 now comes only from
the live query). An explicit num_sms / target_ctas still supersedes.

Shrinks the deviation from develop to the single verified-on-box case, avoids
over-subscribing same-arch SKUs, and drops the hardcoded CU count. Byte-identity
unaffected (dispatch-only; goldens pin num_segments). Off-box test now asserts
120 (was 304).
@AviralGoelAMD

Copy link
Copy Markdown
Contributor Author

Thank you for nudging me into a better direction via your review comment with a better design I have incorporated that into my PR and simplified things now.

…ents

Replace the SKU marketing names in the two comments this PR owns with the
arch (gfx942): the num_sms resolver docstring ("MI300X 304 vs MI300A 228" ->
"varies across parts") and the split-KV segment clamp comment ("measured on
MI300X" -> "measured on gfx942"). Comment-only; no logic/IR change.

Scoped to the lines this PR touches; the broader repo-wide SKU->gfx scrub is a
separate follow-up.
@yraparti
yraparti merged commit 5b12834 into develop Aug 4, 2026
36 checks passed
@yraparti
yraparti deleted the users/avirgoel/ck/attention-num-sms-resolver branch August 4, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants