Repository navigation
perf(cuda): byte-exact bf16 skip-RMSNorm kernel (ship); standalone fold opt-in (no-ship default) - #903
Merged
Conversation
…in (no-ship default) Closes the §8.5 blocker from #900: adds a byte-exact bf16 fused skip-RMSNorm kernel (skip_rmsnorm_bf16) that rounds the residual sum to bf16 before the RMS reduction — bit-for-bit identical to standalone Add(bf16) + rmsnorm_bf16 — and a CudaSkipRmsNormFusion optimizer pass that collapses the `Add -> SimplifiedLayerNormalization` seam into one com.microsoft::SkipSimplifiedLayerNormalization. Kernel is GPU-verified 0-ulp (bf16 and f32 gamma) and the real-model greedy stream is bit-identical fold OFF vs ON (128/128 tokens). SHIP the kernel. The fold, however, is a measured ~1.5% regression under CUDA-graph replay (47.77 -> 47.06 tok/s): at M=1 the RMS reduction is single-CTA, so folding the residual add into it serializes work the standalone multi-CTA Add spread across all 132 SMs; graph replay already amortizes the saved launches (decode is at its launch-amortized latency floor, per #898/#900). The fold is therefore gated behind ONNX_GENAI_CUDA_ENABLE_SKIP_RMSNORM_FUSION (default OFF) — the default binary is unchanged (no regression). The kernel is kept as the prerequisite for the only winning bf16 path: folding the norm into the neighbouring multi-CTA int4 GEMV (bf16 analogue of CudaSkipRmsNormMatMulFusion). Portability: native bf16 dispatch falls back to run_bf16_via_f32 for non-dense/bias/header-unavailable cases (Rule 11). CUDA EP cdylib ldd-confirmed not linking libonnxruntime. fmt + clippy clean; 61 optimizer + 20 normalization unit tests pass. Doc §8.6 + decision drop added. Refs #900. Do NOT self-merge — Chew gates numerics. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby
added a commit
that referenced
this pull request
Aug 13, 2026
Merge 3 inbox drops into one "Decode latency-floor: node-collapse arc" section in decisions.md. Batch-1 native CUDA int4 decode is confirmed at its launch-amortized latency floor from three independent directions: megakernel NO-GO (#898), glue-collapse +0.9% ceiling (#899/#900), skip-RMSNorm fold -1.5% regression (#903). - #899 (Sebastian): glue node-collapse GO under graph replay (74-75%). - #900 (Batty): bf16 SiLU/SwiGLU-mul glue collapse SHIP (+0.9%, byte-exact, SiLU-fusion bf16 portability fix under Rule 11). - #903 (Sebastian): bf16 skip-RMSNorm KERNEL byte-exact SHIP; standalone FOLD -1.5% NO-SHIP (opt-in behind ONNX_GENAI_CUDA_ENABLE_SKIP_RMSNORM_FUSION, default OFF). Size gate: archived 2026-08-12 VMM/offload + #762 durable-lessons narratives to decisions-archive/2026-08.md to keep decisions.md under 50 KB (49,833 B). Appended cross-agent history for sebastian + batty; delete 3 processed drops. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #903 +/- ##
=======================================
Coverage 77.79% 77.79%
=======================================
Files 354 354
Lines 148003 148003
Branches 148003 148003
=======================================
+ Hits 115133 115134 +1
+ Misses 28378 28377 -1
Partials 4492 4492
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
🔴 Benchmark Regression DetectedComparison of criterion micro-benchmarks: PR head vs merge-base, measured on the same runner in the same job (base first → PR second).
Visual flags: Host infoWhat this cannot catch
|
justinchuby
added a commit
that referenced
this pull request
Aug 14, 2026
…#4) Reduce PR #916 to a finding-only record. The bf16 norm-into-GEMV-prologue fusion (bf16 analogue of CudaSkipRmsNormMatMulFusion) was prototyped and measured on H200: -4.6% regression AND greedy-stream divergence at ~token 38 (not byte-exact). Per our byte-exact quality bar we do NOT land a known-divergent kernel even behind a default-OFF flag — it is a numerically-wrong footgun and dead code. The value is the finding, not the code. Kept: docs/research/dense-decode-megakernel-feasibility.md §8.7 (design, the -4.6% + token-38 divergence result, and the mechanism: the fp16 prologue reduction is single-warp-serial over H=6656 re-run once per fan-out follower = +180ms / +48% GEMV time, while the bf16 standalone norm is already a cheap block-tree parallel reduction; a real win needs a NEW genuine multi-CTA cooperative-reduction prologue kernel, out of scope), plus the NO-GO decision drop. Removed: the prototype fused-prologue pass, the ONNX_GENAI_CUDA_ENABLE_NORM_GEMV_PROLOGUE flag + optimizer wiring, and the two unit tests that exercised the divergent path. Branch now touches NO production src/ — docs + decision drop only. Fourth independent confirmation of the launch-amortized decode latency floor (~47.6-48 tok/s). Refs #903. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
justinchuby
added a commit
that referenced
this pull request
Aug 14, 2026
…#4) (#916) ## Finding-only: bf16 norm-into-GEMV-prologue fusion — measured **NO-GO** Reduced to **docs + decision drop only** (no production `src/` change). The prototype fusion was measured, found regressive **and** numerically divergent, and per our byte-exact quality bar we do **not** land a known-divergent kernel even behind a default-OFF flag (numerically-wrong footgun + dead code). **The value is the finding, not the code.** Refs #903. ### The lever (prototyped, then discarded) Fold the standalone bf16 RMSNorm into the following **multi-CTA int4 GEMV**'s in-kernel prologue (bf16 analogue of the fp16 `CudaSkipRmsNormMatMulFusion`) so the RMS reduction rides the GEMV's full-device (132-SM) occupancy instead of a standalone single-CTA launch — the distributed-reduction fix for why #903's standalone fold regressed −1.5%. Pattern `x → SimplifiedLayerNormalization(x,γ) → MatMulNBits`; 104 foldable pre-norms/model. ### Measured (H200, `ONNX_GENAI_CUDA_GRAPH=1`, `--pipeline`, interleaved A/B) | Config | tok/s | Δ | |---|---|---| | Baseline (pass OFF) | **47.9** | — | | Norm→GEMV-prologue fused ON | **45.7** | **−4.6% REGRESSION** | - **Numerics:** NOT byte-exact — greedy 128-token stream **diverges at ≈token 38**. - **Mechanism (op timer):** folding saved only ~1.8 ms of the *already-cheap* bf16 block-tree norm but added **+180 ms (+48%)** to the GEMVs, because the available fp16 prologue reduction is **single-warp-serial** over H=6656, re-run once per fan-out follower on the critical GEMV path — the inverse of the fp16 skip case the benefit gate assumed. A real win would need a **new genuine multi-CTA cooperative-reduction prologue kernel** (out of scope). ### Verdict — **NO-GO / NO-SHIP** Prototype **not landed**. Decode remains at its launch-amortized latency floor (~47.6–48 tok/s) — the **fourth** independent confirmation (megakernel, glue-under-replay, standalone skip-fold, and now this). ### Contents (docs-only) - `docs/research/dense-decode-megakernel-feasibility.md` §8.7 (design + NO-GO result + mechanism). - `.squad/decisions/inbox/sebastian-norm-gemv-prologue.md` (NO-GO decision). `git diff origin/main --stat` = 2 files (doc + decision drop); `cargo fmt --all -- --check` clean; no `crates/`/`src/` change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the §8.5 blocker from #900: adds a byte-exact bf16 fused skip-RMSNorm kernel and an optimizer fold that collapses the
Add → SimplifiedLayerNormalizationresidual seam. Outcome: SHIP the kernel (proven 0-ulp), NO-SHIP the fold as default (measured −1.5% regression under graph replay) — the fold is retained opt-in behind an env flag, so the default binary is unchanged.Refs #900. Do NOT self-merge — Chew gates numerics.
What
skip_rmsnorm_bf16NVRTC kernel (kernels/normalization.rs):sum = __float2bfloat16_rn(f32(residual)+f32(x))(bit-for-bit a standalone bf16Add), stores the bf16-rounded sum as the next layer's residual, then the identicalrmsnorm_bf16block-tree reduction (fp32 accumulate over the rounded sum,NORM_BLOCK=256). Guarded native dispatch withrun_bf16_via_f32fallback for non-dense/bias/header-unavailable (Rule 11 portability).CudaSkipRmsNormFusionoptimizer pass: collapses the seam into onecom.microsoft::SkipSimplifiedLayerNormalization, deleting the standaloneAdd+norm. bf16 (or f32-gamma-over-bf16) only; fp16 left toCudaSkipRmsNormMatMulFusion. Opt-in viaONNX_GENAI_CUDA_ENABLE_SKIP_RMSNORM_FUSION(default OFF).Numeric fidelity (Chew gate): BYTE-EXACT — 0-ulp
bf16_native_skip_rmsnorm_is_byte_exact_with_{bf16,f32}_gammapass (bit-identical vs standaloneAdd(bf16)→rmsnorm_bf16at H=6656).Perf A/B (H200,
ONNX_GENAI_CUDA_GRAPH=1,--steady --warmups 1 --runs 3 --tokens 128, interleaved)SkipSimplifiedLayerNormalizationnodesMechanism (honest): eager per-op timer shows the fused glue is faster (Add+norm+skip 355.6 → 349.0 ms — real launch saving), but graph replay already amortizes launches (~0.9 µs/node floor). At M=1 the RMS reduction is single-CTA; folding the residual add into it serializes work the standalone
Addhad spread across all 132 SMs. Same structural reason the multi-CTA GEMV megakernel was NO-GO (§7/#898) and glue collapse paid only +0.9% (§8.5/#900): decode is at its launch-amortized latency floor.Verdict
CudaSkipRmsNormMatMulFusion) — a larger GEMV job, not this fold.Validation
cargo fmt --all -- --checkclean; clippy clean on ep-cuda (pre-existing optimizer.rs:3347 + standard_attention.rs warnings are not mine).ldd-confirmed not linkinglibonnxruntime.docs/research/dense-decode-megakernel-feasibility.md§8.6 + decision drop.squad/decisions/inbox/sebastian-bf16-skip-rmsnorm.md.🤖 Generated with Copilot. Co-authored-by: Copilot.