onnxruntime
55ab2dff - [CUDA][PagedAttention] Fuse small-batch sequence-length sanitizer and add session.paged_attention.skip_input_sanitizer (#33597)

Commit
5 days ago
[CUDA][PagedAttention] Fuse small-batch sequence-length sanitizer and add session.paged_attention.skip_input_sanitizer (#33597) ### Description - Add single-CTA `FusedSanitizeSequenceLengthsSmallBatchKernel` (`cub::BlockScan<int32_t, 256>`, `workspace_bytes = 0`) in `onnxruntime/contrib_ops/cuda/bert/paged_attention_impl.cu` for `batch_size < 256`, replacing the 4-kernel `ClampCumulativeSequenceLengths` + `cub::DeviceScan::InclusiveScan(Max)` + `SanitizePastSequenceLengths` + `cub::DeviceScan::InclusiveSum` chain with a single kernel launch while preserving exact bit-identical semantics and keeping the general grid-wide CUB path for `batch_size >= 256`. - Add session configuration key `kOrtSessionOptionsPagedAttentionSkipInputSanitizer` (`"session.paged_attention.skip_input_sanitizer"`, with environment variable fallback `ORT_PAGED_ATTENTION_SKIP_SANITIZER=1`) allowing trusted runtimes that already validate `block_table`, `cumulative_sequence_length`, and `past_seqlens` to bypass `SanitizeBlockTableKernel`, `LaunchSanitizeSequenceLengths`, and `MaskInvalidSequenceOutputs` and compute `cumulative_seqlens_kv` via a single `LaunchGetCumulativeSeqlensKV` launch (`sequence_validity = nullptr`). - Update `docs/contrib_ops/cuda/paged_attention.md` to document both the small-batch fused sanitizer and `session.paged_attention.skip_input_sanitizer`. ### Motivation and Context - In speculative decoding with a 5-layer `PagedAttention` drafter (`DFlash2`, `batch_size = 1`), the 4-kernel sequence-length sanitizer plus `SanitizeBlockTableKernel` and `MaskInvalidSequenceOutputs` added 5 extra kernel launches and 2 host-side `cub::DeviceScan` workspace queries per `PagedAttention` layer (`25` extra kernel launches per drafter step, `~175` launches per 7-step cycle). - **Fused small-batch sanitizer (`skip_input_sanitizer = 0`, default)**: Collapses the 4 sequence-length sanitization launches into 1 single-CTA launch (`1.56 µs/call`, `workspace_bytes = 0`), eliminating 15 kernel launches per drafter step (`~105` launches/cycle) while retaining full defensive input sanitization. - **Skipping input sanitizer (`session.paged_attention.skip_input_sanitizer = "1"`)**: Eliminates `SanitizeBlockTableKernel` (`1.13 µs/call`), `MaskInvalidSequenceOutputs` (`1.08 µs/call`), replaces `FusedSanitizeSequenceLengthsSmallBatchKernel` (`1.56 µs/call`) with `GetCumulativeSeqlensKV` (`1.40 µs/call`), removes 3 scratch buffer allocations per layer, and skips the `FindTokenBatch` binary search in `ReshapeAndCache` (`2.79 µs -> 2.62 µs/call`), saving another 2 kernel launches (`2.54 µs` GPU kernel time) per `PagedAttention` layer (`10` launches per drafter step) and reducing end-to-end Qwen3.5-397B decode cycle latency on H200 by **`0.07–0.16 ms/cycle`** (`14.635 -> 14.550 ms/cycle` at `2K`, `14.748 -> 14.681 ms/cycle` at `8K`, `15.044 -> 14.948 ms/cycle` at `32K`). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Author
Parents
Loading