sdpa fp8 sm100: fix THD O-descriptor row stride (latent) - #577
Conversation
The per-batch O-descriptor builder takes the DECLARED per-token element
stride of packed O (QH * d_v). The fp8 kernel passed CFG.TILE_O -
coincidentally correct only at QH == 1 with d_v == 128, otherwise landing
every batch >= 1's descriptor base at 1/QH of the true offset inside
earlier batches' O rows (region b receives batches {b, b+1}'s stores, the
last region goes stale). Latent today - the engine declares thd=False and
no API path drives fp8 THD - but real: reproduced and isolated on SM107
silicon via sentinel-V probes, with H=1xB=N and H=NxB=1 exact and H>1xB>1
corrupt. The f16 kernel has carried the same fix since its envelope work;
this aligns the fp8 kernel before any THD enablement lands on top.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe THD O-descriptor construction now uses ChangesTHD output descriptor
Estimated code review effort: 1 (Trivial) | ~5 minutes Mergeability Score: ⚪ Minimal · up to This localized fix corrects FP8 THD output descriptor addressing and is reported validated across the relevant test suites; no actionable merge-blocking risk remains beyond normal checks and review. Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
@cudnn-ci-bot run frost |
|
🚀 Running mirror pipeline Branch: cudnn-gh/pr-577-16e04d7 |
…ard (#579) Two follow-ups to #576: 1. #574 (has_lse specialization for the FP8/MXFP8 SM100 flavors) merged between #577 and #576, so the shared adapter now passes has_lse to the fp8 compile() while the SM107 sibling still had the pre-#574 signature - a TypeError on any Rubin fp8 compile at develop tip. This ports #574's fp8-kernel hunks onto the sibling verbatim (LSE None-specialization, compile(has_lse), the specialized epilogue), restoring signature parity with the shared call site. 2. Responding to review on #576: the 9-stage ring with BF16/FP16 O (~242 KiB) exceeds the STANDARD sm_10x 227 KiB per-CTA opt-in and is legal on GR100 only through the sm107 oversized-SMEM launch mode (function attribute 16), which the required internal cutlass-dsl toolchain enables for its sm_107a kernels - board-validated e4m3->bf16 across the full suite. Rather than rejecting output dtypes that demonstrably work on the target stack, a static import-time guard now accounts the geometry against the GR100 hardware budget so a future stage/width bump fails with a clear message instead of at launch. Validated: SM100 box 34 passed; SM107 board 34 passed (full fp8 e2e suite through the sibling, has_lse and no-lse populations both exercised via the suite's generate_stats matrix). Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… FP8 Two capability enablements for the per-tensor FP8 path: 1. THD/varlen execute. _execute_fp8_thd lands the f16 _execute_thd recipe on the FP8 ABI: packed [1,T,H,D] views (packed contract only, gated in check_support), the [seq_kv | cu_q | cu_k] metadata buffer, the per-sequence O-descriptor array, folded descales, in-kernel amax slots, and the head-major packed LSE [1,QH,T] (bound with has_lse=True). THD samples are declared at LOGICAL dims (B, H, S_max, D); buffers stay packed at execute. Serves every fp8-capable part: the SM100 kernel's O-descriptor stride fix (NVIDIA#577) makes cc10.0/10.3 correct - the irregular varlen e2e passes on SM100 silicon, the first Blackwell validation of this path - and cc10.7 routes to the sibling as usual. 2. d<=128 ENVELOPE for per-tensor FP8 on Rubin (the ViT d=72-in-80 contract's landing zone): compile() takes the ACTUAL head dims into the TMA descriptors while the tile box stays d128 - loads past d zero-fill (FP8 zero is exact), O stores OOB-clip, same semantics as the f16 kernels. Equal head dims, d % 16 == 0 (TMA 16-byte global-stride rule at BPE=1). Blackwell fp8 and MXFP8 keep the exact-d128 gate; the d_qk/d_v compile() parameters are added to BOTH fp8 kernels so the sibling signatures stay in lockstep with the shared adapter call site (the NVIDIA#574 lesson). Direct-adapter surface only; the engine row still declares thd=False and d128 - graph-route exposure is follow-up work. Validation: SM107 board 36 passed (d80 envelope e2e + irregular THD e2e + full fp8 suite); SM100 box 35 passed + 1 skip (envelope is Rubin-gated). Perf work for these shapes (the cga=1 short-sequence config) follows separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… FP8 Two capability enablements for the per-tensor FP8 path: 1. THD/varlen execute. _execute_fp8_thd lands the f16 _execute_thd recipe on the FP8 ABI: packed [1,T,H,D] views (packed contract only, gated in check_support), the [seq_kv | cu_q | cu_k] metadata buffer, the per-sequence O-descriptor array, folded descales, in-kernel amax slots, and the head-major packed LSE [1,QH,T] (bound with has_lse=True). THD samples are declared at LOGICAL dims (B, H, S_max, D); buffers stay packed at execute. Serves every fp8-capable part: the SM100 kernel's O-descriptor stride fix (NVIDIA#577) makes cc10.0/10.3 correct - the irregular varlen e2e passes on SM100 silicon, the first Blackwell validation of this path - and cc10.7 routes to the sibling as usual. 2. d<=128 ENVELOPE for per-tensor FP8 on Rubin (the ViT d=72-in-80 contract's landing zone): compile() takes the ACTUAL head dims into the TMA descriptors while the tile box stays d128 - loads past d zero-fill (FP8 zero is exact), O stores OOB-clip, same semantics as the f16 kernels. Equal head dims, d % 16 == 0 (TMA 16-byte global-stride rule at BPE=1). Blackwell fp8 and MXFP8 keep the exact-d128 gate; the d_qk/d_v compile() parameters are added to BOTH fp8 kernels so the sibling signatures stay in lockstep with the shared adapter call site (the NVIDIA#574 lesson). Direct-adapter surface only; the engine row still declares thd=False and d128 - graph-route exposure is follow-up work. Validation: SM107 board 36 passed (d80 envelope e2e + irregular THD e2e + full fp8 suite); SM100 box 35 passed + 1 skip (envelope is Rubin-gated). Perf work for these shapes (the cga=1 short-sequence config) follows separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Summary
One-line latent-bug fix in the SM100 per-tensor FP8 kernel's THD path: the per-batch O-descriptor builder takes the declared per-token element stride of packed O (
QH * d_v), but the fp8 kernel passedCFG.TILE_O— coincidentally correct only atQH == 1withd_v == 128, and otherwise landing every batch ≥ 1's descriptor base at1/QHof the true offset inside earlier batches' O rows (region b receives batches {b, b+1}'s stores; the last region goes stale). The f16 kernel has carried the correct form (o_tensor.stride[1]) since its envelope work — this aligns the fp8 clone.Why latent
The engine row declares
thd=Falseand no API path drives fp8 THD today, so no shipping configuration reaches the bug. It should land before any fp8 THD enablement does (see the companion SM107 enablement PR, whose sibling kernel copy inherits this fix).Evidence
Reproduced and isolated on SM107 silicon (the first hardware ever to run this path) with per-(batch, head) sentinel-V probes:
H=1 × B=Nexact,H=N × B=1exact,H>1 × B>1corrupt — precisely theQH-scaling signature🤖 Generated with Claude Code
Summary by CodeRabbit