Skip to content

[All] Refactor nvte_get_fused_attn_backend with cudnn-frontend calls#2964

Open
cyanguwa wants to merge 30 commits into
NVIDIA:mainfrom
cyanguwa:fe_check_support
Open

[All] Refactor nvte_get_fused_attn_backend with cudnn-frontend calls#2964
cyanguwa wants to merge 30 commits into
NVIDIA:mainfrom
cyanguwa:fe_check_support

Conversation

@cyanguwa

@cyanguwa cyanguwa commented May 6, 2026

Copy link
Copy Markdown
Collaborator

Description

This PR replaces the hand-maintained backend selection logic in nvte_get_fused_attn_backend with cudnn-frontend's production-grade support checks.

  • It will build the same graph that runtime uses in execution, cache the graph if the build is successful, and provide a warmed-up cache for the next nvte_get_fused_attn_backend call.
  • It provides a cleaner dispatch logic, avoids accidental regressions, and helps TE to stay in sync with cudnn-frontend's support surface.
  • It also provides appropriate error messaging for when no backend is available, so users are notified and make config/architecture/cudnn version adjustments.

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

See Description.

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

Greptile Summary

This PR refactors fused-attention backend selection to use cudnn-frontend support checks. The main changes are:

  • Adds an opaque fused-attention config handle and attribute API.
  • Threads backend config fields through PyTorch and JAX availability checks.
  • Replaces hand-written support logic with graph-based support probes.
  • Updates fused-attention runtime paths to build richer probe configs.
  • Adds tests for updated backend availability behavior.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
transformer_engine/common/fused_attn/fused_attn.cpp Refactors fused-attention backend selection around the new config object and cudnn-frontend probes.
transformer_engine/common/fused_attn/config_and_params.cpp Adds the backing implementation for fused-attention config creation, destruction, and attribute access.
transformer_engine/common/include/transformer_engine/fused_attn.h Adds the public fused-attention config handle, attributes, and C++ wrapper helpers.
transformer_engine/jax/cpp_extensions/attention.py Updates JAX fused-attention helper calls to pass the expanded backend query inputs.
transformer_engine/pytorch/attention/dot_product_attention/utils.py Updates PyTorch backend availability checks to pass expanded fused-attention config values.

Reviews (12): Last reviewed commit: "replace with opaque handle" | Re-trigger Greptile

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 on lines +232 to +238
thread_local std::string fused_attn_backend_message_buffer;

void set_message(const char **message, const std::string &reason) {
if (message == nullptr) return;
fused_attn_backend_message_buffer = reason;
*message = fused_attn_backend_message_buffer.c_str();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Thread-local buffer invalidated on next call on the same thread

*message is set to the .c_str() of the thread-local fused_attn_backend_message_buffer. Any subsequent call to nvte_get_fused_attn_backend on the same thread will clear and overwrite this buffer, invalidating the raw pointer before the caller has a chance to copy it. The internal probe calls currently pass nullptr for message, so the buffer won't be clobbered by them, but this invariant is fragile and easy to break in a future change.

Comment on lines +232 to +233
bool return_max_logit, bool cuda_graph, bool deterministic, cudnnHandle_t handle,
const char **message);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Breaking public API change: cudnnHandle_t added as a required parameter

nvte_get_fused_attn_backend is a public C API (no namespace, exported symbol). Adding cudnnHandle_t handle as a required parameter, and including <cudnn.h> in the public header, is a breaking change that forces every downstream consumer to hold and pass a cuDNN handle for what was previously a pure-metadata query.

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
cyanguwa and others added 2 commits May 8, 2026 12:19
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Comment on lines +454 to +460
NVTE_Fused_Attn_Backend fused_attention_backend = nvte_get_fused_attn_backend(
is_training, Q_type, KV_type, qkv_layout, bias_type, attn_mask_type, softmax_type, dropout,
h_q, h_kv, max_seqlen_q, max_seqlen_kv, d_qk, d_v, window_size_left, window_size_right,
return_max_logit, cuda_graph, false);
is_training, b, Q_type, KV_type, O_type, scaling_mode, qkv_layout, o_format,
/*do_format=*/o_format, /*dqkv_layout=*/qkv_layout, qkv_scale_inv_format,
/*do_scale_inv_format=*/qkv_scale_inv_format, bias_type, attn_mask_type, softmax_type,
attn_scale, dropout, h_q, h_kv, max_seqlen_q, max_seqlen_kv, d_qk, d_v, window_size_left,
window_size_right, bottom_right_diagonal, return_max_logit, cuda_graph,
/*deterministic=*/false, /*message=*/nullptr);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Forward path re-probes backward with deterministic=false, wasting graph cache entries for deterministic training

nvte_fused_attn_fwd calls nvte_get_fused_attn_backend with is_training=is_training and hardcoded deterministic=false. When a user trains with deterministic=true, the forward invocation builds and caches a cuDNN graph for the non-deterministic backward. A few lines later, nvte_fused_attn_bwd calls nvte_get_fused_attn_backend again with deterministic=true, causing the backward graph to be rebuilt from scratch on every step. The deterministic flag should be threaded through to this site (as it already is in the nvte_fused_attn_bwd call), or the forward pass should skip the backward probe entirely and let nvte_fused_attn_bwd drive its own backend check.

cyanguwa and others added 4 commits May 8, 2026 12:35
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
@cyanguwa

cyanguwa commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L1

cyanguwa and others added 4 commits May 12, 2026 16:21
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

Copy link
Copy Markdown
Collaborator Author

/te-ci L1

Comment on lines +353 to +375
(void)is_training;
NVTEFusedAttnConfig cfg = NVTE_FUSED_ATTN_CONFIG_INIT;
cfg.qkv_layout = qkv_layout;
cfg.bias_type = bias_type;
cfg.attn_mask_type = attn_mask_type;
cfg.softmax_type = softmax_type;
cfg.attn_scale = 1.0f; // legacy default; matches the value pre-PR probes hardcoded
cfg.dropout = dropout;
cfg.max_seqlen_q = max_seqlen_q;
cfg.max_seqlen_kv = max_seqlen_kv;
cfg.window_size_left = window_size_left;
cfg.window_size_right = window_size_right;
cfg.cuda_graph = cuda_graph;
NVTE_CHECK(q_dtype == kv_dtype, "Q and KV must have the same data type.");
cfg.qkv_dtype = q_dtype;
cfg.o_dtype = q_dtype; // legacy: O dtype matches Q dtype
cfg.batch_size = 1; // legacy: pre-PR probes assumed batch=1
cfg.num_attn_heads = num_attn_heads;
cfg.num_gqa_groups = num_gqa_groups;
cfg.head_dim_qk = head_dim_qk;
cfg.head_dim_v = head_dim_v;
cfg.is_training = false; // legacy wrapper cannot express dO/dQKV dtypes; skip bwd probe
cfg.return_max_logit = return_max_logit;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Deprecated wrapper silently discards is_training, accepting unsupported training configurations

The parameter is cast to (void) and then overridden to false, meaning any caller that passes is_training=true through this deprecated API now only runs the forward graph probe. Before this PR, the old hand-written logic applied training-specific restrictions (e.g. rejecting head_dim_qk > 128 for backward on Hopper with older cuDNN). Now those restrictions are bypassed — the forward probe passes, and the function returns a non-No_Backend value for configurations where the actual backward kernel would fail. Downstream code that calls the deprecated API with is_training=true to guard training-mode usage will silently get incorrect results.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
enable_fused_attn = int(os.getenv("NVTE_FUSED_ATTN", "1"))

sequence_dim = 0 if self.transpose_batch_sequence else 1
batch_dim = 1 - sequence_dim

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.

Should this be batch_dim = sequence_dim - 1 instead of the current 1 - sequence_dim?

For the (B, S, H, D) case they're equivalent, in both cases batch_dim = 0. But if we wanted to support an additional axis (_, B, S, H, D), the current formula would put the batch dim as batch_dim = 1 - seq_dim = 1 - 2 = -1, which is really the D axis.

.value("NVFP4_2D_SCALING", JAXX_Scaling_Mode::NVFP4_2D_SCALING)
.export_values();

pybind11::enum_<NVTEScalingMode>(m, "NVTEScalingMode", pybind11::module_local())

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.

Please use the existing already exported JAXX_Scaling_Mode instead of exporting NVTEScalingMode

@pggPL

pggPL commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

There is a limitation for torch.compile dynamic shapes here. If b and s are passed to black-box function affecting the program flow, I will need to force recompilation on every b or s change.
If we have args like s_greater_or_eq_512 and we check s >= 512 in python then no recompilation would be needed for every change of s. It may be more problematic for b.

I don't think this is a blocker for this PR, but we need to keep it in mind.

Comment on lines +507 to +511
cfg.is_training = false;
cfg.return_max_logit = return_max_logit;
cfg.deterministic = false;
NVTE_Fused_Attn_Backend fused_attention_backend =
nvte_get_fused_attn_backend_v2(&cfg, /*message=*/nullptr);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 The probe uses cfg.is_training = false, so it builds and caches an inference-mode cuDNN forward graph (no softmax-stats output). When the actual kernel is called below with the real is_training value — which is true during training — the cuDNN graph cache key differs and there is a guaranteed cache miss. The training-mode graph is then rebuilt on the first forward pass of every training run, negating the prewarming benefit. Additionally, if a configuration happens to be inference-supported but training-forward-unsupported (cuDNN validates outputs differently in training mode), the backend check returns success but the actual kernel throws at execution time.

Suggested change
cfg.is_training = false;
cfg.return_max_logit = return_max_logit;
cfg.deterministic = false;
NVTE_Fused_Attn_Backend fused_attention_backend =
nvte_get_fused_attn_backend_v2(&cfg, /*message=*/nullptr);
cfg.is_training = is_training;
cfg.return_max_logit = return_max_logit;
cfg.deterministic = false;
NVTE_Fused_Attn_Backend fused_attention_backend =
nvte_get_fused_attn_backend_v2(&cfg, /*message=*/nullptr);

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants