Repository navigation
perf(cuda-ep): collapse bf16 SiLU/SwiGLU-mul glue nodes — realized +0.9% byte-exact (#899) - #900
Merged
Merged
Conversation
….9% byte-exact (#899) Extend CudaSiluFusion to accept BFloat16 activations (was Float16-only), so the standalone Sigmoid(x) + Mul(x, sigmoid) + Mul(silu, up) glue chain on the bf16 Muse-Glimmer-30B decoder collapses through CudaSiluFusion -> CudaSwiGluFusion into the tagged decomposed Mul[_cuda_silu_mul], lowered by the already-landed byte-exact decomposed_silu_mul_bf16 epilogue (#867). Deletes 104 standalone nodes (2/layer x 52): Sigmoid 104->52, Mul 210->158. CudaGateUpSwiGluFusion requires an fp16 activation, so it stays dormant on bf16 -> the int4 GEMVs are left untouched (per #898). No cooperative kernel, no grid.sync, no reduction reorder -> no Chew gate. Measured (H200, CUDA_VISIBLE_DEVICES=0, ONNX_GENAI_CUDA_GRAPH=1, --steady --warmups 1 --runs 3 --tokens 128, 3 interleaved A/B rounds, same binary): decode 21.19 -> 20.99 ms/token, throughput 47.20 -> 47.63 tok/s (+0.9%); greedy token stream byte/token-identical before<->after. SHIP. Converts the §8 +5.3% ceiling (#899) into a realized number; §8.5 doc records why the larger glue is blocked (bf16 skip-RMSNorm kernel missing -> flagged for Chew; constant gamma+1 Add fold regressed in #872; reshapes are GQA-coupled). Tests: collapses_bf16_decomposed_swiglu_glue, leaves_fp32_decomposed_swiglu_glue_separate. 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 #900 +/- ##
==========================================
- Coverage 78.39% 78.39% -0.01%
==========================================
Files 354 354
Lines 148003 148003
Branches 148003 148003
==========================================
- Hits 116023 116022 -1
- Misses 27484 27485 +1
Partials 4496 4496
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 13, 2026
…ld opt-in (no-ship default) (#903) ## 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 → SimplifiedLayerNormalization` residual 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 1. **`skip_rmsnorm_bf16` NVRTC kernel** (`kernels/normalization.rs`): `sum = __float2bfloat16_rn(f32(residual)+f32(x))` (bit-for-bit a standalone bf16 `Add`), stores the bf16-rounded sum as the next layer's residual, then the **identical** `rmsnorm_bf16` block-tree reduction (fp32 accumulate over the rounded sum, `NORM_BLOCK=256`). Guarded native dispatch with `run_bf16_via_f32` fallback for non-dense/bias/header-unavailable (Rule 11 portability). 2. **`CudaSkipRmsNormFusion`** optimizer pass: collapses the seam into one `com.microsoft::SkipSimplifiedLayerNormalization`, deleting the standalone `Add`+norm. bf16 (or f32-gamma-over-bf16) only; fp16 left to `CudaSkipRmsNormMatMulFusion`. **Opt-in** via `ONNX_GENAI_CUDA_ENABLE_SKIP_RMSNORM_FUSION` (default **OFF**). ## Numeric fidelity (Chew gate): BYTE-EXACT — 0-ulp - GPU unit tests `bf16_native_skip_rmsnorm_is_byte_exact_with_{bf16,f32}_gamma` pass (bit-identical vs standalone `Add(bf16)`→`rmsnorm_bf16` at H=6656). - Real-model greedy stream fold OFF vs ON: **bit-identical** (128/128 tokens). No reduction reordered. ## Perf A/B (H200, `ONNX_GENAI_CUDA_GRAPH=1`, `--steady --warmups 1 --runs 3 --tokens 128`, interleaved) | metric | fold OFF (default) | fold ON | |---|---|---| | `SkipSimplifiedLayerNormalization` nodes | 4 | **104** (2/layer × 52) | | decode | 20.93 ms/token | 21.25 ms/token | | throughput | **47.77 tok/s** | **47.06 tok/s (−1.5%)** | | greedy tokens | reference | **byte-identical** | **Mechanism (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 `Add` had 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 - **Kernel: SHIP** — 0-ulp, portability-gated, no production change. Prerequisite for the only winning bf16 path: folding the norm into the neighbouring **multi-CTA int4 GEMV** prologue/epilogue (bf16 analogue of `CudaSkipRmsNormMatMulFusion`) — a larger GEMV job, not this fold. - **Fold: NO-SHIP as default** — opt-in only; default binary unchanged (47.77 tok/s == baseline). ## Validation - `cargo fmt --all -- --check` clean; clippy clean on ep-cuda (pre-existing optimizer.rs:3347 + standard_attention.rs warnings are not mine). - 61 optimizer + 20 normalization (incl. 2 new byte-exact) unit tests pass. - CUDA EP cdylib `ldd`-confirmed **not** linking `libonnxruntime`. - Doc `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. 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>
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.
Converts the measured GO from #899 (§8 "+5.3% ceiling" glue node-collapse GO, byte-exact under CUDA-graph replay) into a realized, measured number on the production Muse-Glimmer-30B native CUDA decode graph.
What
Extend
CudaSiluFusion(crates/onnx-runtime-ep-cuda/src/optimizer.rs) to accept BFloat16 activations — it wasFloat16-only, which left every SiLU/SwiGLU fusion dormant on Muse-Glimmer (its activation stream is bf16). With bf16 enabled, the standaloneSigmoid(x)+Mul(x, sigmoid)+Mul(silu, up)glue collapses throughCudaSiluFusion→CudaSwiGluFusioninto the tagged decomposedMul[_cuda_silu_mul], which the runtime lowers to the already-landed byte-exactdecomposed_silu_mul_bf16epilogue (#867).Sigmoid104→52,Mul210→158; total decoder 2458→2354.CudaGateUpSwiGluFusionrequires an fp16 activation, so it stays dormant on bf16 → the int4 MatMulNBits GEMVs are left untouched (per perf(cuda-ep): dense megakernel P2-prototype — multi-CTA cooperative MLP measured (NO-GO for GEMV megakernel) #898). No cooperative kernel, nogrid.sync, no reduction reorder → no Chew gate.Measured (H200,
CUDA_VISIBLE_DEVICES=0,ONNX_GENAI_CUDA_GRAPH=1,--steady --warmups 1 --runs 3 --tokens 128, 3 interleaved A/B rounds on the same release binary)Sigmoid/ forwardMul/ forward[24, 372, 1045, 10016, …, 1740, 2885]Verdict: SHIP
Small-but-real, byte-exact, zero-risk (activates a landed kernel the pass gate was hiding). Honest bound vs the +5.3% ceiling: only the 2 SiLU/SwiGLU-mul nodes/layer are byte-exactly collapsible here. The larger glue is blocked and documented in §8.5:
x + norm(y); perf(cuda-ep): fix bf16 skip-norm capture-safety flag → 0 seams (Muse-Glimmer +33%) #854SkipSimplifiedLayerNormalization(norm(x+skip)) applies across the layer seam, but only f32/f16 skip kernels exist, and the f32-template path bf16 would reach accumulates the RMS over the unrounded fp32 sum (vs standalone bf16-rounded) → not byte-exact → flagged for Chew/Sebastian: needs a bf16 skip-RMSNorm kernel that rounds the residual sum.gamma+1Adds/layer — perf(cuda-ep): fuse decode-graph nodes to cut captured node count (47->flat; cheap-node fusion is not a lever) #872CudaFoldConstantAddalready MEASURED −2.8% regression; not re-attempted.Validation
cargo fmt --all -- --checkclean;cargo clippy -p onnx-runtime-ep-cuda --features cudaclean.cargo test --release -p onnx-runtime-ep-cuda --lib --features cuda: 366 passed / 0 failed (incl. newcollapses_bf16_decomposed_swiglu_glue,leaves_fp32_decomposed_swiglu_glue_separate).libonnx_runtime_ep_cuda_plugin.sobuilt;lddconfirms it does not linklibonnxruntime.Docs:
docs/research/dense-decode-megakernel-feasibility.md§8.5 (realized subsection). Decision drop:.squad/decisions/inbox/batty-glue-node-collapse.md.Refs #899. Do not self-merge.