Skip to content

Shrink-wrap block-level GPU register allocations - #9343

Open
abadams wants to merge 5 commits into
mainfrom
abadams/gpu_register_sgemm
Open

Shrink-wrap block-level GPU register allocations#9343
abadams wants to merge 5 commits into
mainfrom
abadams/gpu_register_sgemm

Conversation

@abadams

@abadams abadams commented Aug 14, 2026

Copy link
Copy Markdown
Member

#9325 checks that block-level register storage is safe, but it still duplicates that storage per-thread. This new pass, which runs later, decomposes that into a set of actual ptx registers for each thread to use by identifying which subtiles of the block-level allocation are actually used by each thread. Without this, each thread writes to a dynamic thread-id-dependent index into the allocation, resulting in it spilling to local instead of truly being a register. There doesn't seem to be a good way to merge this with the earlier PR's pass, unfortunately. That one checks legality, and then this one is an optimization that exploits the legality. The need to run at different times.

This is the missing ingredient required to hit >90% of cublas performance in the cuda mat mul app for float32 (i.e. cuda cores, not tensor cores). #9283 hits similar performance levels for narrower float mat muls.

@alexreinking alexreinking left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's see if we can get Claude to lean more on the lambda mutators

Comment thread src/PromoteGPURegisters.cpp Outdated
Comment thread src/PromoteGPURegisters.cpp Outdated
@codecov

codecov Bot commented Aug 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.85714% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.13%. Comparing base (bb5426d) to head (b49398d).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/PromoteGPURegisters.cpp 93.82% 0 Missing and 5 partials ⚠️
src/Lower.cpp 66.66% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #9343      +/-   ##
==========================================
+ Coverage   70.08%   70.13%   +0.05%     
==========================================
  Files         259      261       +2     
  Lines       79158    79371     +213     
  Branches    19293    19343      +50     
==========================================
+ Hits        55477    55667     +190     
- Misses      17886    17900      +14     
- Partials     5795     5804       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 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.

@abadams
abadams force-pushed the abadams/gpu_register_sgemm branch from 0ba4a36 to 8861d3b Compare August 16, 2026 22:21
Comment thread test/error/gpu_register_dynamic_index.cpp
Base automatically changed from abadams/gpu_register_crosstalk_check to main August 20, 2026 17:41
abadams and others added 5 commits August 20, 2026 10:41
An allocation in MemoryType::Register outside the loops over GPU threads is
storage private to a thread, so what looks like one allocation of many elements
is really a handful of registers held by each thread. The cross-talk check
established that each thread keeps to its own part; this shrinks the allocation
to just that part.

Two accesses by one thread are to the same elements when the distance between
them is the same whatever thread it is, because the thread cancels when only
comparing accesses made by the same one. That is the question get_subtile
already answers for tile memory, so ask it: group the accesses into sets that
are each identical or disjoint, reject a partial overlap, and give each set
registers of its own. Nothing about how an access covers its elements matters,
because the registers a set gets are its own, so a dense ramp reaches them all.

A thread that indexes its own storage dynamically has no fixed register to use
and gets a user error saying so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The accumulator now lives at block level in registers, which puts the loop over
the reduction above the loop over threads, so one staged panel of each input
serves every thread in the block. The panels are copied from global to shared
asynchronously, laid over the same grid of threads as the compute so that no
thread sits idle in either phase.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Staging the inputs with asynchronous copies needs compute capability 8.0,
which the app's Makefile asks for but its CMake build did not, so the
generator refused to compile it. Raise the guard in the runner to match,
so that the test skips on an older GPU rather than failing on one.

Also report an error when the dynamic index test skips, since an error
test that returns cleanly reads as a failure to the Makefile's harness.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@abadams
abadams force-pushed the abadams/gpu_register_sgemm branch from d8492ff to b49398d Compare August 20, 2026 17:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants