Skip to content

[All] Refactor fused attention APIs with cuDNN-frontend support checks and opaque config/params handles - #2964

Merged
cyanguwa merged 173 commits into
NVIDIA:mainfrom
cyanguwa:fe_check_support
Sep 10, 2026
Merged

cyanguwa merged 173 commits into
NVIDIA:mainfrom
cyanguwa:fe_check_support

Conversation

@cyanguwa

@cyanguwa cyanguwa commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Description

TE currently hand-maintains the fused-attention backend-selection logic in nvte_get_fused_attn_backend, duplicating cuDNN's support rules. This list drifts out of sync as cuDNN evolves, and the support check can disagree with what actually runs.

This PR replaces that logic with cuDNN-frontend's production-grade support checks. The new nvte_get_fused_attn_backend_v2 builds the same graph cuDNN executes at runtime, so the probe and execution can no longer diverge. It caches the graph on success and returns a diagnostic message on failure, giving users actionable guidance (e.g. adjust the config, GPU architecture, or cuDNN version).

This PR also reworks nvte_fused_attn_fwd / nvte_fused_attn_bwd into nvte_fused_attn_fwd_v2 / nvte_fused_attn_bwd_v2, which take opaque, attribute-based config/params handles instead of long flat argument lists — improving TE's API and ABI stability.

Legacy APIs are retained as deprecated shims that route through the v2 APIs, so existing callers keep working.

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

API rework (opaque config/params + v2 entry points)

  • Opaque config/params handles (common/fused_attn/config_and_params.{h,cpp}, common/include/transformer_engine/fused_attn.h): new NVTEFusedAttnConfig / NVTEFusedAttnFwdParams / NVTEFusedAttnBwdParams with create/destroy/get/set attribute accessors, for better API/ABI stability. The cache key, probe, and execution now all originate from one place via make_config / derive / make_cache_key.
  • v2 APIs (common/fused_attn/fused_attn*.{cpp,cu}): nvte_get_fused_attn_backend_v2, nvte_fused_attn_fwd_v2, and nvte_fused_attn_bwd_v2, taking the opaque handles above in place of flat argument lists.
  • Graph construction split from execution (common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu, fused_attn_fp8.cu, graph_cache.h): graph building moved out of fused_attn_*_{fwd,bwd}_impl into create_graph_{f16,fp8}_{fwd,bwd}(const FusedAttnConfig &); the *_impl bodies now only fetch the graph, finish its plans, and bind the variant pack. The probe (support_verdict_*) and execution reach that builder through the same get_graph<Backend, Pass, create_graph_*> and cache, so they can't diverge; build_plans() is deferred so a probe-only miss doesn't pay for it.
  • Deprecated shims: legacy nvte_get_fused_attn_backend / nvte_fused_attn_fwd / nvte_fused_attn_bwd are retained, routed through the v2 APIs.
  • Bindings updated to v2: PyTorch (csrc/extensions/attention.cpp) and JAX (jax/csrc/extensions/attention.cpp).

Correctness & backend selection

  • Process-wide graph cache: cache is now process-wide (was thread-local) and guarded by a mutex, so a compiled graph is reused across threads instead of rebuilt per thread (still thread-safe).
  • Bias-shape handling fix: applied consistently across common, PyTorch, and JAX.
  • Per-step CP config checks: cp_per_step_configs probes each context-parallel step instead of only the global, non-CP config.
  • log2(0) guard: avoids UB when casting -inf to size_t in get_max_batch_size / get_max_tokens.

Diagnostics

  • NVTE_DEBUG / NVTE_DEBUG_LEVEL for JAX (parity with PyTorch): level 1 reports the selected backend; level 2 adds a diagnostic message explaining why fused attention was rejected.
  • Fused attention graph cache debug NVTE_FUSED_ATTN_CACHE_DEBUG=<level>[:<ranks>]: opt-in instrumentation that reports cuDNN graph build-vs-execution counts and per-stage cudnn-frontend build timings, so cache hit/miss/build/exec behaviors and graph build time can be inspected. Off by default; [:<ranks>] specifies select ranks for diagnostics; available for both PyTorch and Jax.

Cleanup / removals

  • Removed NVTE_FUSED_ATTN_BACKEND — the two remaining backends (F16, FP8) are mutually exclusive now that max512 is gone.
  • Removed dead Q_ID/.../MASK_VAL_ID macros (used only by the max512 backend).
  • Removed dead cudnn_frontend::xxx utility functions (used only by fp8_impl_v0 and max512).
  • Unified include-guard names across fused_attn/ headers.

Tests

  • Enabled previously skipped tests: padding + post_scale_bias in both PyTorch and Jax, D256 bprop in PyTorch, and SWA + dropout/post_scale_bias in Jax.
  • Curated the L0 sweeps to keep CI time in check: deduplicated PyTorch tests, and tiered the newly enabled JAX tests across L0/L1/L2.
  • Added test_fused_attn_graph_cache to test graph cache's behavior with NVTE_FUSED_ATTN_CACHE_DEBUG=2 on, and test_fused_attn_backend_message to test the surfacing of TE-specific or cuDNN-related error messages to users.

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

cyanguwa and others added 4 commits May 5, 2026 18:55
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa cyanguwa changed the title [Common] Refactor nvte_get_fused_attn_backend with cudnn-frontend calls [All] Refactor nvte_get_fused_attn_backend with cudnn-frontend calls May 8, 2026
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa
cyanguwa marked this pull request as ready for review May 8, 2026 00:10
@greptile-apps

greptile-apps Bot commented May 8, 2026

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new actionable issue was introduced after the previous review.

Summary

  • Adds v2 fused-attention APIs while retaining deprecated compatibility shims.
  • Unifies support probing and execution graph construction through a process-wide, device-aware graph cache.
  • Updates PyTorch and JAX bindings, including packed-THD metadata and context-parallel capability checks.
  • Adds backend diagnostics, cache instrumentation, documentation, and expanded tests.
  • Changes since the previous review only reformat a diagnostic string and introduce no behavioral changes.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[PyTorch or JAX attention request] --> B[Opaque fused-attention config]
    B --> C[Backend v2 support query]
    C --> D[TE capability guards]
    D --> E[cuDNN frontend graph construction]
    E --> F[Process-wide device-aware graph cache]
    F --> G[Forward or backward execution]
    C -->|Unsupported| H[Diagnostic and compatible fallback]
Loading

Comment thread transformer_engine/common/fused_attn/fused_attn_fp8.cu Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn_fp8.cu Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp
Comment thread transformer_engine/common/include/transformer_engine/fused_attn.h Outdated
cyanguwa and others added 2 commits May 7, 2026 17:22
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp Outdated
cyanguwa and others added 3 commits May 7, 2026 18:30
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

cyanguwa commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L1

Comment thread transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Comment thread transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu Outdated
Comment thread transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu Outdated
cyanguwa and others added 3 commits May 7, 2026 22:28
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp Outdated
Comment thread transformer_engine/jax/cpp_extensions/attention.py Outdated
cyanguwa and others added 2 commits May 8, 2026 12:19
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Comment thread transformer_engine/common/fused_attn/fused_attn.cpp Outdated
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

cyanguwa commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

Conflict in dot_product_attention.py: NVIDIA#3476 respelled the attention_params_kwargs
dict() call as a dict literal, which rewrote every line of a block this branch had
also edited. Resolved to main's literal syntax carrying this branch's semantics:
the four added keys (num_tokens_q, num_tokens_kv, cp_size_a2a, softmax_scale) and
the tightened alibi_slopes_shape and core_attention_bias_requires_grad guards.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

cyanguwa commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

cyanguwa commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

@KshitijLakhani KshitijLakhani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: So the fact that we use an adjusted window size for CP in one of our primitives which is different than the global window size is not something this PR introduces, however, I'm wondering ff the caching becomes less efficient because of this ?

workspace query with global window
      → build/cache global-window graph in the thread-local cache

  runtime with adjusted CP window
      → different descriptor
      → cache miss
      → build/cache adjusted-window graph

it's a minor penalty I suppose, but I do not think we need to worry about it - thoughts ?

Comment thread transformer_engine/jax/csrc/extensions/attention.cpp Outdated
Comment thread transformer_engine/jax/cpp_extensions/attention.py
Comment thread transformer_engine/jax/cpp_extensions/attention.py Outdated
Comment thread transformer_engine/jax/csrc/extensions/attention.cpp
Comment thread tests/jax/test_fused_attn_score_mod.py Outdated
@cyanguwa

cyanguwa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

nit: So the fact that we use an adjusted window size for CP in one of our primitives which is different than the global window size is not something this PR introduces, however, I'm wondering ff the caching becomes less efficient because of this ?

workspace query with global window
      → build/cache global-window graph in the thread-local cache

  runtime with adjusted CP window
      → different descriptor
      → cache miss
      → build/cache adjusted-window graph

it's a minor penalty I suppose, but I do not think we need to worry about it - thoughts ?

Done. I added a helper function to get the effective window size for CP + Ring + Striped + THD + SWA. Looking through other CP variants, it seems that they are already passing the exact config that the execution will run, during the probing/support query stage. Thanks.

@cyanguwa

cyanguwa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

The FFI fwd/bwd paths queried the backend and then populated the RNG state
and the aux tensor pack unconditionally. With NVTE_No_Backend that walked
the FP8 branch of PopulateRngStateAsync, advanced the global RNG offset and
launched a kernel for an attention that never runs, and the rejection
message was discarded, so the failure only surfaced later from
nvte_fused_attn_{fwd,bwd}_v2 without the diagnostic.

Check the backend right after the query and surface the message.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
GetFusedAttnBackendImpl took 36 positional arguments, including runs of
same-typed heads, head dims and sequence lengths that could be transposed
at a call site and still compile.

Have each of the three call sites build a FusedAttnConfigWrapper with its
named setters instead, and reduce GetFusedAttnBackendImpl to the query
itself. The wrapper already names every field and owns the defaults, so it
supplies the anti-swap property without a second struct mirroring
FusedAttnConfig. The pybind entry point now reads like its PyTorch
counterpart, and the two FFI handlers build the config in
FUSED_ATTN_IMPL_COMMON_BLOCK, which already decodes the rest of the
arguments they share, so the config cannot drift from the values it is
derived from.

GetFusedAttnBackendImpl stays separate from the pybind GetFusedAttnBackend
because the FFI handlers run without the GIL and have no params object to
pass. Their query is not a repeat of the one the Python layer ran: batch
size is the segment count, which for ragged input is only resolved inside
the handler.

No behavior change: the fields the FFI paths never set are pinned to the
values they previously picked up as struct defaults. return_max_logit is
pinned to false, which is what the backward pass always passed, and the
forward pass overrides it before querying.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
The three production call sites passed the first sixteen fields positionally,
including the is_training/batch_size and q_num_heads/kv_num_heads/q_max_seqlen/
kv_max_seqlen runs, where a transposition type-checks and yields a plausible
backend answer rather than an error. Name them, convert the one remaining
positional construction under tests/jax, and mark the dataclass kw_only so the
convention cannot drift back.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
The positional index into the recorded FusedAttnHelper arguments had to move
from 3 to 4 when batch_size was added. Now that the call site passes keywords,
read qkv_layout out of the recorded kwargs instead.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
The fwd/bwd lowerings substitute cp_striped_window_size for the global window
(CP + Ring P2P + THD + SWA), but the backend probe and the workspace-size
query in abstract kept using config.window_size. The probe therefore
validated, and the graph cache retained, a descriptor that never executes,
while the executing descriptor was never checked.

Add _FusedAttnConfig.effective_window_size and use it in both places, which
also collapses the branch that was duplicated in the two lowerings.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

cyanguwa commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

@cyanguwa

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

@cyanguwa

Copy link
Copy Markdown
Collaborator Author

L1 and L2 jax distributed tests have some hangs but fused_attn related tests have run and passed.

@cyanguwa
cyanguwa merged commit f689f8f into NVIDIA:main Sep 10, 2026
9 of 22 checks passed
nvegesna-netizen added a commit to nvegesna-netizen/TransformerEngine that referenced this pull request Sep 10, 2026
Three conflicts, all two sides adding different things at the same line.

tests/pytorch/utils.py: NVIDIA#2964 adds bottom_right_diagonal to ModelConfig and to
the AttentionParams call where this branch adds softcap. AttentionParams carries
both fields, so both are kept.

dot_product_attention.py: the attention_params_kwargs dict. NVIDIA#2964 tightens the
alibi_slopes_shape guard to require core_attention_bias_type == "alibi". Kept
that and kept the "softcap" key, which get_attention_backend needs to see in
order to disqualify the backends that cannot honour a cap. Dropping it here
would leave softcap silently ignored during backend selection with no test
failure, which is how it was lost in an earlier merge on this branch.

Signed-off-by: Nitin Vegesna <nvegesna@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants