Repository navigation
perf(indexed_attention): query-tiled QSA selection scores + deterministic radix select - #23
Merged
Merged
Conversation
…erministic radix select
faresobeid
marked this pull request as ready for review
October 26, 2026 11:37
samsja
approved these changes
Oct 26, 2026
samsja
pushed a commit
to PrimeIntellect-ai/prime-rl
that referenced
this pull request
Oct 26, 2026
…on plan under AC (#4279) Bumps prime-kernels from `v0.1.0-3101960` to `v0.1.0-b52bd9d` (wheel URLs, `deps/prime-kernels` submodule, `uv.lock`). Also adds `prime_kernels::indexed_attention_plan` to `MANDATORY_SAVE_OPERATIONS`, so full AC does not rebuild the plan in the recompute. ## What the new kernels bring (Qwen3.8-Flash-Next QSA; API unchanged) - PrimeIntellect-ai/prime-kernels#22: `indexed_attention` as windowed GQA Triton kernels. A new plan op groups 5 queries per window. 1.2-3.7x per QSA layer fwd+bwd on H200 (16K: 116 -> 31 ms, 64K: 352 -> 191 ms, CP8 rank 16K x 131K: 84 -> 65 ms) and lower peak memory. The dK/dV backward is key-major with no atomics, so it is bitwise deterministic. - PrimeIntellect-ai/prime-kernels#23: `select_indexed_blocks` uses query-tiled scores and a radix select without atomics. 3.8-4.4x on selection (64K: 32 -> 7.2 ms, CP8 at 1M: 2186 -> 516 ms). Scores and selected sets are bitwise equal to before, and the output order is now deterministic (ascending). - The release also carries the DeepSelect wheel (#17), used by #3958; this PR does not pin it. ## Plan save under full AC The plan depends only on the indices and syncs to host for its sizes. Without the save it is built twice per step (forward and recompute). Profiled step (4-layer Qwen3.8, one QSA layer, full AC): `_window_keys_kernel` launches 4 -> 2, so the plan is built once. ## Numbers (1 node, 8x H200, FSDP, 4-layer Qwen3.8 `q38-4l-small`, full AC, SGD; median step after 2 warmup steps, 8 steps) | tokens / rank | main (3101960) | new kernels, plan not saved | this PR | |---|---|---|---| | 16K | 1.087 s | 0.908 s | 0.904 s | | 64K | 3.754 s | 3.613 s | 3.593 s | - Only 1 of the 4 layers is QSA, so the full-model gain is smaller than the per-layer gain. Saving the plan saves about 20 ms per QSA layer at 64K. - Loss per step matches main to about 1e-4 relative, the same run-to-run noise main shows. The two new-kernel arms give bitwise-equal losses over all 8 steps. ## Validation - H200 with the release wheel: `test_qwen3_8_flash_next.py` + `test_checkpointing.py` (16 passed). prime-kernels `smoke_test.py prime-kernels` OK, prime-kernels `tests/` (6 passed). - `pytest -m "not gpu" tests/unit/orchestrator tests/unit/test_configs.py tests/unit/train` (620 passed), `ruff check`, `ruff format --check`, `uv lock --check` (uv 0.13.5). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches training correctness under full activation checkpointing and swaps prebuilt GPU kernel wheels; mis-saving the plan could change numerics or performance on QSA paths. > > **Overview** > Bumps **prime-kernels** from `v0.1.0-3101960` to **`v0.1.0-b52bd9d`** on Linux x86_64 and aarch64 (`pyproject.toml` wheel URLs and matching **`uv.lock`** hashes). > > Adds **`prime_kernels::indexed_attention_plan`** to **`MANDATORY_SAVE_OPERATIONS`** in activation checkpointing so **full AC** keeps the indexed-attention plan across forward and recompute instead of rebuilding it (paired with the new kernels’ plan op). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 96d8822. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
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.
Speeds up
select_indexed_blocks(Qwen3.8-Flash-Next QSA selection) by ~4x at real shapes. The selection stays the same: the scores are bitwise equal to main's, and so is the set of selected blocks, ties included. The output order is now deterministic (ascending block index).Problem
selection_score_kernelran one query per CTA. The 4 indexer heads were padded to 16 MMA rows, and every query re-read all of its visible keys (~550 GB of key reads at 131K).radix_select_kernelwrote picks withatomic_add, so the output order changed from run to run. TileLang also put two__syncthreads()around every histogram atomic, becausedigitwas a sharedalloc_var. A single thread scanned the 256 bins serially, and partial ties at the threshold were resolved by a single thread looping over every column.Change
(relu(h0)+relu(h1)) + (relu(h2)+relu(h3)), the same order main'sreduce_sumused, so the scores are bitwise equal.score_workspace, allocated once per call and reused for every chunk).T.cumsum.num_blocks.select_indexed_blocksis unchanged.compute_selection_scoresreturns the same thing (a view into the padded buffer).Numbers
H200. Single-document micro-batch. Indexer inputs from prime-rl's
IndexedGatedAttentionwith random weights on locally correlated hidden states, the same setup as #22. "Layer" is one QSA attention module fwd+bwd, no AC. CP rows emulate the last CP rank on one GPU (keys and KV of the other ranks are precomputed; communication is not included).Per 1 GiB chunk at CP8 16K x 131K (8192 rows):
Validation
torchreference (stable sort, lowest index first among ties), and repeat equality. All equal. The cases cover odd block counts (37, 1021, 1023), up to 5 documents, query counts that are not a multiple of 16, heavy ties (zeroed and duplicated keys), and topk 64 and 512.IndexedGatedAttention(20K tokens, 2 documents): sorted indexer indices, the attention output, d(hidden) and d(q_proj.weight) are bitwise equal to main (feat(indexed_attention): windowed GQA forward + deterministic key-tile dK/dV (Triton) #22's plan sorts the indices anyway).tests/indexed_attention/test_selection.py(GPU): exact output against the reference, including ties, plus repeat equality. 6 passed on H200.Notes
T.Parallel(only one bin satisfies the condition) and for the score store.