Repository navigation
[diffusion] GLM-Image bit-exact fused aten LayerNorm+modulate / qk-LN (H200 30-step denoise -8.1%) - #34008
Merged
Conversation
BBuf
requested review from
AgainstEntropy,
HaiShaw,
mickqian,
ping1jing2,
yichiche and
yingluosanqian
as code owners
August 7, 2026 16:58
… (H200 1024^2 30-step denoise 6.385 -> 5.870 s, -8.1%) Fuse the GLM-Image adaLN eager chains into bit-exact Triton kernels: - GlmImageAdaLayerNormZero / GlmImageAdaLayerNormContinuous: LN + norm*(1+scale)+shift in one kernel, replicating the aten vectorized_layer_norm_kernel numerics (SASS-verified Welford DAG) and the modulate chain per-op bf16 rounding - attention qk-norm: per-head nn.LayerNorm over q and k in one launch (aten spends a 128-thread block per head_dim row; 301 -> 62 us at the real shape) - ff gate: reuse residual_gate_add_cuda Outputs are bit-exact: torch.equal at every site, and 2 prompts x 2 seeds full-image PNG md5s identical to main. Wrappers verify against the eager chain on first call and permanently fall back on mismatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BBuf
force-pushed
the
k1-glm-bitexact-adaln
branch
from
August 8, 2026 00:58
cfe97e9 to
35816d6
Compare
BBuf
added a commit
to BBuf/sglang
that referenced
this pull request
Aug 8, 2026
…H200 1024px denoise 405.2 -> 385.9 ms/req, -4.8%) Generalize the sgl-project#34008 aten-LN-replica Triton kernel to any hidden % 4 == 0 by adding a masked partial tail chunk (aten threads whose next vector index reaches N/4 skip the iteration, entering the warp fold with a smaller Welford count); the hidden % 512 == 0 fast path compiles unchanged. Sana hidden is 2240, which no existing fusion family covers. Route Sana 3 adaLN sites (norm1/norm2 per block + SanaModulatedNorm) through the kernel with per-signature torch.equal verification against the live eager chain and permanent fallback on mismatch. The fusion engages only in CUDA-graph contexts (stream capture / the BCG capture stream, sgl-project#33989): there replay pays no Python launch cost and the fused kernel is a pure GPU win (-4.9% total denoise GPU kernel time, -13.5% launches, denoise 405.2 -> 385.9 ms/req, -4.8% in 8/8 order-balanced paired rounds). Sana eager mode is CPU-launch bound and one Triton launch costs more Python than the whole 5-deep aten enqueue pipeline it replaces (measured +14% forward wall), so default-stream eager serving keeps the eager chain (trace-verified identical to main). Output PNG md5s are identical to main in every round, both configs. Also expose fused_layernorm_modulate_raw (direct call without the ~40us torch.ops dispatch); the registered custom op wraps it, GLM-Image call sites unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BBuf
added a commit
to BBuf/sglang
that referenced
this pull request
Aug 8, 2026
…-exact fused LayerNorm+modulate kernel (H200 1024^2 denoise 6652.6 -> 6573.5 ms, -1.2% lossless) Follow-up to sgl-project#34004, wiring sgl-project#34008's SASS-exact fused LayerNorm+modulate Triton kernel into all five FLUX.1 adaLN site classes (dual-stream norm1 / norm1_context / norm2 / norm2_context + single-stream) through the existing _flux_norm_modulate plumbing: the lossless tier drops the aten LN + fused-modulate pair for one kernel, and the lossy quality="high" LN-affine fold is superseded wherever the kernel verifies (it stays as a fallback for foreign LayerNorm dispatches). Bit-exactness is a property of the live aten dispatch, so every distinct (shape, stride, eps) signature a site emits is verified torch.equal against the eager chain once at runtime and the fast path is disabled permanently on any mismatch -- same safety net as sgl-project#34008's GLM-Image wiring, tightened from one global flag to per-signature. Verification is skipped inside torch.compile tracing and CUDA-graph capture; the warmup pass verifies first. H200 FLUX.1-dev 1024^2 50-step bf16 B=1 seed=42, on the sgl-project#34004+sgl-project#34008 merge base, 2x(10 sequential gens, drop first), pooled median of 18, lossless tier: e2e wall 7428.6 -> 7211.2 ms (-2.9%), server total 6961.3 -> 6858.6 ms (-1.5%), DenoisingStage 6652.6 -> 6573.5 ms (-1.2%). All 40 images across both arms share a single md5 (the same hash the sgl-project#34004 campaign measured). quality="high": DenoisingStage parity with the affine fold (6406.9 vs 6404.3 ms) while the LN sites become lossless again -- PSNR vs lossless is 34.7 dB, exactly the GELU-epilogue-only footprint (sgl-project#34004 measured 34.7 dB for that arm). GPU-only microbenchmark at the site shapes (torch profiler, 100 iters): (1,4096,3072) 35.2 -> 22.2 us, (1,512,3072) 8.6 -> 6.9 us, (1,4608,3072) 40.3 -> 26.1 us vs the eager LN+modulate pair; ~1.1 ms/step summed over the 114 sites, closing against the measured DenoisingStage delta (79 ms / 50 steps).
This was referenced Aug 8, 2026
saturn-acc
pushed a commit
to saturn-acc/sglang
that referenced
this pull request
Aug 16, 2026
… (H200 30-step denoise -8.1%) (sgl-project#34008) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Atituiset
pushed a commit
to Atituiset/sglang
that referenced
this pull request
Sep 10, 2026
… (H200 30-step denoise -8.1%) (sgl-project#34008) Co-authored-by: Claude Fable 5 <noreply@anthropic.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.
Motivation
GLM-Image's transformer block (hidden 4096, 30 blocks, 32 heads x head_dim
128, 30 steps, true CFG = 2 forwards/step) still runs its adaLN chain in
eager pieces — 242 bare aten
layer_normlaunches per step plus ahand-written modulate/gate chain around them:
GlmImageAdaLayerNormZeroruns barenn.LayerNorm(no affine) plus ahand-written
norm * (1 + scale) + shiftfor both the image and textstreams — 4 aten kernels per stream per block;
nn.LayerNorm(128)per head on(1, S, 32, 128): aten'svectorized_layer_norm_kernelspends a full 128-thread block per128-element row (warp 0 does 4 elements per lane, warps 1-3 idle), and
the two launches cost 301 us per block at the real shape — 10.1% of
denoise GPU time in the profiler trace;
GlmImageAdaLayerNormContinuousrepeats the LN+modulatepattern once per forward;
hidden + ff_out * gateis another eager pair per stream.This PR fuses each site into one Triton kernel that is numerically
re-engineered to be rounding-faithful to the eager chain, step for step,
so it is bit-exact (
torch.equal, full-image md5-identical) and wired onthe default lossless path with no quality gate — the standard #33734 /
#33854 / #33886 used.
What "rounding-faithful" means here
nn.LayerNormon bf16 dispatches to aten'svectorized_layer_norm_kernel<BFloat16, float>. The Triton kernelsreplicate that kernel's exact arithmetic DAG, verified instruction by
instruction against the sm_90 SASS of torch 2.11's
libtorch_cuda.so:aligned_vector<bf16, 4>loads (thread t owns vectorst, t+128, ...),with both FMA contractions nvcc makes (
mean' = FFMA(delta, recip, mean),sigma2' = FFMA(delta, val - mean', sigma2)) and its guarded4-instruction reciprocal fast path (
MUFU.RCP; FFMA(x,r,-1); FADD.FTZ(-e,-0); FFMA(r,e,r)) reproduced via inline PTX;SHFL.DOWN16/8/4/2/1 fold tree throughcuWelfordCombine(count-weighted merge, zero-count branch included) expressed with
order-exact
tl.reshape/tl.permute/tl.splitfolds, then the(0,2)/(1,3)/(0,1) inter-warp combines;
sigma2 / float(N)as an IEEE correctly-roundeddiv.rn.f32, andrstd = rsqrtf(...)asrsqrt.approx.f32with the 2^24/2^12 subnormalrescale branch;
cvt.rn.bf16(FMUL(rstd, FADD(x, -mean)))(no FMA), then theaten modulate chain's per-op bf16 boundaries (
round(1+scale),round(y*(1+scale)),round(+shift)) via the bitcast RNE helper from[diffusion] ERNIE-Image bit-exact fused RMSNorm+scale/shift (H200 1024^2 e2e 15.63 -> 15.00 s, denoise -3.3%) #33854;
only vectors
0..head_dim/4-1of the 128 aten threads carry data (theremaining lanes and warps 1..3 enter the fold with count 0), and handles
q and k in one launch, 64 rows per program.
Because bit-exactness is a property of which LayerNorm implementation aten
dispatches to, the model-side wrappers verify
torch.equalagainst theeager chain once at runtime on the first real input and permanently fall
back to eager on any mismatch. The fast path can never silently change
outputs.
The ff gate reuses
residual_gate_add_cuda(bit-exact by construction,#33734) behind the same one-time-disable wrapper.
Modifications
python/sglang/kernels/ops/diffusion/triton/layernorm_modulate.py(new): the two Triton kernels + guards, registered as custom ops
(torch.compile-safe fake impls).
glm_image.py:_glm_ln_modulate/_glm_qk_layernorm/_glm_residual_gate_addwrappers (first-call self-verification, one-timedisable on exception/mismatch, [diffusion] ERNIE-Image bit-exact fused RMSNorm+scale/shift (H200 1024^2 e2e 15.63 -> 15.00 s, denoise -3.3%) #33854 style) wired at
GlmImageAdaLayerNormZero(both streams),GlmImageAdaLayerNormContinuous, the attention qk-norm, and both ff-gatesites.
test/registered/kernels/ops/diffusion/test_glm_image_ln_modulate.py(new): bit-exactness at the real GLM-Image shapes plus an assertion that
the fast paths were actually taken (not silently disabled).
Speed Tests (H200)
zai-org/GLM-Image, 1024x1024, default 30-step preset (true CFG, 2forwards/step), bf16, eager, fixed prompt,
seed=42. One warmed process,10 sequential requests, first dropped, median of 9, GPU 0.
DenoisingStage -8.1% (pooled main 6.388 -> 5.870 s), bit-exact
(md5-identical) output.
E2e caveat: ~83% of e2e is the
GlmImageARautoregressive prior-tokenstage (a separate
GlmImageForConditionalGenerationmodel this PR does nottouch), which drifted 32.1 -> 35.4 -> 33.2 s across the three arms with
host load (other tenants on the box) — the A/B/A bracket shows the drift is
environmental, not tree-dependent (main's own e2e moved +1.2 s between its
two runs while its denoise stayed within 6 ms). Net of the AR stage, e2e
drops 6.70/6.71 -> 6.19 s (-7.7%, i.e. -0.52 s/request, -1.3% of total
e2e).
Kernel microbenchmark at the real shapes (
triton.testing.do_bench):LN + norm*(1+scale)+shift(1, 4096, 4096) bf16 (aten LN + 3 aten elementwise)nn.LayerNorm(128)(1, 4360, 32, 128) bf16, q and k (2 aten launches)Attribution (built-in
--profile, same 5-step denoise window on bothtrees, kernel self-time):
vectorized_layer_norm_kernel(norm1 x2, qk x2, norm_out)_qk_ln_head_kernel(new)_ln_modulate_kernel(new)residual_gate_add(reused #33734 kernel)Remaining window is GEMMs (72.8%) and FlashAttention (13.6%), consistent
with the -8.1% DenoisingStage wall-clock delta.
Benchmark command
Both arms run the identical protocol (this PR is lossless-only; arms = main
tree @ d4be483 vs PR tree), default eager configuration
(
performance_mode=auto,enable_torch_compile=false):Accuracy Tests
torch.equal, real shapes (1,4096,4096) LN+mod, (1,4360,32,128) qk, plus text-stream/odd shapesTrue(registered unit test, 6 passed)f86659da...,91882448...,7a2f3b00...,ac01683d...)torch.equalvs eager and permanently disables the fast path on mismatchUnit test (registered,
base-b-kernel-unit):pre-commit run --files ...: all hooks pass.🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ⏳ Run #31235033025
Latest PR Test (Extra): ✅ Run #31235968414