Repository navigation
[inductor] Proven int32 size arguments for the dynamic-concat kernel in #189940 (opt-in, for review) - #198733
[inductor] Proven int32 size arguments for the dynamic-concat kernel in #189940 (opt-in, for review)#198733scasella wants to merge 3 commits into
Conversation
…kernel (pytorch#189940) Adds config.triton.narrow_proven_size_args (default off). When on, a Triton pointwise kernel's ks* size arguments are declared i32 instead of i64 only if the kernel is in a proven class (currently the 6-segment dynamic concat from pytorch#189940, recognised by a canonical-IR digest) and Inductor's symbolic state establishes the premises (ks0 = sum of widths, numel = rows * ks0, lower bounds >= 1, the int32 numel guard installed). Every other kernel is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/198733
Note: Links to docs will display an error until the docs builds have been completed. ❌ 1 New Failure, 9 Unrelated FailuresAs of commit 293565c with merge base ef166fb ( NEW FAILURE - The following job has failed:
NEW FAILURES SUPPRESSED BY AI (non-blocking) - The following jobs failed, but the AI CI Advisor judged the failures not to be evidence against this change:
UNSTABLE - The following jobs are marked as unstable, possibly due to flakiness on trunk:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
|
|
|
The following ciflow label(s) have been added but CI has not been triggered yet because the workflows are awaiting approval:
Once a maintainer approves the workflows (scroll to the bottom of the PR page), the corresponding CI jobs will be triggered automatically. Please ping one of the reviewers if you do not have access to approve and run workflows. |
…ange) Applies lintrunner fixes (PYFMT, SET_LINTER) and resolves pyrefly errors in triton_size_arg_narrowing.py with annotations and typing.cast only. The test file changes are formatting only (identical AST). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The proven class is pinned to the source Inductor emits on NVIDIA CUDA; other backends may emit different code, where the rule simply does not fire. The two tests that expect recognition now use requires_cuda_and_triton and skipIfRocm; the two backend-agnostic tests keep requires_gpu. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@pytorchbot label "release notes: inductor" |
|
@claude review these changes |
|
Claude finished @jansel's task in 2m 3s —— View job Needs Discussion: the speedup looks real, but matching a SHA-256 of one generated kernel's source isn't something we can maintain in-tree. The same premises (C2) should instead be checked generically at the point where Full reviewPR Review: #198733SummaryThe PR adds an opt-in flag, Code Quality
Infrastructure
Testing
RecommendationNeeds Discussion The measured L4 speedup (1.39×) is worth chasing, but not as a per-kernel source digest behind a private flag. Please rework it as a generic, bounds-based |
|
Thank you very much for sending a PR. |
What
When
config.triton.narrow_proven_size_argsis on (default off), a Triton pointwise kernel'sks*sizearguments are declared
i32instead ofi64, but only if the kernel is in a proven class and Inductor's ownsymbolic state establishes the premises. Every other kernel is unchanged.
Supported class today: exactly the kernel Inductor emits for the nested 6-segment concatenation along a dynamic last
dimension in #189940.
The flag is a staging mechanism for review, not the proposed end state. Following the discussion in #190034 (fix the
root cause; avoid a dedicated knob), the question we want to put to reviewers is whether this proven eligibility
condition can become an automatic compiler decision at the right internal representation.
Why
signature_to_metawidens everyks*toint64, because a product or sum of symbols can overflow even when eachsymbol fits. For this kernel that makes every index operation 64-bit, including
xindex % ks0andxindex // ks0,and roughly doubles the per-element SASS.
Related work
divisibility metadata, which keeps loads scalar. They dispatch at runtime to an alignment-hinted variant and report
that dynamic latency matches static on H100. Those variants keep the
ks*argument types unchanged. This PRaddresses index width (
i64→i32) and leaves alignment alone. The two could compose, but we have not measuredthem together, and we have not measured on H100.
assume_32bit_indexing(add assume_32bit_indexing inductor config #167784; honoured in_decide_tl_dtypesince [inductor] Honor assume_32bit_indexing in _decide_tl_dtype (#194127) #194127) narrowsks*for every kernelon the user's promise. This PR narrows one proven kernel class without that promise, using facts Inductor already
holds.
Evidence (two stacks; please read them separately)
ks*: i32kernel. In a paired, pre-registered confirmation over a 14-shape dynamic sequence, it reduced warmcomplete-sequence time by 26% against the emitted kernel (median 601 → 444 ms; paired ratio 1.35×), with
non-regression shown at all 10 small shapes tested. Output was bitwise identical to eager at every shape.
mainef166fb),one L4. It applies; the four tests below pass; the 14-shape sequence is bitwise equal to eager with all seven
ksas
i32. That build emits the same kernel source byte for byte. This branch is based on ef166fb, where the twomodified files are byte-identical to the tested build.
OFF/ON on the 14-shape sequence is 1.388× (95% CI 1.378–1.408); median 592 → 427 ms. Non-regression was shown
at all 10 small shapes, with every CI lower bound ≥ 1.29. Output was bitwise equal to eager, and there were no
recompiles during measurement. The executed ON kernel was verified to be the
i32build.unrelated kernel. It runs once per compile, never per call.
Full report, including negative results and the selection history: https://github.com/scasella/veritile-narrow-cat/blob/stage15-patch-timing/bench/optimizations/inductor_narrow/REPORT.md
Premises
Implemented in
torch/_inductor/codegen/triton_size_arg_narrowing.py.proved argument list and signature; and a body whose canonical IR (indexing, masks, payload dataflow, dead
statements) has the proved SHA-256. The recognizer rejects any construct it does not model.
ks1..ks6are size symbols with avar_to_rangelower bound ≥ 1;ks0's precomputed definition equalsks1 + … + ks6;numel == s * ks0for a size symbolswith lower bound ≥ 1;numel <= 2**31 - 1is installed (or statically known).kernel is not in the class. Exercised at
numel = 2**31: bitwise identical to eager.Why this is sound
A Lean 4 proof (standard axioms only),
NarrowCat.narrow_cat_equivand companions: https://github.com/scasella/veritile-narrow-cat/blob/stage15-review/bench/optimizations/inductor_narrow/NarrowCat.lean. It models thekernel's integer semantics (int32/int64 promotion, two's-complement wrap at every operation, truncating
%and//,i32argument packing). Under the premises, for every lane of the grid, thei64andi32kernels agree on thestore mask, store address, stored value and every memory read. Companion lemmas: equal positive divisors (no new
undefined behaviour); every
ks_iin[0, 2**31)given positive widths and rows (exacti32packing); noxindexwraparound on the grid. The payload is symbolic, so no floating-point claim is made.Why not
assume_32bit_indexing. That flag is a user's promise that every symbol fits. Our boundary tests showlanes where every
ks_i < 2**31yet the two kernels disagree, becauseks1 + ks2 + ks3overflows. This rule relieson relations Inductor already knows instead.
Trusted, not proved: SymPy and the
sizevars/shape_envfacts; the recognizer's parse of the generated source(binding-tested against the IR the proof is stated over); Triton's lowering; masked-off lanes never dereferencing
their (possibly wrapped) addresses, as the
i64kernel already assumes for past-the-end addresses.Tests
test/inductor/test_triton_size_arg_narrowing.py, four GPU tests; all passed on one L4 at the pinned revision(correctness only). They are targeted tests, not a substitute for the usual CI.
test_narrows_proven_cat_kernel: flag on →ks*arei32, output bitwise equal to eager at two shapes.test_default_is_unchanged: flag off →ks*stayi64.test_other_kernel_is_unchanged: a 3-segment cat keepsi64.test_recognizer_rejects_mutations: an extra size use, a changed index and a changed cast all fail recognition.The GPU tests and the timing above ran on the first commit, 406a839. The third commit, 293565c, limits the two tests
that expect recognition to CUDA; see Limitations. The second commit, ad17abc, only applies lint fixes: formatting,
OrderedSetin place ofset, and type annotations/castfor pyrefly. The test file's AST isunchanged. We re-checked ad17abc on the pinned nightly's CPU codegen path:
i32;i64;Limitations
programs is eligible; each new class would currently need its own extraction and proof. The upstream form may
need to be a structural or range-based rule rather than a list of generated kernels. We would rather get
direction on that before building it.
temporary numbering. A benign codegen change makes the rule stop firing (the kernel keeps
i64); it does notmisfire, provided the recognizer is correct.
XPU) the emitted code may differ, and then the rule does not fire and the kernel keeps
i64. The two tests thatexpect recognition therefore run on CUDA only.
Review questions
typed indexing expressions?
later invocations?
🤖 Generated with Claude Code
cc @voznesenskym @penguinwu @EikanWang @jgong5 @Guobing-Chen @XiaobingSuper @zhuhaozhe @blzheng @wenzhe-nrv @jiayisunx @ipiszy @kadeng @muchulee8 @amjames @chauhang @aakhundov @coconutruben @jataylo