Skip to content

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

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

[All] Refactor fused attention APIs with cuDNN-frontend support checks and opaque config/params handles#2964
cyanguwa wants to merge 97 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. The F16 and FP8 is_supported_* probes copy the config, set direction, derive(), and attempt a null-pointer graph build via check_support — i.e. the same graph cuDNN builds at runtime, so probe and execution can't diverge.
  • 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: 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; 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.

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 replaces TE's hand-maintained cuDNN support matrix with cuDNN-frontend's production-grade support checks and introduces opaque config/params handles (NVTEFusedAttnConfig / NVTEFusedAttnFwdParams / NVTEFusedAttnBwdParams) for API/ABI stability. Legacy entry points are retained as deprecated shims. The graph cache is promoted from thread-local to process-wide with mutex guards, so a compiled plan is reused across threads rather than rebuilt per thread.

  • New v2 APIs (nvte_get_fused_attn_backend_v2, nvte_fused_attn_fwd_v2, nvte_fused_attn_bwd_v2) probe backend support by building the exact cuDNN graph that will run at runtime, caching it in a process-wide map keyed by a normalised FusedAttnConfig (including device ID for multi-GPU safety).
  • Correctness fixes include log2(0) UB guards in get_max_batch_size/get_max_tokens, the cuda-graph guard scoped to the F16 branch only, initialised input_Bias/input_SoftmaxOffset pointers, per-step CP config probes, and accurate bias-shape handling.
  • build_sdpa_f16_bwd_graph uses cfg.qkv_layout for all gradient tensor strides and ignores cfg.dqkv_layout; all current callers keep them equal, but any future caller with a distinct gradient layout would get silent wrong strides — this field should be either honoured or asserted equal to qkv_layout.

Confidence Score: 3/5

  • This is a large refactor of a critical attention kernel path. The core graph-cache design is sound and addresses real drift between TE's hand-written support rules and cuDNN's runtime decisions. Several blocking issues identified in prior review rounds remain open (compilation error in fp8 probe message construction, transient errors permanently cached as unsupported, thread-local message buffer lifetime). These should be resolved before merging.
  • The PR brings meaningful correctness improvements but carries a cluster of unresolved issues from the prior review that affect compilation (fp8 enum pointer arithmetic), runtime reliability (OOM permanently marking a config unsupported), and API misuse (dangling const char* from thread-local buffer). Until those are addressed, the change introduces new failure modes alongside the ones it fixes. The dqkv_layout divergence in the backward graph builder is a latent correctness gap that is safe today but will silently corrupt gradients for any future caller that legitimately uses a different gradient layout.
  • transformer_engine/common/fused_attn/fused_attn_fp8.cu (compilation error from prior threads still needs verification), transformer_engine/common/fused_attn/graph_cache.h (transient-error caching), transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu (dqkv_layout ignored in backward graph build)

Important Files Changed

Filename Overview
transformer_engine/common/fused_attn/config_and_params.cpp New file implementing opaque config/params handles with attribute accessors. The switch-based setter/getter pattern is large but correct; all bool fields (is_training, cuda_graph, etc.) have proper cases in both fwd and bwd setters. make_config() correctly propagates MXFP8 columnwise V shape for forward and rowwise for backward (intentional per author reply). derive() correctly guards log2(0) UB. make_cache_key() normalises attn_scale, device_id, and forward vs backward fields to avoid redundant graph entries.
transformer_engine/common/fused_attn/fused_attn.cpp Revised backend-selection and dispatch logic. Several issues from prior review threads were noted here (probe with is_training=false, cache-key mismatch, deprecated wrapper's batch_size=0 and missing o_format). Some appear addressed (batch_size=1 seeded, o_format derived). Uninitialized pointer (input_Bias/input_SoftmaxOffset) fix confirmed at lines 577-583. The cuda-graph guard was moved to the F16 branch only (line 359), correcting the FP8 regression noted in prior threads.
transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu Major refactor converting from hand-written cuDNN graph construction to cudnn-frontend graph API with process-wide cache. The graph build correctly uses qkv_layout for all tensor strides, but dqkv_layout from FusedAttnConfig is never used in build_sdpa_f16_bwd_graph — all current callers keep dqkv_layout == qkv_layout, but divergence would produce silent wrong gradient strides. Device ID is now included in make_cache_key() addressing the multi-GPU cache aliasing bug from prior threads.
transformer_engine/common/fused_attn/graph_cache.h New process-wide graph cache with mutex-guarded positive and negative maps, and deferred build_plans() via std::call_once. The lock-free build with a possible duplicate-build race is well-documented and harmless. The catch-all in validate_and_check_support that permanently caches transient failures (OOM, CUDA errors) as "unsupported" was flagged in prior threads; no change here.
transformer_engine/common/fused_attn/graph_cache_debug.h Well-structured opt-in diagnostics gated behind NVTE_FUSED_ATTN_CACHE_DEBUG. All counter increments and stderr output are guarded by enabled(). The atexit summary handler holds the registry mutex, and new thread registration also holds it, so there is no lifetime or ordering issue with the deliberately leaked registry. Unlike the "temporary debug instrumentation" flagged in earlier rounds, this file is production-quality and not labeled for removal.
transformer_engine/jax/cpp_extensions/attention.py FusedAttnHelper updated with batch_size, bottom_right_diagonal, attn_scale, and bias dimension fields. The backend probe now correctly passes these to the C++ backend check. Error message regression at line 475: f"Unsupported backend: {message}" yields an empty message when a non-F16 backend is accepted (message is empty on success), losing the backend name that the old f"Unsupported {backend=}" provided. The NOT_SET sentinel values for o_format/do_format/dqkv_layout are properly normalised in the C++ binding layer.
transformer_engine/pytorch/attention/dot_product_attention/context_parallel.py New cp_per_step_configs helper correctly enumerates the distinct per-step configs for a2a, all_gather, and p2p CP communication patterns. For p2p causal steps, the halved token counts (t_q // 2, t_kv // 2) are approximate probes; integer rounding can cause cache-key divergence from actual execution for odd token counts, forcing redundant graph rebuilds at runtime.
transformer_engine/common/fused_attn/utils.cu Removed now-unused legacy utility functions (allowAllConfig, tensor_create, pw_desc_create, etc.). Added zero-guards before log2() calls in get_max_batch_size and get_max_tokens, fixing the UB from casting -inf to size_t that the deprecated wrapper's zero batch_size would trigger.
transformer_engine/common/fused_attn/fused_attn_fp8.cu Converted to cudnn-frontend graph API with the same process-wide cache pattern as the F16 file. The prior compilation-error bug (pointer arithmetic on qkv_format enum in is_supported_fp8_fwd/bwd) was flagged in earlier review threads; this should be verified fixed before merge.

Sequence Diagram

sequenceDiagram
    participant Caller as PyTorch/JAX Caller
    participant v2 as nvte_get_fused_attn_backend_v2
    participant Probe as is_supported_f16/fp8_fwd/bwd
    participant Cache as Process-wide GraphCache
    participant cuDNN as cuDNN Frontend

    Caller->>v2: config (NVTEFusedAttnConfig)
    v2->>v2: cfg.derive()
    v2->>Probe: is_supported_f16_fwd(cfg, handle)
    Probe->>Probe: make_cache_key()
    Probe->>Cache: get_or_build_cached_graph(key)
    alt Cache Miss
        Cache->>cuDNN: build_sdpa_f16_fwd_graph()
        cuDNN-->>Cache: graph
        Cache->>cuDNN: validate + build_op_graph + create_plans + check_support
        cuDNN-->>Cache: UnsupportedGraph OR ok
        Cache-->>Probe: cached entry OR throw UnsupportedGraph
    else Cache Hit (supported)
        Cache-->>Probe: cached entry
    else Cache Hit (unsupported)
        Cache-->>Probe: throw UnsupportedGraph (replayed)
    end
    Probe-->>v2: "" (supported) OR reason string
    v2-->>Caller: NVTE_F16_arbitrary_seqlen / NVTE_FP8 / NVTE_No_Backend + message

    Note over Caller,cuDNN: Execution path (nvte_fused_attn_fwd_v2)
    Caller->>v2: "re-probe with check_for_forward_support=true"
    v2->>Cache: get_or_build_cached_graph (same key → HIT)
    Cache-->>v2: cached entry
    v2->>Cache: ensure_plans_built() [once per entry]
    Cache->>cuDNN: build_plans()
    cuDNN-->>Cache: compiled kernel
    v2->>cuDNN: graph.execute(runtime tensors)
Loading

Reviews (42): Last reviewed commit: "[pre-commit.ci] auto fixes from pre-comm..." | 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 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
@cyanguwa

Copy link
Copy Markdown
Collaborator Author

/te-ci L1

t_q,
num_tokens_kv * cp_size * s_kv // max_seqlen_kv if max_seqlen_kv else 0,
)
for s_kv in dict.fromkeys([s_kv_chunk, max_seqlen_kv])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this varies for every rank, would it make a difference?

@KshitijLakhani

KshitijLakhani commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator
  • The addition of NVTE_DEBUG / NVTE_DEBUG_LEVEL for JAX (parity with PyTorch) bridges a long time gap. Thank you !

  • I am very curious if you've run any experiments with NVTE_FUSED_ATTN_CACHE_DEBUG - what sort of numbers/metrics have you seen ?

    • For e.g. with the new API, when we query backend support, we try to build a graph which in theory seems more expensive as compared to the C++ decision tree query we had in the older API, however, we'd expect the first fused attn execution call made to have a cache hit and hence in theory be faster than the older API.
      I'd expect fused attn backend supported cases (where a graph has to be built anyways) to get faster, but the unsupported cases to get slower. Would be interesting to know by how much though.

    • Another interesting case would be if we have THD segments with a very small standard deviation packed together thereby resulting in them being in the same "bucket" hence triggering a cache hit ad no new graph creation. Comparing this to a larger deviation in the THD packed segment sizes which would trigger repetitive graph creation.

    • It seems like in the instrumentation code there's no way for the user to know if the defensive call for getting the backend in the fwd pass fused attn in the execution phase of the model has a cache miss resulting in creating a new graph and new cache entry thereby losing any benefits of the graph created in the query phase. It could be useful information in the future, so that if the user is doing something incorrectly (inadvertently), maybe changing/passing any tensor params between querying and fused attn execution at least we can inform them via debug logging as I'd image that penalty won't be inexpensive

    • Do we log the device ID when NVTE_FUSED_ATTN_CACHE_DEBUG is enabled ? Maybe I missed i but if not, maybe we should so that the user knows which device the debug info corresponds to ?

@cyanguwa cyanguwa added 2.19 and removed 2.18 labels Jul 29, 2026
"<b>Note</b>\n",
" \n",
"Environment variables <code>NVTE_FLASH_ATTN</code>, <code>NVTE_UNFUSED_ATTN</code>, <code>NVTE_FUSED_ATTN_BACKEND</code>, and <code>NVTE_FUSED_ATTN_USE_FAv2_BWD</code> are supported in PyTorch. <code>NVTE_FUSED_ATTN</code> and <code>NVTE_ALLOW_NONDETERMINISTIC_ALGO</code> are supported in both PyTorch and JAX.\n",
"Environment variables <code>NVTE_FLASH_ATTN</code>, <code>NVTE_UNFUSED_ATTN</code>, and <code>NVTE_FUSED_ATTN_USE_FAv2_BWD</code> are supported in PyTorch. <code>NVTE_FUSED_ATTN</code> and <code>NVTE_ALLOW_NONDETERMINISTIC_ALGO</code> are supported in both PyTorch and JAX.\n",

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.

No NVTE_FUSED_ATTN_USE_FAv2_BWD support in JAX ?

Comment on lines +134 to +135
// Restrict each direction's key to the fields its graph actually consumes, so
// no redundant graphs are built and no cache misses either

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.

That's a good change to avoid redundant graphs if fwd only

" bytes)");
NVTE_CHECK(buf != nullptr, "Invalid buffer (got NULL)");

auto &cfg = *get_fused_attn_config_mutable(config);

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: for the setter and getter (nvte_set_fused_attn_config_attribute and nvte_get_fused_attn_config_attribute) - do you think in the future it might make sense to make this a bulk API in which one can request to set/get multiple attributes in in one call? Which would then reduce multiple calls to get_fused_attn_config_mutable() and get_fused_attn_config ?

int64_t window_size_right, bool return_max_logit, bool cuda_graph, bool deterministic) {
namespace {

// The per-thread storage for the diagnostic string; it's re-used (cleared + re-populated)

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:

Suggested change
// The per-thread storage for the diagnostic string; it's re-used (cleared + re-populated)
// The per-thread storage for the diagnostic string; it is re-used (cleared + re-populated)

// Only used when THD format is requested.
cudnnHandle_t handle = cudnnExecutionPlanManager::Instance().GetHandle();
const NVTE_QKV_Format qkv_format = nvte_get_qkv_format(cfg.qkv_layout);
const NVTE_QKV_Layout_Group layout_group = nvte_get_qkv_layout_group(cfg.qkv_layout);

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: Although having the explicit type like NVTE_QKV_Format and NVTE_QKV_Layout_Group is almost always the better option, consider using auto instead ? Especially since the var names are descriptive enough to understand the data type ?
It could help reduce code verbosity

cache_hit = (it != cache.end());
if (cache_hit) cached_graph = it->second;
}
graph_cache_debug::record_cache_lookup("fwd", cache_hit, cfg);

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.

I think it makes sense to have the recording outside the mutex's scope so that the mutex is not held when performing (slow) I/o ops, however, this would mean that the recorded logs for the cache ops may not reflect exact wall clock ordering. I think it is vital to mention this in the docs/code if not already so that the users are aware of this

bool is_ragged_q = (q_format == NVTE_QKV_Format::NVTE_THD);
bool is_ragged_kv = (kv_format == NVTE_QKV_Format::NVTE_THD);
bool is_ragged_q = cfg.is_ragged_q;
bool is_ragged_kv = cfg.is_ragged_kv;

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.

const ?

Comment on lines +654 to +655
bool is_causal_bottom_right = cfg.is_causal_bottom_right;
bool is_padding = cfg.is_padding;

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.

const ?

graph_cfg.derive();

size_t workspace_size = 0;
try {

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.

Thanks for adding the try catch in here. I was hoping for it while reviewing the code for fused_attn_arbitrary_seqlen_fwd_impl()

static thread_local CacheType sdpa_f16_bprop_cache;
using CacheType = std::map<FusedAttnConfig, graph_and_tensors>;
static CacheType sdpa_f16_bprop_cache;
static std::mutex sdpa_f16_bprop_cache_mutex;

@KshitijLakhani KshitijLakhani Jul 29, 2026

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.

maybe nit and overthinking on my part but would it better to reverse the order of creation ?
Because the order of destruction will be reverse of creation, we'd like to first destroy the resource (cache) and then the mutex guarding it, right ?

It may also be beneficial (to make it mistake proof) if we tie these together in a struct with the suggested new ordering above so that if ever anyone else touches the cache and mutex code in the future they do not need to worry about the individual object ordering (destroying the struct object is all they'd care about and we can take care of the reordering in the struct object)

@cyanguwa

cyanguwa commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Note to self: integrate these changes to this PR, thanks to @sudhakarsingh27.
https://github.com/cyanguwa/TransformerEngine/pull/5/changes

cc #3092

PR 2964 addresses the following points from the above PR:
Mixed THD incorrectly passes the C++ OR condition
Unsupported SM80 + old cuDNN reaches runtime and crashes
Clear fallback/debug reason

These points aren't fully addressed in 2964:
Enable valid SM80 THD execution with cuDNN 9.18.1+
Correct SM8x Stats/LSE/Max shapes
Mixed-layout Python prefilters

@KshitijLakhani
KshitijLakhani self-requested a review August 1, 2026 00:17
void* devActualSeqlenKV = static_cast<int8_t*>(devActualSeqlenQ) + b * sizeof(int32_t);
cu_seqlens_to_actual_seqlens<<<grid, nthreads_per_block, 0, stream>>>(
b, b, static_cast<const int32_t*>(devPtrcuSeqlensQ), // TODO(pass max_b)
b, b, static_cast<const int32_t*>(devPtrcuSeqlensQ), // TODO(pass bucketed_batch_size)

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.

Is this TODO for the future ? if yes,

Suggested change
b, b, static_cast<const int32_t*>(devPtrcuSeqlensQ), // TODO(pass bucketed_batch_size)
b, b, static_cast<const int32_t*>(devPtrcuSeqlensQ), // TODO(<GH username>): pass bucketed_batch_size

Comment on lines 552 to 559
bool is_bias = (bias_type == NVTE_Bias_Type::NVTE_POST_SCALE_BIAS);
bool is_alibi = (bias_type == NVTE_Bias_Type::NVTE_ALIBI);
bool is_causal = ((mask_type == NVTE_Mask_Type::NVTE_CAUSAL_MASK) ||
(mask_type == NVTE_Mask_Type::NVTE_PADDING_CAUSAL_MASK));
bool is_padding = ((mask_type == NVTE_Mask_Type::NVTE_PADDING_MASK) ||
(mask_type == NVTE_Mask_Type::NVTE_PADDING_CAUSAL_MASK));
bool is_causal_bottom_right = cfg.is_causal_bottom_right;
bool is_padding = cfg.is_padding;
bool is_dropout = (dropout_probability != 0.0f);
bool is_softmax_offset = (softmax_type != NVTE_Softmax_Type::NVTE_VANILLA_SOFTMAX);

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.

const ?

Comment on lines 562 to 569

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.

outide this PR's scope but if possible: const ?

Comment on lines 739 to 745

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.

const ?

graph_cache_debug::record_build("bwd");
// Lock the insert. If another thread inserted a graph for the same key while we were building,
// use their graph (it's the same as ours) and discard our graph.
{

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.

Maybe we already have this, but if not, it might be useful to add a cache specific test especially since that's a pretty imp component of graphing in TE attention.

Add a single-thread test that queries one config twice, then executes fused attention with matching params.

Assert the first query causes one miss/build, while the second query and execution are hits with no additional build. Maybe then modify one graph-defining field and assert exactly one new miss/build—this directly catches broken key normalization and unintended recompilation.

try {
fused_attn::fused_attn_fp8_fwd_impl(
graph_cfg,
/*devPtrQ=*/nullptr, /*devPtrK=*/nullptr, /*devPtrV=*/nullptr,

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.

thanks for adding the args as comments here


// More readable, shorter thread IDs (0, 1, 2, ...).
inline unsigned thread_seq_id() {
static std::atomic<unsigned> next{0};

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.

This got me thinking about logging device id a bit more

IIUC, the cache key includes device ID, but the debug events omit it right ?.
Could we log descriptor.device_id and pass the normalized descriptor to the recorder? This would make multi-GPU cache behavior diagnosable.

} // namespace fused_attn
} // namespace transformer_engine

#endif // TRANSFORMER_ENGINE_COMMON_FUSED_ATTN_GRAPH_CACHE_DEBUG_H_

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.

If it is not too much effort, would be nice to see an example of what this logs looks like for the different diagnostics enabled. This is a good to have only so feel free to skip over

template <typename T>
FusedAttnFwdParamsWrapper &set_attr(NVTEFusedAttnFwdParamsAttribute attr, T val) noexcept {
nvte_set_fused_attn_fwd_params_attribute(params_, attr, &val, sizeof(val));
return *this;

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.

Chaining is a good addition to this 👍

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

cyanguwa commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

Comment on lines +229 to +241
namespace {

// The per-thread storage for the diagnostic string; it's re-used (cleared + re-populated)
// on every call to nvte_get_fused_attn_backend_v2 on the same thread.
thread_local std::string fused_attn_backend_message_buffer;

// Stash `reason` in the thread-local buffer and, if the caller asked for a diagnostic,
// publish a NUL-terminated pointer to it via `*message`. Safe to call with `message == nullptr`.
void set_message(const char **message, std::string reason) {
if (message == nullptr) return;
fused_attn_backend_message_buffer = std::move(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.

P1 Returned message pointer is invalidated by any subsequent same-thread backend call

*message is set to .c_str() of a thread_local std::string. Any call to nvte_get_fused_attn_backend_v2 on the same thread (including the internal calls from nvte_fused_attn_fwd_v2 and nvte_fused_attn_bwd_v2) will std::move() a new string into fused_attn_backend_message_buffer, destroying the previous string object and making the pointer dangle. The internal probe calls currently pass nullptr so the buffer isn't clobbered by them, but any caller that stores the returned const char* and then makes a subsequent backend call will read freed memory. The API contract (e.g. "copy this string before calling anything else") should be clearly documented, or the v2 signature should return std::string instead of const char**.

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
…run, still build plans in probes

Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
…be, dry-run, still build plans in probes"

This reverts commit 8fdd81d.

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>
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
…ad and not modify cfg

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

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

Comment thread transformer_engine/common/fused_attn/graph_cache.h
Signed-off-by: Charlene Yang <8636796+cyanguwa@users.noreply.github.com>
@cyanguwa

Copy link
Copy Markdown
Collaborator Author

/te-ci L0 L1 L2 L3

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