indexer: support non-power-of-two group sizes in the Triton kernels - #5
Open
GeoffreyWang1117 wants to merge 1 commit into
Open
indexer: support non-power-of-two group sizes in the Triton kernels#5GeoffreyWang1117 wants to merge 1 commit into
GeoffreyWang1117 wants to merge 1 commit into
Conversation
mm_bpr_kernel, reduce_rr_kernel and softmax_inplace_r_kernel walk the
group axis with tl.arange(0, G) where G is taken straight from the tensor
shape. Triton requires the length of a tl.arange to be a power of two, so
any model whose GQA group size (num_attention_heads / num_key_value_heads)
is not a power of two fails at kernel compile time:
triton.compiler.errors.CompilationError:
arange's range must be a power of 2
That rules out, among others, Qwen2.5-1.5B (12/2 = 6), Qwen2.5-7B
(28/4 = 7) and Llama-3.2-3B (24/8 = 3) -- the indexer never gets to run
at all on those.
Fix: round the affected extent up to the next power of two for the
tl.arange only, and mask the surplus lanes on both load and store.
Pointer arithmetic and the Mean divisor keep using the real extent, so
nothing changes for power-of-two shapes -- the mask is all-true and the
generated code is equivalent.
Padded lanes are filled with the identity element of the reduction
(-inf for Max, +inf for Min, 0 for Mean / L2Norm / Sum) so they cannot
influence the result, and they are never written back.
Also fixes the fallback branch of the DIM == 2 reduce, which allocated
its zero tile with the dim-1 extent instead of the dim-0 extent.
Verified on an RTX 3090 against a torch reference: group sizes 1..8 for
mm_bpr, all five ReduceTypes over both dims for six (D0, D1) shape pairs,
and five shapes for softmax_inplace_r -- all within bf16 tolerance, with
power-of-two shapes bit-for-bit unchanged from before. Repro script in
the PR description.
Not covered here, to keep the diff reviewable: mm_rrr / mm_rpr in the
same file and the kernels under vortex_torch/cache/triton_kernels/ use
the same tl.arange-on-raw-shape pattern. Happy to extend this PR or send
a follow-up, whichever you prefer.
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.
Problem
Three indexer kernels walk the group axis with
tl.arangesized directly from the tensor shape:matmul_impl.pyg_ptr = tl.arange(0, G)reduce_impl.pydim0 = tl.arange(0, x_D0)/dim1 = tl.arange(0, x_D1)softmax_impl.pyd1_idx = tl.arange(0, x_D1)Triton requires the length of a
tl.arangeto be a power of two.Ghere is the GQA group size (num_qo_heads // num_kv_heads— seeflow.py::run_indexer_virtual, which buildsq_dummyas[1, group_size, head_dim]). So for any model whose group size is not a power of two, the indexer fails at kernel compile time and never runs at all:Models this rules out (numbers read from the published HF configs):
The repo's own example (
examples/verify_algo.py, Qwen3-1.7B) has G=2, so this doesn't surface until you point it at a different model.Change
Round the affected extent up to the next power of two for the
tl.arangeonly, and mask the surplus lanes on load and store. Pointer arithmetic and theMeandivisor keep using the real extent.For power-of-two shapes the mask is all-true and the generated code is equivalent — nothing changes on the currently-supported path.
Padded lanes are filled with the identity element of the reduction (
-infforMax,+infforMin,0forMean/L2Norm/Sum) so they cannot influence the result, and they are never written back.next_pow2lives in the (previously empty)indexer/triton_kernels/utils_impl.pyso the three kernels share one definition.Also fixes the fallback branch of the
DIM == 2reduce, which allocated its zero tile with the dim-1 extent instead of the dim-0 extent.Verification
RTX 3090, torch 2.9.1, triton 3.5.1. The reproducer below covers
mm_bprfor group sizes 1–8,reduce_rrfor all fiveReduceTypes over both dims across six(D0, D1)pairs, andsoftmax_inplace_racross five shapes — each against a torch reference. 73 checks total.On
v1today — dies at the first non-power-of-two case:With this PR — 73/73 pass, all within bf16 tolerance:
Power-of-two shapes are bit-for-bit unchanged from before the patch.
Reproducer script
tests/is in.gitignore, so I kept this out of the tree rather than change your ignore rules. Happy to check it in as a real test if you'd like — just say where.Scope
Deliberately not covered here, to keep the diff reviewable:
mm_rrr/mm_rprin the same file (six shape constexprs each) use the same pattern.vortex_torch/cache/triton_kernels/do too, though their dims are page/head layout rather than the GQA group, so they're less likely to be hit in practice.Happy to extend this PR to cover those, or send a follow-up — whichever you prefer.