Repository navigation
[Fix] Minimax-M3: Deterministic tie-breaking in the CUDA radix decode top-k - #45174
Conversation
This comment has been minimized.
This comment has been minimized.
|
@DarkSharpness @BBuf Hi. Could you kindly review this PR? Thanks! |
|
/tag-and-rerun-ci |
|
/rerun-test test/registered/kernels/ops/attention/test_minimax_sparse_flash_with_topk_idx.py Targeted validation for |
|
🚀 🚀 |
DarkSharpness
left a comment
There was a problem hiding this comment.
Why do we need deterministic here? This may introduce non-trivial overhead. The result is already correct.
|
Results are valid either way, but not stable : tied blocks get picked in atomicAdd order, so the same prompt can select different blocks across runs. The small and ROCm paths already break ties by lower id, so this just makes CUDA radix match. |
|
@DarkSharpness FYI, The ranking only runs when ties straddle the top-k boundary, so overhead is negligible. Microbench is within ±0.2 us of main, and E2E shows no regression. In practice, serving the same prompts twice from a cold cache gave 43/45 identical greedy outputs on main and 45/45 with this PR. Feel free to give feedback!! |
BBuf
left a comment
There was a problem hiding this comment.
LGTM once related CI passes; non-blocking: add a constructed cross-slot tie case.
|
/rerun-test test/registered/kernels/ops/attention/test_minimax_sparse_flash_with_topk_idx.py Targeted validation for |
|
🚀 🚀 |
Motivation
minimax_decode_topkhas three selection regimes. Two of them already break exact score ties by block id (the lower id wins):num_blocks <= 128):is_greater(...), "lower block id wins"(score, ~id)total orderThe CUDA radix paths (
num_blocks > 128, i.e. contexts above 16k tokens at block size 128) place the final-round ties inatomicAddorder instead. When tied blocks straddle the top-k boundary, the selected set changes from run to run. ROCm indexer CP already depends on a fixed tie order ("requires ... the ROCm radix top-k tie ordering"); the CUDA paths gave no such guarantee.Exact ties are common for indexers whose keys carry no RoPE: the block score is the max over units, so repeated text produces equal scores. On such a model, serving the same prompts twice from a cold cache with the radix cache on gave 43/45 identical greedy outputs; with this change, 45/45.
Modifications
equal_count <= topk_remain), the order does not matter (the output is sorted ascending), so the ties are written with a plain atomic as before and the ranking is skipped.counter_final.test_decode_topk_ties_pick_lowest_ids, which checks the selected set against(score desc, id asc)for 300 to 9,000 blocks with heavy ties.ROCm is unchanged.
Accuracy Tests
MiniMax-M3 (bf16, TP8, H200, radix cache on), main vs this PR:
Speed Tests and Profiling
MiniMax-M3, TP8, H200, one request at a time, radix cache flushed before each request:
Kernel microbenchmark (2 heads, 8 to 64 rows, 2k to 8k blocks): within ±0.2 us of main for distinct scores; equal or faster with ties.
Checklist
Review and Merge Process
/tag-and-rerun-ci,/tag-run-ci-label,/rerun-failed-ciCI States
Latest PR Test (Base): ✅ Run #41007060631
Latest PR Test (Extra): ❌ Run #41007060650
Latest PR Test (AMD ROCm 10): ❌ Run #41007060555