Skip to content

[executorch][cuda] Add a length-aware Triton SDPA over per-sequence KV columns - #24072

Merged
meta-codesync[bot] merged 5 commits into
gh/Gasoonjia/90/basefrom
gh/Gasoonjia/90/head
Nov 11, 2026
Merged

meta-codesync[bot] merged 5 commits into
gh/Gasoonjia/90/basefrom
gh/Gasoonjia/90/head

Conversation

@Gasoonjia

@Gasoonjia Gasoonjia commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Stack from ghstack (oldest at bottom):

Adds triton::sdpa_varlen, the attention kernel the CUDA batched-sequence KV cache needs. Each resident sequence owns one column of a [rows, cols, H, D] store, and a device segment table seq_info Int32[S, 4] = (q_start, q_len, col, kv_end) says which packed query rows belong to which column and how long that sequence is. Lengths are read on device, so one captured CUDA graph serves any mix of sequence lengths, as the MLX per-sequence SDPA does per group.

Covers causal, non-causal (DFlash draft blocks), sliding window and ring storage, GQA, rows no segment covers (zeroed), and split-K for short steps. A symbolic query length takes the generic path without host-side length branches, so exporting it with a dynamic step width does not pin that width.

It is a new op in its own file; triton::sdpa and its dispatch are untouched.

Differential Revision: D125987296

[ghstack-poisoned]
@pytorch-bot

pytorch-bot Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24072

Note: Links to docs will display an error until the docs builds have been completed.

❌ 1 New Failure, 10 Unrelated Failures

As of commit 15a539b with merge base 4eb094b (image):

NEW FAILURE - The following job has failed:

FLAKY - The following job failed but was likely due to flakiness present on trunk:

BROKEN TRUNK - The following jobs failed but was present on the merge base:

👉 Rebase onto the `viable/strict` branch to avoid these failures

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

[ghstack-poisoned]
@Gasoonjia
Gasoonjia deployed to upload-benchmark-results November 6, 2026 01:33 — with GitHub Actions Active
[ghstack-poisoned]
[ghstack-poisoned]
@Gasoonjia
Gasoonjia deployed to upload-benchmark-results November 10, 2026 11:23 — with GitHub Actions Active
@meta-codesync
meta-codesync Bot merged commit 981e412 into gh/Gasoonjia/90/base Nov 11, 2026
319 of 330 checks passed
@meta-codesync
meta-codesync Bot deleted the gh/Gasoonjia/90/head branch November 11, 2026 02:49
@meta-codesync
meta-codesync Bot deployed to cherry-pick-bot November 11, 2026 02:50 Active
meta-codesync Bot pushed a commit that referenced this pull request Nov 11, 2026
Stack from [ghstack](https://github.com/ezyang/ghstack/tree/0.15.0) (oldest at bottom):
* #24076
* #24107
* #24075
* __->__ #24074
* #24073
* #24072

Teaches `LowerOffGraphKVPass` a third manifest layout, `batched-sequence`, alongside `sequence` (solo) and `cell`. Each `kvcache.update_and_attend` becomes an in-graph write to slot `(position % rows) * columns + seq_cols[i]` followed by `triton::sdpa_varlen` over the column store, with `seq_cols` / `seq_info` as runtime-owned step buffers. The manifest carries `max_seqs`, `max_segments` (= 2 * max_seqs) and `causal` (false for DFlash drafts).

`CudaBackend` treats it as a packed layout like `cell` (step-width check from the packed position source; no joint-graph constant folding of runtime buffers).

Differential Revision: D125987298

Pull Request resolved: #24074
meta-codesync Bot pushed a commit that referenced this pull request Nov 11, 2026
Stack from [ghstack](https://github.com/ezyang/ghstack/tree/0.15.0) (oldest at bottom):
* #24076
* #24107
* #24075
* #24074
* __->__ #24073
* #24072

Adds `CudaBatchedSequenceCache`, registered as `cache::kind::kBatchedSequence` for the CUDA backend, the CUDA counterpart of MLX's batched sequence cache. Each sequence admitted with `seq_new` gets its own column of every layer's `[rows, max_seqs + 1, H, D]` storage (last column is scratch for padded static-width writes). Flat layers grow by whole rows; ring layers are fixed at `min(capacity, window + max_write - 1)` rows.

It implements the neutral `BatchControl` surface the batching executor already drives: `declare_step` / `prepare_step` / `commit_step`, `seq_clone` (device copy of the source prefix), `seq_rm`, `rewind` (bounded by the ring floor), `pos`, `max_seqs`. The per-sequence position plans reuse `SequenceCache`. `prepare_step` stages `seq_cols` and the `seq_info` segment table for `triton::sdpa_varlen`. DFlash owner blocks are validated as one run per owner.

`CacheConfig` gains `max_seqs`, the number of storage columns fixed at export. `CudaKVPool` exposes `layer_data()` for clone copies.

Differential Revision: D125987297

Pull Request resolved: #24073
meta-codesync Bot pushed a commit that referenced this pull request Nov 11, 2026
…ence cache

Stack from [ghstack](https://github.com/ezyang/ghstack/tree/0.15.0) (oldest at bottom):
* #24076
* #24107
* __->__ #24075
* #24074
* #24073
* #24072

Adds `--kv-layout {cell,sequence}` and `--max-kv-seqs` to the Muse Glimmer CUDA batching exports (solo and DFlash). `sequence` writes a `batched-sequence` manifest for target and draft and publishes `get_offgraph_kv_max_seqs` (and `get_draft_offgraph_kv_max_seqs` / `get_draft_offgraph_kv_seq_capacity` for the draft). `CudaExecutor` and the CUDA DFlash executor pick `kBatchedSequence` when the artifact declares a sequence count, `kBatchedCell` otherwise, so the served feature set matches MLX's batched-sequence path. Cell artifacts are unchanged. The layout is chosen at export; switching it at runtime on one .pte is out of scope.

Differential Revision: D125987299

Pull Request resolved: #24075
meta-codesync Bot pushed a commit that referenced this pull request Nov 11, 2026
…the sampler rows

Stack from [ghstack](https://github.com/ezyang/ghstack/tree/0.15.0) (oldest at bottom):
* #24076
* __->__ #24107
* #24075
* #24074
* #24073
* #24072

`forward_others` and `forward_from_embeddings` take up to `max_step_tokens`
(4096) tokens and select which rows' logits to return. The selector's dynamic
bound was also `max_step_tokens`, so memory planning sized each method's FP32
logits output at `4096 x 202048 x 4 B = 3157 MiB`, allocated at load whether or
not a request ever uses it.

A step never selects more rows than the device samplers take
(`MAX_SAMPLE_ROWS = 64`: one row per prefilling session, or `max_owners x
block` for DFlash verify), and the executor already rejects a step with more.
Bound the selector at `min(max_step_tokens, MAX_SAMPLE_ROWS)`. Each planned
logits output drops to 49 MiB; token width and the DFlash taps keep their
`max_step_tokens` bound.

Differential Revision: D127114350

Pull Request resolved: #24107
meta-codesync Bot pushed a commit that referenced this pull request Nov 11, 2026
Stack from [ghstack](https://github.com/ezyang/ghstack/tree/0.15.0) (oldest at bottom):
* __->__ #24076
* #24107
* #24075
* #24074
* #24073
* #24072

A ring layer cannot wrap before it holds all its slots, so until then it grows with the flat layers instead of allocating window + max_write - 1 rows per sequence up front. Growable layers now cap at their own declared rows.

Differential Revision: D126851225

Pull Request resolved: #24076
Gasoonjia added a commit that referenced this pull request Nov 11, 2026
…V columns (#24879)

This PR was created by the merge bot to help merge the original PR into
the main branch.
ghstack PR number: #24072 by
@Gasoonjia
^ Please use this as the source of truth for the PR details, comments,
and reviews
ghstack PR base:
https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/base
ghstack PR head:
https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/head
Merge bot PR base: https://github.com/pytorch/executorch/tree/main
Merge bot PR head:
https://github.com/pytorch/executorch/tree/gh/Gasoonjia/90/orig
Differential Revision:
[D125987296](https://our.internmc.facebook.com/intern/diff/D125987296/)
@diff-train-skip-merge

Co-authored-by: gasoonjia <gasoonjia@icloud.com>

This branch was successfully deployed

2 active and 1 inactive deployments
cherry-pick-bot — 15a539bd Deployed Nov 11, 2026 by meta-codesync[bot] via Try to create a PR with ghstack /orig branch #1931
upload-benchmark-results — 15a539bd Deployed Nov 10, 2026 by Gasoonjia via upload-benchmark-results #22226
cadence — 15a539bd Deployed Nov 10, 2026 by Gasoonjia via hifi-op-test / hifi4 #37533
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. meta-exported

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants