Size Flash split-KV from valid KV length in ONNX Attention opset-24 external-cache path (CUDA)#29689
Size Flash split-KV from valid KV length in ONNX Attention opset-24 external-cache path (CUDA)#29689titaiwangms with Copilot wants to merge 3 commits into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR optimizes the CUDA EP implementation of the ONNX-domain Attention op (opset 24) in the external KV-cache decode path by sizing FlashAttention split-KV work (num_splits) using the valid KV length (from nonpad_kv_seqlen) rather than the buffer length (total_sequence_length). This avoids wasted work over padded cache regions as the preallocated cache grows.
Changes:
- Read back
nonpad_kv_seqlen(device → host) to compute a per-batch max valid KV length and use it to size Flash split-KV launches. - Keep
seqlen_kunchanged (= buffer length) to preserve KV stride correctness while adjusting only the split heuristic. - Add CUDA fp16 test cases with large cache buffers but small valid lengths to exercise the new sizing branch.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
onnxruntime/core/providers/cuda/llm/attention.cc |
Size FlashAttention split-KV from max valid KV length (from nonpad_kv_seqlen) instead of cache buffer length. |
onnxruntime/test/python/transformers/test_onnx_attention/test_tensorscatter_attention.py |
Add fp16 CUDA tests for large-buffer/small-valid external-cache decode scenarios to validate correctness in the new sizing regime. |
Review synthesis (multi-model review team)Thanks for the fix — it targets the right root cause from #29686, and the numerical correctness is solid (see below). But there's one Critical integration problem that should block merge as-is: the per-step host sync breaks CUDA Graph capture. Critical — the unconditional
|
Evaluation: not adopting, closingThanks for the submission. After a design evaluation we've decided not to take this PR, for the following reasons:
Full analysis is captured in #29686. Closing this PR. |
Description
In the opset-24 external-KV-cache decode path (
nonpad_kv_seqlen, CUDA EP),RunFlashAttentionsized the Flash split-KVnum_splitsfromparameters.total_sequence_length. On this path K/V are the full cache, sototal_sequence_lengthis the buffer length, not the valid KV length — over-partitioning the split-KV launch and doing wasted work over the padding region. The same kernel gets slower as the cache buffer grows, exactly the opposite of what long-max-context deployments want.onnxruntime/core/providers/cuda/llm/attention.cc): read the per-batch max valid length from the device-sidenonpad_kv_seqlenonto the host (onebatch_size-element copy) and feed it intoget_num_splits_and_buffer_sizes. Clamped to the buffer length and floored at 1.nonpad_kv_seqlenFlash path; the non-decode paths keeptotal_sequence_lengthsizing.seqlen_kstays= total_sequence_length— it drives the KV-cache strides (k_batch_stride = seqlen_k*h*d) andn_blocks_per_split. Device-sideseqlens_kmasking still bounds the math per batch, sonum_splitsis a perf-only knob and outputs are unchanged.test_tensorscatter_attention.py): added CUDA fp16 large-buffer / small-valid cases (buffer 2048, valid 64/96/128) to exercise the new sizing branch acrossnum_splitsvalues.Tradeoff: reading the valid length host-side costs one small device→host copy + stream sync, scoped to the external-cache Flash path. Valid lengths change every decode step, so cross-call host caching does not apply. A
nonpad_kv_seqlen-as-CPU-input design (mirroring GQA) would avoid the sync but is a larger, cross-cutting change touching all three dispatch paths and input memory placement — left for reviewer input per the issue's open questions.Motivation and Context
The
nonpad_kv_seqlen+TensorScatterdecode path is meant to reach near-parity with contrib GQA, but buffer-sized split launches make it measurably slower (~+66% e2e at buffer 8192, worse as the buffer grows). This is architecture-independent host-side sizing logic, and the single largest term separating the ONNX Attention external-cache decode path from GQA. It bringsattn_scatterup to the Flash-tierattn_pastlevel; the remaining GQA decode-tier gap (fused KV-append prep, decode-specialized kernel) is out of scope here.