Skip to content

fix(dsa): adapt cuDNN DSA call sites to cuDNN frontend >= 1.26 defaults - #8539

Draft
buptzyb wants to merge 1 commit into
NVIDIA:mainfrom
buptzyb:codex/fix/dsa-cudnn-fe126-defaults-main
Draft

buptzyb wants to merge 1 commit into
NVIDIA:mainfrom
buptzyb:codex/fix/dsa-cudnn-fe126-defaults-main

Conversation

@buptzyb

@buptzyb buptzyb commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Main counterpart of #8534.

cuDNN frontend 1.26 (NVIDIA/cudnn-frontend#316) changed two defaults that the DSA cuDNN call sites rely on. main pins FE 1.29.1, so both issues are live on main. Both are silent: no error, just wrong gradients or a wrong top-k selection.

1. Sparse indexer backward: top-k ids are treated as per-batch local

Since FE 1.26, indexer_backward_wrapper treats top-k ids as local to each batch unless topk_indices_global=True. The sparse-indexer-loss path passes global ids (b * S_k + local). With BSHD and more than one sequence per micro-batch, every batch after the first loses its sparse indexer gradient on FE 1.29.

The wrapper is now called through _call_sparse_indexer_backward. It sets topk_indices_global=True whenever the installed wrapper accepts that parameter (or takes **kwargs), and keeps the old call otherwise.

2. Causal offsets: the default alignment moved from bottom-right to top-left

When q_causal_offsets is not given, FE <= 1.25 aligns the causal mask bottom-right (seqlen_k * ratio - seqlen_q), and FE >= 1.26 aligns it top-left (offset 0). On main, three callers relied on the old default:

With FE 1.26 or newer, the bottom-right offsets are now passed explicitly: a uniform offset for BSHD calls, and per-segment offsets (segment_k_lengths - segment_q_lengths) for the THD call. Older frontends and zero-offset calls keep their exact call and kernel compile key. A top-left frontend whose wrapper lacks q_causal_offsets fails closed.

Differences from the dev PR

  • main has no utils.filter_kwargs_for_callable, so the wrapper signature is probed locally (_wrapper_accepts_kwarg).
  • The THD call site above (item 2, second bullet) is fixed here; on dev it is already correct.
  • The tests call main's _indexer_topk_single_packed_cp_segments signature.

Test plan

  • CPU tests in test_dsa_cudnn_fe_compat.py, 23 passed. They use fakes of both frontend generations (dsa_cudnn_fe_fakes.py) and cover:
    • version parsing and the 1.25/1.26 gate;
    • fail-closed behavior;
    • the cropped segment scorer and the multi-sequence packed CP THD scorer selecting identical top-k under either default;
    • a negative control for each scorer: with the offsets removed on FE 1.31, the selection changes.
  • In test_dsa_native_parity.py, the fake indexer_backward_wrapper used by the sparse-loss tests requires topk_indices_global=True.
  • CPU: test_dsa_native_parity.py and test_attention_variant_dsa.py give the same results on this branch and on main. The 3 failures and 34 errors are identical on both and come from the CPU environment.
  • GPU check on GB300 with cuDNN FE 1.29.1, main's pin. One standalone script ran against main and against this branch, calling the affected functions directly:
    • Sparse indexer backward, BSHD micro-batch 2. The reference is the same rows run as two b=1 calls, scaled for the row-mean loss.

      • main: batch 1 gets all-zero dQ/dW/dK.
      • This branch: both batches match (dK relative error ≤ 8e-6).
    • Top-k recall against an fp32 torch top-k over each row's absolute causal keys (mean / min):

      Scorer main This branch
      Single-sequence segment scorer, CP=1 0.706 / 0.266 1.000 / 1.000
      Single-sequence segment scorer, CP=2, rank 0 0.587 / 0.086 1.000 / 1.000
      Single-sequence segment scorer, CP=2, rank 1 0.332 / 0.109 1.000 / 1.000
      Multi-sequence packed CP THD, CP=2, rank 0 (documents of 1024 and 2048 tokens) 0.579 / 0.039 1.000 / 1.000
      Multi-sequence packed CP THD, CP=2, rank 1 0.289 / 0.055 1.000 / 1.000

      On main, the selected keys stay inside each row's bounds, but tens of thousands of them score below the reference k-th score: top-k ran over a top-left-masked key range.

  • CHECK_ONLY=true BASE_REF=main tools/autoformat.sh (black 26.3.0, isort, pylint, ruff)

Overlap with open PRs

🤖 Generated with Claude Code

cuDNN frontend 1.26 (NVIDIA/cudnn-frontend#316) changed two defaults that
the DSA cuDNN call sites rely on.

Sparse indexer backward: indexer_backward_wrapper treats top-k ids as
per-batch local unless topk_indices_global=True. MCore's sparse-loss path
has always materialized global B * S_k + local ids, so BSHD calls with
more than one sequence per micro-batch lost or misplaced the sparse
indexer gradient of every batch after the first (THD, one sequence per
batch, is unaffected). Call the wrapper through
_call_sparse_indexer_backward, which sets topk_indices_global=True
whenever the installed wrapper names the parameter (or takes **kwargs)
and keeps the historical call otherwise.

Dense causal offsets: the dense indexer forward, dense attention score
recompute and dense indexer backward kernels gained q_causal_offsets and,
when it is not given, use a zero (top-left) q_causal_offset instead of
the bottom-right alignment (seqlen_k * ratio - seqlen_q): FE <= 1.25
aligns bottom-right, FE >= 1.26 top-left.

MCore's BSHD call sites pass no offsets and were written against the
bottom-right contract. The single-sequence packed CP scorer in particular
crops the key prefix of each query chunk so that bottom-right masking
reproduces each row's absolute causal length; on a top-left frontend the
same call masks top-left and the top-k reads -inf columns inside the
row's length, silently changing the selection.

Pass the bottom-right offset explicitly when the installed frontend is
1.26 or newer and the offset is non-zero. Older frontends and square
(zero-offset) calls keep their exact call and kernel compile key. A
top-left frontend whose wrapper lacks q_causal_offsets fails closed.

Add CPU fakes of both frontend generations (dsa_cudnn_fe_fakes.py:
BottomRightDefaultDSA for FE <= 1.25, Fe131DSA) and tests that the
cropped segment scorer selects identical top-k on either default; the
offset tests cover the 1.25 / 1.26 boundary. The native parity tests
install fakes that model the frontend <= 1.25 wrappers, so while a fake
namespace is installed they now report frontend 1.21 instead of the
installed cudnn version; on a host with FE >= 1.26 the offset gate would
otherwise treat those fakes as a top-left frontend and fail four tests.
The real namespace keeps reporting the installed version, so the
real-kernel tests still exercise the top-left path.

The fake indexer_backward_wrapper of the five sparse-loss tests in
test_dsa_native_parity.py (selected scores, masked query rows, TP
reduction, batch-major ids for batched KV, sequence chunking) now takes
a required topk_indices_global argument and asserts it is True, so they
fail without the sparse-backward fix; the batched-KV test is the BSHD
micro-batch > 1 case.

Main port. Main has neither dev's DSA pre-attention scorer dispatch (NVIDIA#6206)
nor its utils.filter_kwargs_for_callable or row-limit warning wrapper, so:
- the cuDNN wrapper signature is probed locally (_wrapper_accepts_kwarg);
- the multi-sequence packed CP THD scorer (_indexer_topk_multi_packed_cp_thd)
  gets per-segment bottom-right offsets on FE >= 1.26. On dev, NVIDIA#6206 already
  passes these offsets; on main this varlen call relied on the FE <= 1.25
  default, so packed THD with CP > 1 selected from top-left-masked key
  prefixes on main's FE 1.29.1 pin. Tests cover it across both frontend
  defaults, with a negative control;
- the tests call main's _indexer_topk_single_packed_cp_segments signature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Robin Zhang <robinz@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Oct 27, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

This branch has not been deployed

No deployments
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.

1 participant