-
Notifications
You must be signed in to change notification settings - Fork 795
[PyTorch][torch.compile] Support for DotProductAttention on flash and unfused backends #3286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pggPL
wants to merge
40
commits into
NVIDIA:main
Choose a base branch
from
pggPL:dpa_torch_compile
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
f6ed27c
[PyTorch][torch.compile] Support for DotProductAttention
pggPL 3053d76
Delegate the no-FP8 case of DPA's init_fp8_metadata to the base class
pggPL d6b285e
Run DPA eagerly for undeclared packed q/k/v instead of guessing the l…
pggPL 1cd6704
Log backend selection in eager only; shorten comments
pggPL 42a9e6c
Fix lint: unused import and keyword-arg-before-vararg
pggPL af11f8f
Compare against eager under CUDA graphs, and tighten the tolerances
pggPL 0b64c96
Support one cu_seqlens tensor for both q and kv under torch.compile
pggPL 7f480c6
Apply the shared-cu_seqlens workaround to the FlashAttention v4 path too
pggPL adc96c4
Run FP8 attention eagerly under torch.compile
pggPL 737904f
Drive the compile tests off ModelConfig instead of a hand-picked inte…
pggPL 9026a41
Scale the unfused comparison to the tensor, and take tolerances from …
pggPL bd2c506
Merge the per-backend compile tests, and give padding masks real padding
pggPL 725931e
Run eagerly when max_seqlen has to be derived from cu_seqlens
pggPL b2ab7eb
Share the run-and-compare code between the compile tests
pggPL 50d1984
Use the shared run-and-compare code in the remaining compile tests
pggPL f19c52d
Read the predicate's arguments by name, not by hardcoded position
pggPL fbc7146
Move the eager-fallback decorator to jit.py
pggPL 6b24630
Skip FusedAttention rather than special-casing it in the compile tests
pggPL 9a204b8
Cover the compiled path around FusedAttention
pggPL 2188f18
Log backend selection through the no-op logger, as get_attention_back…
pggPL 10d141b
Return the fused sub-backend as an int, in eager as well
pggPL 93a201d
Rename eager_under_compile_if to fallback_to_eager_when
pggPL 0866319
Fold the eager fallback into no_torch_dynamo as a `when` predicate
pggPL a80a41a
Cover the declared packed layouts beyond bs3hd
pggPL 9eb1449
Let ONNX export keep its own path in get_qkv_layout
pggPL 5c5b451
Guard the assumption the argument binding rests on
pggPL 95030f9
Check that CUDA graphs were actually captured, and generalize a docst…
pggPL b0e56d4
Skip lazy compilation while already tracing
pggPL 6f7c818
Merge upstream/main into dpa_torch_compile
pggPL 2cf695b
Run context parallel attention eagerly
pggPL b89446f
Keep DotProductAttention compilable under a CUDA RNG states tracker
pggPL fb50154
Run checkpointed attention eagerly
pggPL 4b1ebb2
Support KV caching under torch.compile
pggPL eb5ad55
Compile the fused sbh3d QKV split
pggPL 7da1a66
Run FlashAttention 4 eagerly
pggPL d1beb9b
Pin the sequence lengths backend selection bakes in
pggPL 4af8c30
Capture CUDA graphs with a KV cache
pggPL bd94fff
Test generation against a KV cache
pggPL 688e10c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 43eba11
Drop the imports the custom ops made unused
pggPL File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| # Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # | ||
| # See LICENSE for license information. | ||
|
|
||
| """Attention kernels wrapped as custom ops, so they don't graph-break under torch.compile.""" | ||
|
|
||
| import torch | ||
| import transformer_engine_torch as tex | ||
|
|
||
| from transformer_engine.pytorch.cpp_extensions.fused_attn import QKVFormat | ||
|
|
||
| # The ops take the format's value rather than the pybind enum: converting the | ||
| # enum inside a traced region makes dynamo recurse until it gives up. | ||
| QKV_FORMAT_VALUE = {name: int(fmt) for name, fmt in QKVFormat.items()} | ||
| _QKV_FORMAT_BY_VALUE = {int(fmt): fmt for fmt in QKVFormat.values()} | ||
|
|
||
|
|
||
| @torch.library.custom_op( | ||
| "te_kv_cache::copy_to_kv_cache", | ||
| mutates_args=("k_cache", "v_cache"), | ||
| device_types="cuda", | ||
| ) | ||
| def copy_to_kv_cache( | ||
| new_k: torch.Tensor, | ||
| new_v: torch.Tensor, | ||
| k_cache: torch.Tensor, | ||
| v_cache: torch.Tensor, | ||
| page_table: torch.Tensor, | ||
| cu_new_lens: torch.Tensor, | ||
| cu_cached_lens: torch.Tensor, | ||
| qkv_format: int, | ||
| b: int, | ||
| max_ctx_len: int, | ||
| max_seq_len: int, | ||
| max_pages_per_seq: int, | ||
| is_non_paged: bool, | ||
| ) -> None: | ||
| """Copy new key/value tokens into the KV cache.""" | ||
| tex.copy_to_kv_cache( | ||
| new_k, | ||
| new_v, | ||
| k_cache, | ||
| v_cache, | ||
| page_table, | ||
| cu_new_lens, | ||
| cu_cached_lens, | ||
| _QKV_FORMAT_BY_VALUE[qkv_format], | ||
| b, | ||
| max_ctx_len, | ||
| max_seq_len, | ||
| max_pages_per_seq, | ||
| is_non_paged, | ||
| ) | ||
|
|
||
|
|
||
| @copy_to_kv_cache.register_fake | ||
| def _copy_to_kv_cache_fake(*_args, **_kwargs) -> None: | ||
| return None | ||
|
|
||
|
|
||
| @torch.library.custom_op("te_kv_cache::convert_bshd_to_thd", mutates_args=(), device_types="cuda") | ||
| def convert_bshd_to_thd(tensor: torch.Tensor, cu_seqlens: torch.Tensor, t: int) -> torch.Tensor: | ||
| """Convert a tensor from bshd to thd.""" | ||
| return tex.convert_bshd_to_thd(tensor, cu_seqlens, t) | ||
|
|
||
|
|
||
| @convert_bshd_to_thd.register_fake | ||
| def _convert_bshd_to_thd_fake( | ||
| tensor: torch.Tensor, cu_seqlens: torch.Tensor, t: int | ||
| ) -> torch.Tensor: | ||
| del cu_seqlens | ||
| return tensor.new_empty((t, *tensor.shape[2:])) | ||
|
|
||
|
|
||
| @torch.library.custom_op("te_kv_cache::convert_thd_to_bshd", mutates_args=(), device_types="cuda") | ||
| def convert_thd_to_bshd( | ||
| tensor: torch.Tensor, cu_seqlens: torch.Tensor, b: int, max_seq_len: int | ||
| ) -> torch.Tensor: | ||
| """Convert a tensor from thd to bshd.""" | ||
| return tex.convert_thd_to_bshd(tensor, cu_seqlens, b, max_seq_len) | ||
|
|
||
|
|
||
| @convert_thd_to_bshd.register_fake | ||
| def _convert_thd_to_bshd_fake( | ||
| tensor: torch.Tensor, cu_seqlens: torch.Tensor, b: int, max_seq_len: int | ||
| ) -> torch.Tensor: | ||
| del cu_seqlens | ||
| return tensor.new_empty((b, max_seq_len, *tensor.shape[1:])) | ||
|
|
||
|
|
||
| @torch.library.custom_op("te_attention::fa_prepare_fwd", mutates_args=(), device_types="cuda") | ||
| def fa_prepare_fwd(qkvi: torch.Tensor) -> torch.Tensor: | ||
| """Split interleaved sbh3d QKV into bshd q/k/v.""" | ||
| return tex.fa_prepare_fwd(qkvi) | ||
|
|
||
|
|
||
| @fa_prepare_fwd.register_fake | ||
| def _fa_prepare_fwd_fake(qkvi: torch.Tensor) -> torch.Tensor: | ||
| # qkvi is the q view into the packed buffer, and its strides cover all of it. | ||
| s, b, n, h = qkvi.shape | ||
| return qkvi.new_empty((3, b, s, n, h)) | ||
|
|
||
|
|
||
| @torch.library.custom_op("te_attention::fa_prepare_bwd", mutates_args=(), device_types="cuda") | ||
| def fa_prepare_bwd(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor: | ||
| """Pack bshd gradients back into an interleaved sbh3d buffer.""" | ||
| return tex.fa_prepare_bwd(q, k, v) | ||
|
|
||
|
|
||
| @fa_prepare_bwd.register_fake | ||
| def _fa_prepare_bwd_fake(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor) -> torch.Tensor: | ||
| del k, v | ||
| b, s, n, h = q.shape | ||
| return q.new_empty((s, b, n, 3 * h)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would
_unalias_cu_seqlens,_ensure_distinct_cu_seqlens, or_clone_shared_cu_seqlensbe a better name?