Skip to content

DSA: fix 576-wide d_sink, test/document top-k index semantics, B300 benchmarks#405

Open
vedaanta wants to merge 3 commits into
NVIDIA:developfrom
vedaanta:vagarwalla/dsa-api-layout
Open

DSA: fix 576-wide d_sink, test/document top-k index semantics, B300 benchmarks#405
vedaanta wants to merge 3 commits into
NVIDIA:developfrom
vedaanta:vagarwalla/dsa-api-layout

Conversation

@vedaanta

@vedaanta vedaanta commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Three small, independent improvements to the DeepSeek Sparse Attention backward — no API changes; the existing cudnn.DSA.sparse_attention_backward_wrapper surface is untouched.

1. Bug fix: zero d_sink for the 576-wide latent

The kernel's sum_dSink postprocess only runs when D_qk == D_v, so the 576-wide MLA latent (512 value dims + 64 RoPE dims) silently returned a zero sink gradient. Fixed with a closed-form epilogue in _interface_sm100.py (d_sink_h = -sum_t sigmoid(sink_h - lse[t,h]) * (out . dout)[t,h]); the failing test_DSA_sparse_attention_backward_wrapper[False-512-576-512-32-...] now passes.

Known pre-existing issue (reproducible on clean develop, unchanged by this PR): [True-512-576-512-32-...] fails on dq accuracy when topk_length is combined with D_qk=576; kernel-level fix tracked separately.

2. Test + docs: top-k index semantics

The kernel supports two index conventions that were undocumented and untested: -1 sentinels without topk_length (the FlashMLA convention) vs the compact-prefix fast path with it. Added a parity test that the same valid index set produces the same gradients through both compiled variants, and documented the semantics in docs/fe-oss-apis/dsa.md — including that topk_length values must be >= 1 and the inert-row (padding) recipe: topk_length=1, any valid index, and a zeroed dout row (verified to produce exactly-zero dq rows and no dkv/d_sink contribution).

3. Benchmarks: B300 results with MMA-utilization analysis

Wall-clock sweeps for d_qk=512 (topk 512/2048) and d_qk=576 (topk 2048) across seqlens 1k–32k, plus Nsight Compute tensor-pipe speed-of-light numbers for the main backward kernel. Highlights: MMA utilization is set by topk (44% at topk=2048 / d512, ~37% at d576), is sequence-length invariant, the kernel is memory-pipe-bound (top-k gather + fp32 dKV accumulation), and the seqlen-32k dip is the KV working set exceeding L2.

Testing (SM100-class GPU)

test_DSA_sparse_attention_backward.py: 4 passed, 1 skipped, 1 pre-existing failure (the dq issue above, identical on clean develop).

Summary by CodeRabbit

  • Documentation

    • Clarified sparse attention backward top-k index rules, including sentinel values, valid index ranges, and padding behavior.
    • Added B300 benchmark results, tensor-pipe utilization metrics, and performance observations.
  • Bug Fixes

    • Corrected sink-gradient calculation for mismatched query and value head dimensions.
  • Tests

    • Added coverage verifying equivalent backward results for sentinel-padded and explicit top-k length inputs.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The changes document B300 sparse-attention benchmarks, clarify backward top-k index semantics, compute d_sink for mismatched head dimensions, and test equivalence between sentinel-based and topk_length-based backward inputs.

Changes

DSA backward behavior

Layer / File(s) Summary
Backward contract and validation
docs/fe-oss-apis/dsa.md, test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py
Documents topk_idxs sentinel and prefix rules, inert-row handling, and validates equivalent gradients for both input modes.
Mismatched-dimension sink gradient
python/cudnn/deepseek_sparse_attention/sparse_attention_backward/_interface_sm100.py
Recomputes and stores d_sink when head_dim differs from head_dim_v.

B300 benchmark documentation

Layer / File(s) Summary
B300 results and profiling
benchmark/dsa/README.md
Adds B300 backward runtime and throughput tables plus Tensor-pipe utilization metrics and observations.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the three main changes in the pull request.
Description check ✅ Passed The description covers summary, why, compatibility, and testing; only some template fields are left implicit.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

vedaanta and others added 3 commits July 17, 2026 14:45
The kernel's sum_dSink postprocess only runs when D_qk == D_v, so the
576-wide MLA latent (512 value dims + 64 RoPE dims) silently returned a
zero sink gradient. Compute it with a closed-form epilogue from tensors
already at hand:

    d_sink_h = -sum_t sigmoid(sink_h - lse[t,h]) * (out . dout)[t,h]

which is the derivative of the sink-normalized softmax through the
KV-only LSE. Precision matches the kernel's own D_qk == D_v sum_dSink
path (both consume 16-bit out/dout).

Fixes the failing
test_DSA_sparse_attention_backward_wrapper[False-512-576-512-32].

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The backward kernel supports two top-k index conventions that were
undocumented and untested: without topk_length, invalid topk_idxs
entries are -1 sentinels (the FlashMLA convention, zero-filled rows);
with topk_length, valid indices must be a compact prefix and per-row
sentinel checks are skipped. Add a parity test that the same valid index
set produces the same gradients through both compiled variants, and
document the semantics — including that topk_length values must be >= 1
and the inert-row (padding) recipe: topk_length=1, any valid index, and
a zeroed dout row.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…alysis

Wall-clock sweeps for d_qk=512 (topk 512/2048) and d_qk=576 (topk 2048)
across seqlens 1k-32k, plus Nsight Compute speed-of-light numbers for
the main backward kernel. Key observations recorded in the README: MMA
utilization is set by topk (per-CTA inner-loop depth), not sequence
length; the kernel is memory-pipe-bound (top-k gather + fp32 dKV
accumulation); the seqlen-32k dip is the KV working set exceeding L2;
the 576-wide latent runs ~7 SOL points below 512 due to the
non-128-aligned RoPE tail tiles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vedaanta
vedaanta force-pushed the vagarwalla/dsa-api-layout branch from acb10b1 to 336417e Compare July 17, 2026 21:45
@vedaanta vedaanta changed the title DSA: sdpa-style cudnn.dsa API layout — fwd/bwd wrappers, torch/JAX ops, benchmarks DSA: fix 576-wide d_sink, test/document top-k index semantics, B300 benchmarks Jul 17, 2026
@vedaanta
vedaanta marked this pull request as ready for review July 17, 2026 22:54
@vedaanta
vedaanta requested a review from Anerudhan July 17, 2026 22:54

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py (1)

236-249: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the documented inert-row contract.

The test only compares valid-prefix and sentinel-padded inputs. Add a zeroed-dout row with topk_length = 1, then assert that its dq is zero and changing its valid index does not alter dkv or d_sink.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py` around lines
236 - 249, Extend the test around ref_sparse_attention_forward to include a row
with topk_length equal to 1 and an all-zero dout row. Assert that this row
produces zero dq, then change its single valid top-k index and verify dkv and
d_sink remain unchanged; preserve the existing valid-prefix and sentinel
comparisons.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@benchmark/dsa/README.md`:
- Around line 154-155: Update the comparison text in the README to qualify the
performance gap by sequence length: state that the 576-wide latent trails the
512-wide case by approximately 7 SOL points through 16k, widening to
approximately 10 points at 32k.

In `@docs/fe-oss-apis/dsa.md`:
- Around line 122-130: Update the “Top-k index semantics” documentation to
define every valid topk_idxs entry as satisfying 0 <= topk_idxs < total_S_kv.
Clarify that negative entries and entries greater than or equal to total_S_kv
are invalid, while preserving the existing sentinel and topk_length rules.

In `@test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py`:
- Around line 251-257: Restrict the exception handling around the two
DSA.sparse_attention_backward_wrapper calls to the specific known unsupported
condition, such as ImportError, ValueError, or NotImplementedError, and remove
RuntimeError from the skip list. Unexpected runtime failures from compilation,
launch, validation, or implementation must propagate and fail the test.

---

Nitpick comments:
In `@test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py`:
- Around line 236-249: Extend the test around ref_sparse_attention_forward to
include a row with topk_length equal to 1 and an all-zero dout row. Assert that
this row produces zero dq, then change its single valid top-k index and verify
dkv and d_sink remain unchanged; preserve the existing valid-prefix and sentinel
comparisons.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4f4b205d-9d8d-4126-a7ca-a901b73ca57d

📥 Commits

Reviewing files that changed from the base of the PR and between 3041f3e and 336417e.

📒 Files selected for processing (4)
  • benchmark/dsa/README.md
  • docs/fe-oss-apis/dsa.md
  • python/cudnn/deepseek_sparse_attention/sparse_attention_backward/_interface_sm100.py
  • test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py

Comment thread benchmark/dsa/README.md
Comment on lines +154 to +155
- The 576-wide latent runs ~7 SOL points below 512 at the same top-k: the
non-128-aligned 64-dim RoPE tail takes separate tail-tile MMAs.

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.

🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick win

Qualify the “~7 SOL points” comparison.

The 32k row is 44.3% for d_qk=512 versus 34.2% for d_qk=576, a 10.1-point gap. State that the gap is ~7 points through 16k and widens to ~10 points at 32k.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmark/dsa/README.md` around lines 154 - 155, Update the comparison text
in the README to qualify the performance gap by sequence length: state that the
576-wide latent trails the 512-wide case by approximately 7 SOL points through
16k, widening to approximately 10 points at 32k.

Comment thread docs/fe-oss-apis/dsa.md
Comment on lines +122 to +130
- **Top-k index semantics** — without `topk_length`, invalid `topk_idxs`
entries must be `-1` sentinels (the FlashMLA convention). With
`topk_length`, the first `topk_length[i]` entries of each row must all be
valid indices — this compact-prefix fast path skips per-row sentinel
checks, so `-1` entries inside the prefix are not allowed. `topk_length`
values must be `>= 1`; to make a query row inert (e.g. padding rows for
fixed-shape capture), use `topk_length = 1` with any valid index and a
zeroed `dout` row, which yields an exactly-zero `dq` row and no `dkv` /
`d_sink` contribution.

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map the relevant files first
git ls-files docs/fe-oss-apis/dsa.md test/python/fe_api/dsa/dsa_reference.py

# Show the relevant section of the docs with line numbers
sed -n '100,150p' docs/fe-oss-apis/dsa.md | cat -n

# Inspect the reference helper for top-k index handling
rg -n "topk|total_S_kv|invalid|sentinel|topk_length" test/python/fe_api/dsa/dsa_reference.py -n -A 4 -B 4

# If needed, show the surrounding implementation
python3 - <<'PY'
from pathlib import Path
p = Path('test/python/fe_api/dsa/dsa_reference.py')
text = p.read_text()
for needle in ['topk_length', 'total_S_kv', 'topk_idxs']:
    idx = text.find(needle)
    print(f"\n== {needle} @ {idx} ==")
    if idx != -1:
        start = max(0, idx - 500)
        end = min(len(text), idx + 1800)
        print(text[start:end])
PY

Repository: NVIDIA/cudnn-frontend

Length of output: 18739


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search the docs for any other definition of top-k bounds or topk_length semantics
rg -n "topk_length|topk_max|total_S_kv|valid indices|sentinel|FlashMLA" docs/fe-oss-apis/dsa.md -n -A 3 -B 3

# Search the reference helper for any explicit assertion on topk_length bounds
rg -n "assert|clamp|min=1|max=|topk_length" test/python/fe_api/dsa/dsa_reference.py -n -A 3 -B 3

Repository: NVIDIA/cudnn-frontend

Length of output: 13212


Document the top-k index range. “Valid index” should explicitly mean 0 <= topk_idxs < total_S_kv; the current wording leaves the accepted range implicit, even though the helper treats negative and >= total_S_kv entries as invalid.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fe-oss-apis/dsa.md` around lines 122 - 130, Update the “Top-k index
semantics” documentation to define every valid topk_idxs entry as satisfying 0
<= topk_idxs < total_S_kv. Clarify that negative entries and entries greater
than or equal to total_S_kv are invalid, while preserving the existing sentinel
and topk_length rules.

Comment on lines +251 to +257
try:
from cudnn import DSA

r_len = DSA.sparse_attention_backward_wrapper(q, kv, out, dout, lse, attn_sink, topk_idxs, topk_length=topk_length)
r_sen = DSA.sparse_attention_backward_wrapper(q, kv, out, dout, lse, attn_sink, idx_sentinel, topk_length=None)
except (ImportError, ValueError, NotImplementedError, RuntimeError) as e:
pytest.skip(f"Unsupported testcase: {e}")

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not skip unexpected RuntimeErrors.

This catches kernel compilation, launch, validation, and implementation regressions as “unsupported testcase” skips. Restrict the skip to a known unsupported condition and let unexpected runtime failures fail the test; otherwise this parity test cannot reliably guard the compiled variants.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/python/fe_api/dsa/test_DSA_sparse_attention_backward.py` around lines
251 - 257, Restrict the exception handling around the two
DSA.sparse_attention_backward_wrapper calls to the specific known unsupported
condition, such as ImportError, ValueError, or NotImplementedError, and remove
RuntimeError from the skip list. Unexpected runtime failures from compilation,
launch, validation, or implementation must propagate and fail the test.

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