Skip to content

frost(sdpa): optimize and enhance sm120 sdpa forward fp8 kernel - #562

Merged
Aneureka merged 4 commits into
NVIDIA:developfrom
Aneureka:haobing/frost/sm120_sdpa_fp8_cleanup
Aug 13, 2026
Merged

frost(sdpa): optimize and enhance sm120 sdpa forward fp8 kernel#562
Aneureka merged 4 commits into
NVIDIA:developfrom
Aneureka:haobing/frost/sm120_sdpa_fp8_cleanup

Conversation

@Aneureka

@Aneureka Aneureka commented Aug 12, 2026

Copy link
Copy Markdown
Member

Before submitting

  • I agree to license this contribution under the terms of LICENSE.txt.
  • I ran pre-commit run and committed any formatting changes.
  • I added GitHub labels: one cat-*, one or more mod-*, and one orig-* (see label list).

Affected area

  • FE OSS kernels or CuTeDSL

Summary

  • Kernel optimizations (numerics-preserving, ABI unchanged)
    • Skip the O-accumulator rescale when the online-softmax running max holds; old_scale lowers to a single predicated ex2 via inline PTX
    • Fold Scale_S into the exp2 bias instead of a per-element multiply before the e4m3 cast; row_sum un-folds once per row in the epilogue
    • Skip the masked first KV tile for dense non-causal shapes whose KV extent is a whole number of tiles (mask_steps = 0)
    • Restructure the epilogue Amax_O reduction: four independent accumulators instead of one serial fmax chain, and the row-validity gate applied once per row half instead of per element
  • Expand head-dim support
    • Widen from exact d128 to every multiple of 32 up to 256 (different from 16 for f16's engine, both aligning with 32B TMA swizzled chunk and MMA K dimension)
    • Support mixed D_QK and D_V, e.g., (192, 128)
  • Tests and small fixes
    • Regression guards for single-KV-tile shapes (previously untested)
    • Head-dim × mask-family cross coverage; suite grows 42 → 61 cases
    • Small cleanups in engines.py / heuristics.py / api_dsl.py (decline-reason ordering, fp8 SMEM sizing in the tile filter, stale docstrings, dead sink-epilogue code)

Why

To enhance frost sdpa forward FP8 engine for SM120.

Related issues

Related to #381.

API and compatibility impact

  • Public API: no signature changes. The SM120 FP8 engine now accepts D_QK/D_V in {32, 64, …, 256} independently (previously exactly 128)
  • Numerics: unchanged; O / Amax_S / Amax_O / LSE verified against fp32 references at scale_s ∈ {1, 448}

Performance

On RTX PRO 6000: ~2% speedup

Before:
Label                    ca        TF/s          us  tile
B=1 S=65536 D=128         0      702.27     50100.7  (128, 128)
B=1 S=65536 D=128 ca      1      690.14     25490.6  (128, 128)

After:
Label                    ca        TF/s          us  tile
B=1 S=65536 D=128         0      720.52     48832.1  (128, 128)
B=1 S=65536 D=128 ca      1      701.29     25085.5  (128, 128)

Testing

cd test/python
pytest -m "L0 or L1" sdpa/frost/test_sdpa_fwd_fp8_sm120.py    # 61 passed
pytest -m "L0 or L1" sdpa/frost/test_sdpa_fwd_dsl_sm120.py    # 62 passed, 8 skipped

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Expanded SM120 FP8 support for independent Q/K and value head dimensions from 32 through 256, including mixed-dimension configurations.
    • Improved tile selection and memory sizing for broader supported shapes.
    • Added more reliable single-tile and padded-tile execution scenarios.
  • Bug Fixes

    • Corrected FP8 scaling, normalization, empty-row handling, and output-amax tracking.
    • Clarified FP8 rescaling limits and validation behavior.
  • Tests

    • Added coverage for supported dimensions, masks, mixed shapes, and rejected configurations.

Signed-off-by: Haobin Guo <haobing@nvidia.com>
Signed-off-by: Haobin Guo <haobing@nvidia.com>
@Aneureka Aneureka self-assigned this Aug 12, 2026
@Aneureka Aneureka added orig-nv-eng Reported or requested by NVIDIA engineering. cat-perf-bug Performance regressions or cases where behavior is correct but too slow. cat-enhancements mod-frost labels Aug 12, 2026
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7b28e93e-6f44-4688-b626-6489e4a99022

📥 Commits

Reviewing files that changed from the base of the PR and between f022f8b and 1ff54f5.

📒 Files selected for processing (3)
  • python/cudnn/sdpa/fwd/api_dsl.py
  • python/cudnn/sdpa/fwd/engines.py
  • test/python/sdpa/frost/test_sdpa_fwd_fp8_sm120.py
💤 Files with no reviewable changes (2)
  • python/cudnn/sdpa/fwd/engines.py
  • python/cudnn/sdpa/fwd/api_dsl.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/python/sdpa/frost/test_sdpa_fwd_fp8_sm120.py

📝 Walkthrough

Walkthrough

SM120 FP8 support now accepts independent QK and value head dimensions from 32 through 256. Heuristics use FP8-specific storage sizes. The kernel updates Scale_S handling, empty-row behavior, sink validation, and Amax_O tracking. Tests cover expanded dimensions and masks.

Changes

SM120 FP8 head-dimension contracts

Layer / File(s) Summary
Head-dimension configuration and validation
python/cudnn/sdpa/fwd/config_sm120.py, python/cudnn/sdpa/fwd/api_dsl.py, python/cudnn/sdpa/fwd/engines.py
SM120 FP8 support uses 32-granularity tiles from 32 through 256. QK and V dimensions are validated independently.
Tile fitting and launch constraints
python/cudnn/sdpa/fwd/heuristics.py, python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py
Shared-memory filtering uses separate FP8 QKV and output element sizes. Kernel dimensions require multiples of 32, fixed full tiles skip masking, and non-None sinks are rejected.
Scaled softmax and output tracking
python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py, python/cudnn/sdpa/fwd/api_dsl.py
Softmax uses log2 Scale_S handling. Normalization compensates row sums, empty rows produce zero output and -inf LSE, and Amax_O tracks row halves separately. SM100 FP8 rescale documentation uses the 4.0 threshold.
Expanded validation and regression coverage
test/python/sdpa/frost/test_sdpa_fwd_fp8_sm120.py
Tests cover supported and rejected dimensions, independent QK/V shapes, mask combinations, single-KV-tile cases, and direct template-loader paths through dimension 256.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: ⚪ Minimal · up to 1ff54

This PR optimizes and expands SM120 FP8 SDPA support while preserving numerical behavior and API compatibility; the reported tests pass, and no actionable merge-blocking risk remains beyond normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant Test
  participant offers_engine
  participant Heuristics
  participant prefill_fp8_sm120
  Test->>offers_engine: submit QK and V head dimensions
  offers_engine->>Heuristics: filter FP8 tile pairs by SMEM capacity
  Heuristics-->>offers_engine: return fitting configurations
  offers_engine->>prefill_fp8_sm120: launch selected FP8 kernel
  prefill_fp8_sm120->>prefill_fp8_sm120: apply log2 Scale_S and normalize rows
  prefill_fp8_sm120-->>Test: return output, LSE, and Amax_O
Loading

Suggested reviewers: yangxu1990uiuc

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the SM120 SDPA forward FP8 kernel optimizations and enhancements.
Description check ✅ Passed The description covers all required template sections and provides scope, compatibility, performance, and exact test results.
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.

@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: 2

🧹 Nitpick comments (2)
python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py (2)

227-234: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Validate the head tiles in __init__. The docstring now requires head_tile_qk and head_tile_v to be multiples of 32 between 32 and 256. The constructor does not check this. _setup_attributes computes self.qk_d_frags = self.head_tile_qk // self.MMA_TILER[2] and self.pv_d_frags = self.head_tile_v // self.MMA_TILER[1], so a non-multiple silently truncates the fragment count and drops head columns. get_swizzle catches only the sub-32-byte case. The graph path is gated by check_support, but the direct template-loader path used by test_fp8_sm120_head_dim_tail_direct calls compile() with caller-supplied d_qk and d_v.

🛡️ Proposed validation
         if has_sink:
             raise ValueError("has_sink is not supported by the fp8 cell (Amax_S semantics)")
+        for label, tile in (("head_tile_qk", head_tile_qk), ("head_tile_v", head_tile_v)):
+            if tile % 32 != 0 or not 0 < tile <= 256:
+                raise ValueError(f"{label} must be a multiple of 32 between 32 and 256; got {tile}")
         if thd_varlen and (thd_batch < 1 or thd_max_sq < 1):
🤖 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 `@python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py` around lines 227 - 234,
Update the constructor validation in the fp8 cell’s __init__ to require
head_tile_qk and head_tile_v to be multiples of 32 within the inclusive range
32–256. Reject invalid values before _setup_attributes computes qk_d_frags and
pv_d_frags, while preserving the existing dtype, sink, and varlen validations.

1080-1081: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Document why mask_steps = 0 stays correct. With mask_steps = 0, phase 1 never runs, so no iteration receives is_first_kv_tile=True. Every tile then takes the "not first" path in online_softmax. That path stays correct only because row_max starts at -inf and row_sum starts at 0.0 (lines 1024-1025): old_scale evaluates to exp2(-inf) == 0, which zeroes the already-zero o_regs and the zero row_sum. The behavior is correct but implicit. A future change to the old_scale predicate would break it silently.

Add a short comment that records this dependency.

🤖 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 `@python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py` around lines 1080 - 1081,
In the mask_steps assignment within the prefill kernel, add a brief comment
documenting why zero is valid: phase 1 is skipped, all tiles use the non-first
online_softmax path, and its initial row_max/row_sum values make old_scale zero
while output and sum registers are already zero. Note that this relies on the
current old_scale predicate.
🤖 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 `@python/cudnn/sdpa/fwd/engines.py`:
- Around line 813-822: Correct the SM120 FP8 THD Stats documentation to reflect
the enforced head-major layout: in python/cudnn/sdpa/fwd/engines.py lines
813-822, state that head-major ragged Stats is served and token-major Stats is
f16-only; in test/python/sdpa/frost/test_sdpa_fwd_fp8_sm120.py lines 11-19,
replace token-major with head-major and no head-major with no token-major.
Update documentation only; implementation symbols such as
SdpaFwdDslSm120.check_support already enforce the correct behavior.

In `@python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py`:
- Around line 1061-1062: Update _execute_fp8 in api_dsl.py to validate the
scalar scale_s value after _scalar(scale_s) applies its default and before
launching the kernel; reject every scale_s <= 0 with the existing host-side
validation/error mechanism, while preserving valid positive values and the
default of 1.0.

---

Nitpick comments:
In `@python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py`:
- Around line 227-234: Update the constructor validation in the fp8 cell’s
__init__ to require head_tile_qk and head_tile_v to be multiples of 32 within
the inclusive range 32–256. Reject invalid values before _setup_attributes
computes qk_d_frags and pv_d_frags, while preserving the existing dtype, sink,
and varlen validations.
- Around line 1080-1081: In the mask_steps assignment within the prefill kernel,
add a brief comment documenting why zero is valid: phase 1 is skipped, all tiles
use the non-first online_softmax path, and its initial row_max/row_sum values
make old_scale zero while output and sum registers are already zero. Note that
this relies on the current old_scale predicate.
🪄 Autofix

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: c5209537-8f9b-4702-a66f-b5d23d1ebd3e

📥 Commits

Reviewing files that changed from the base of the PR and between 66efedf and f022f8b.

📒 Files selected for processing (6)
  • python/cudnn/sdpa/fwd/api_dsl.py
  • python/cudnn/sdpa/fwd/config_sm120.py
  • python/cudnn/sdpa/fwd/engines.py
  • python/cudnn/sdpa/fwd/heuristics.py
  • python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py
  • test/python/sdpa/frost/test_sdpa_fwd_fp8_sm120.py

Comment thread python/cudnn/sdpa/fwd/engines.py
Comment thread python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py
Signed-off-by: Haobin Guo <haobing@nvidia.com>
@Aneureka Aneureka changed the title frost(sdpa): optimize and cleanup sm120 sdpa forward fp8 kernel frost(sdpa): optimize and enhance sm120 sdpa forward fp8 kernel Aug 12, 2026
@Aneureka

Copy link
Copy Markdown
Member Author

@cudnn-ci-bot run frost

@cudnn-ci-bot

Copy link
Copy Markdown

🚀 Running mirror pipeline

Branch: cudnn-gh/pr-562-96800e1
Pipeline: 62323529
Targets: frost

Comment thread python/cudnn/sdpa/fwd/kernels/prefill_fp8_sm120.py
Signed-off-by: Haobin Guo <haobing@nvidia.com>
@Aneureka
Aneureka merged commit 5c178a0 into NVIDIA:develop Aug 13, 2026
1 check passed
@Anerudhan Anerudhan mentioned this pull request Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cat-enhancements cat-perf-bug Performance regressions or cases where behavior is correct but too slow. mod-frost orig-nv-eng Reported or requested by NVIDIA engineering.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants