[CUDA] Enable XQA by default for FP16/BF16 GQA - #29046
Conversation
|
@microsoft-github-policy-service agree |
|
update via |
Signed-off-by: namgyu-youn <namgyu.dev@gmail.com>
tianleiwu
left a comment
There was a problem hiding this comment.
Thanks for the XQA default-on + group_size=5 work. Functionally this looks sound: the env override semantics are preserved (ORT_ENABLE_XQA=0 still wins), and ineligible shapes/group sizes fall back safely via data.use_xqa. Replacing 64 % group_size == 0 with the explicit {1,2,4,5,8,16,32} allowlist is also a nice correctness fix — it drops group_size=64, which previously passed the gate but has no case 64 in the launcher switch and would have returned a hard FAIL instead of falling back.
Two follow-ups (both non-blocking):
-
Missing dedicated test for
group_size=5. This is the first non-power-of-2 group (the M_TILESIZE=8 tile leaves 3 unused rows), but it isn't covered by any XQA parity test —gqa_xqa_test_cases()loops[4,8,16,32]andgqa_xqa_head_sink_test_cases()loops[1,4,8]. Please add agroup_size=5case (e.g. 40 Q / 8 KV) to a parity test intest_gqa.pyso the new kernel path is validated, not only benchmarked. (test_gqa.pyisn't in this diff, hence the note here.) -
Stale docstring:
gqa_xqa_head_sink_test_cases()still documents64 % group_size == 0, which no longer matches the new allowlist.
Inline comments below for the constructor comment drift and the allowlist.
|
@namgyu-youn, there is build error: |
@tianleiwu sorry it's my bad; it should be green now. Could you trigger CI again? |
|
There is another error: |
Signed-off-by: namgyu-youn <namgyu.dev@gmail.com>
|
Update via @tianleiwu could you please trigger CI again? It should be green now I believe. |
|
@namgyu-youn, please run lintrunner to format the code: |
done; @tianleiwu sorry for my bad. Could you please trigger CI again? |
### Description The `GroupQueryAttentionTest.BatchedRightPaddedRotaryPrefill_CUDA` test (added in #29002) fed **fp32** inputs via `AddInput<float>`. The CUDA (and WebGPU) GroupQueryAttention kernels only register for `MLFloat16`/`BFloat16`, so the fp32 node silently fell back to the **CPU EP** — the `_CUDA` test never actually exercised the CUDA kernel it is named for. This surfaced as a CI failure on the CUDA test leg after #29002 and #29046 merged. This PR makes `RunGQAPackedQKVRotaryPrefill` feed **fp16** tensors when targeting CUDA EP, matching the existing `RunGQASharedKVFp16` convention and the test's own "loose enough for fp16 rounding" tolerance. The CPU code path is unchanged. ### Key Changes - `RunGQAPackedQKVRotaryPrefill` now branches on the target EP: - CUDA EP: inputs/outputs use `MLFloat16` (converted via `ToFloat16`), so the node is placed on the real GPU kernel. - WebGPU/CPU EP: unchanged (`float`). - Output is converted back to `float` for the existing comparison logic. ### Testing - `onnxruntime_provider_test --gtest_filter='GroupQueryAttentionTest.BatchedRightPaddedRotaryPrefill_CUDA'` → **PASSED** (now runs on the CUDA fp16 kernel). - Full `GroupQueryAttentionTest.*` suite → 47 passed, WebGPU-only tests skipped locally (no WebGPU EP), no regressions. ### Motivation and Context Restores genuine CUDA kernel coverage for the right-padded rotary prefill scenario and fixes the CI failure. Related: #29002, #29046.
### Description The `GroupQueryAttentionTest.BatchedRightPaddedRotaryPrefill_CUDA` test (added in #29002) fed **fp32** inputs via `AddInput<float>`. The CUDA (and WebGPU) GroupQueryAttention kernels only register for `MLFloat16`/`BFloat16`, so the fp32 node silently fell back to the **CPU EP** — the `_CUDA` test never actually exercised the CUDA kernel it is named for. This surfaced as a CI failure on the CUDA test leg after #29002 and #29046 merged. This PR makes `RunGQAPackedQKVRotaryPrefill` feed **fp16** tensors when targeting CUDA EP, matching the existing `RunGQASharedKVFp16` convention and the test's own "loose enough for fp16 rounding" tolerance. The CPU code path is unchanged. ### Key Changes - `RunGQAPackedQKVRotaryPrefill` now branches on the target EP: - CUDA EP: inputs/outputs use `MLFloat16` (converted via `ToFloat16`), so the node is placed on the real GPU kernel. - WebGPU/CPU EP: unchanged (`float`). - Output is converted back to `float` for the existing comparison logic. ### Testing - `onnxruntime_provider_test --gtest_filter='GroupQueryAttentionTest.BatchedRightPaddedRotaryPrefill_CUDA'` → **PASSED** (now runs on the CUDA fp16 kernel). - Full `GroupQueryAttentionTest.*` suite → 47 passed, WebGPU-only tests skipped locally (no WebGPU EP), no regressions. ### Motivation and Context Restores genuine CUDA kernel coverage for the right-padded rotary prefill scenario and fixes the CI failure. Related: #29002, #29046.
Description
Enable XQA by default for non-quantized FP16/BF16 GQA. XQA is a TensorRT-LLM-derived fused decode kernel (
seq_len=1) that also fuses RoPE + KV-append into a single pass. Requires SM80+, shared KV buffer, no softcap.Also add
group_size=5XQA support (e.g. Qwen3-14B: 40 Q-heads / 8 KV-heads) for the Qwen series.Affected models
Performance Result
repro:
Benchmark: Qwen3-14B (40Q/8KV heads, head_dim=128, fp16, batch=1, SM80, CUDA12.8)
Speedup grows with context length (1.30× → 1.57×). No regression in the prompt/prefill path.
Testing
pytest onnxruntime/test/python/transformers/test_gqa.py -vORT_ENABLE_XQA=0 pytest onnxruntime/test/python/transformers/test_gqa.py -v(opt-out smoke test)