Repository navigation
[diffusion] FLUX.1 bit-exact residual-gate fast path + tanh-GELU epilogue behind quality=high (H200 e2e -1.1% lossless / -4.3% high) - #33819
Merged
Conversation
…0 1024^2 e2e 7.481 -> 7.399 s, denoise 6.802 -> 6.674 s) Wire the existing residual_gate_add_cuda kernel (in-tree since sgl-project#29361, already on the LTX-2 default path) into all five residual-gate sites of FLUX.1: 19 dual-stream blocks x 4 sites (img/txt attn+mlp) + 38 single-stream blocks x 1 site = 114 sites/step, 5,700 eager mul+add pairs per 50-step 1024x1024 image. The kernel is bit-exact vs the eager pair for half dtypes (torch.equal at real FLUX shapes; full generated image md5-identical to main), so it runs on the lossless default path with no quality gate. fp32 stays on the eager pair (the kernel would contract to an fma and stop being bit-exact); batched [B>1,1,D] gates fail the guard and fall back to the eager pair automatically. H200, FLUX.1-dev, 1024x1024, 50 steps, bf16, fixed prompt+seed, median of 9 (first of 10 dropped): e2e wall 7.481 -> 7.399 s (-1.1%), DenoisingStage 6.802 -> 6.674 s (-1.9%). Kernel microbench per site, bf16: (1,4096,3072) 53.3 -> 20.7 us, (1,4608,3072) 59.4 -> 23.1 us (2.6x); the 114-site sum (2.6 ms/step, 130 ms/image) matches the measured 128 ms DenoisingStage delta. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…=high (H200 1024^2 e2e 7.399 -> 7.158 s, denoise 6.674 -> 6.524 s) Mark FLUX.1's three tanh-GELU up-projection layouts as sgl-project#33536 fused-GELU sites (FluxGELU for the TP>1 parallel FF, a new FluxFusedGELUProj wrapper re-homing the shared diffusers-style FeedForward.net[0] at TP=1 while keeping the net.0.proj parameter path, and the single-stream proj_mlp): 19 dual-stream blocks x 2 FFNs + 38 single-stream blocks x 1 MLP = 76 sites/step, 3,800 standalone GELU kernels per 50-step 1024x1024 image folded into the up-proj GEMM epilogue. Sites start unmounted (bit-exact reference path); the shared DenoisingStage mounts them only for quality="high" requests, exactly as shipped for Qwen-Image and GLM-Image in sgl-project#33536. Nunchaku/quantized paths are never marked. This re-lands the FLUX GELU-epilogue fusion from sgl-project#28166 (reverted in sgl-project#28708 for unconditional numerical deviation) behind the quality tier that resolves that objection: the lossless default stays md5-identical to main; quality=high vs lossless same-seed full-image PSNR 34.7-40.1 dB / SSIM 0.977-0.992 across 3 prompts (bar: PSNR > 25). H200, FLUX.1-dev, 1024x1024, 50 steps, bf16, fixed prompt+seed, median of 9 (first of 10 dropped), on top of the residual-gate commit: quality=high e2e wall 7.399 -> 7.158 s, DenoisingStage 6.674 -> 6.524 s (-2.2%); vs main the two commits total -4.3% wall / -4.1% denoise. Per-site microbench bf16 3072->12288: (1,4096) 491 -> 455 us, (1,4608) 569 -> 524 us; the 76-site sum (2.5 ms/step, 127 ms/image) matches the measured 150 ms DenoisingStage delta. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BBuf
requested review from
AgainstEntropy,
HaiShaw,
mickqian,
ping1jing2 and
yichiche
as code owners
August 6, 2026 05:14
Collaborator
Author
BBuf
added a commit
to BBuf/sglang
that referenced
this pull request
Aug 8, 2026
For quality=high requests, replace the affine-free LayerNorm + fused modulate pair with a single aten layer_norm call whose elementwise affine carries the modulation row: layer_norm(x, weight=1+scale, bias=shift). One kernel and one HBM pass per site instead of two; 1+scale keeps the eager rounding of the [1, D] row, the remaining difference is that scale/shift apply in fp32 to the unrounded normalized value (bf16 rounding-order level, max abs diff ~0.05 on FLUX shapes; not bit-exact). Mounted per batch by the denoising stage exactly like the fused linear+GELU epilogue (sgl-project#33819): sites default off, lossless requests keep the bit-exact reference path. Per-call guard requires B == 1 (the folded affine is a [D] row); nunchaku blocks are untouched.
saturn-acc
pushed a commit
to saturn-acc/sglang
that referenced
this pull request
Aug 16, 2026
…ogue behind quality=high (H200 e2e -1.1% lossless / -4.3% high) (sgl-project#33819) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
6 tasks done
Atituiset
pushed a commit
to Atituiset/sglang
that referenced
this pull request
Sep 10, 2026
…ogue behind quality=high (H200 e2e -1.1% lossless / -4.3% high) (sgl-project#33819) 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
FLUX.1's transformer updates its residual streams through eager two-kernel
gate * update+residual + ...pairs, and runs every FFN tanh-GELU as a separate bandwidth-bound kernel after the up-projection GEMM:[tokens, 12288]intermediate.This PR wires two existing, already-shipped mechanisms into FLUX.1 (no new kernels, no protocol changes):
residual_gate_add_cuda(in-tree since [KDA-Pilot] Add diffusion residual-gate CUDA fast path for LTX2 #29361, used by LTX-2 and the ERNIE-Image PR [diffusion] ERNIE-Image bit-exact residual-gate fast path (H200 1024^2 e2e 16.17 -> 15.75 s) #33734 on their default paths): collapses each gate pair into one launch and, for half dtypes, reproduces the eager pair's rounding bit-for-bit — so it runs on the lossless default path with no quality gate.fused_linear_gelusites + DenoisingStage mount/unmount protocol, shipped for Qwen-Image/GLM-Image): folds the tanh-GELU into the up-proj GEMM epilogue. Not bit-exact (bf16 rounding-order level), so it is mounted only forquality="high"requests and the default path stays byte-identical.History: why the GELU fusion is gated
An ungated FLUX GELU-epilogue fusion was merged once before (#28166, "[Diffusion] FLUX: fuse FeedForward GELU into up-proj GEMM (cublasLt epilogue)") and reverted in #28708 because the unconditional numerical deviation broke reference-model comparisons. The
qualitytier system (#33453) is exactly the mechanism that resolves that objection: the deviation is now opt-in per request, the default path is provably byte-identical to main (md5 below), and the mount/unmount protocol is the same one already shipped and validated for Qwen-Image and GLM-Image in #33536.Modifications
All changes are FLUX-local (
flux.py+ two extended test files); the kernels and the mount protocol are untouched. Two commits: lossless residual-gate first, gated GELU second._flux_residual_gate_add(same helper shape as LTX-2/[diffusion] ERNIE-Image bit-exact residual-gate fast path (H200 1024^2 e2e 16.17 -> 15.75 s) #33734: one-time runtime fallback,torch.compiler.is_compiling()escape hatch so compiled graphs never bake in a silent fallback, fp16/bf16-only where the kernel is bit-exact) wired into all 5 gate sites. The kernel's row-broadcast gate only covers[1, ..., 1, D], so batched[B>1, 1, D]gates fail the guard and fall back to the eager pair automatically (FLUX.1-dev uses embedded guidance, so serving batches are B=1 per forward and the gates are already dense[1, 1, D]slices — no extra copies needed).mark_fused_gelu_siteon the three FLUX GELU up-projection layouts:FluxGELU(TP>1 dual-streamFluxParallelFeedForward),FluxFusedGELUProjwrapper re-homing the shared diffusers-styleFeedForward.net[0](TP=1 dual-stream); it keeps thenet.0.projparameter path, so no checkpoint-mapping changes,FluxSingleTransformerBlock.proj_mlp(single-stream, non-nunchaku branch).Sites start unmounted (bit-exact reference); the shared DenoisingStage mounts them per batch for
quality="high"only (log-confirmed:Mounted fused linear+GELU (cublasLt epilogue) for quality=high). Nunchaku/quantized paths are never marked, and the mount's all-or-nothing static guard also rejects them.What was audited and dropped
The adaptation matrix also flagged FLUX's
norm2/norm2_context+ scale/shift and the AdaLayerNormZero internals for the shared norm+scale/shift fusion. Dropped from this PR: the only bit-exact-capable shared implementation (norm_scale_shift_native) is hard-gated to Blackwell (capability >= 10, tuned for that launch geometry) while this validation lane is H200/SM90, and the Tritonselect01layers are neither bit-exact (fp32 single-pass LayerNorm reduction vs eager) nor semantics-matched (dual-branch index selection). Per the matrix's own standard this was a rider item: it does not justify introducing gate complexity it cannot clear, so it is cut rather than gated (same disposition as the ERNIE PR's norm-fusion re-validation).Speed Tests (H200)
FLUX.1-dev, 1024x1024, 50 steps, bf16, fixed prompt and
seed=42. Client wall-clock in one warmed process, 10 consecutive generations, first dropped, median of 9 (#33451/#33536/#33734 protocol), single H200,CUDA_VISIBLE_DEVICESpinned. Weights: sha256-verified byte-identical mirror ofblack-forest-labs/FLUX.1-dev(gated repo; every LFS object checked against the official manifest).Attribution closes against kernel microbenchmarks at the real shapes (bf16, 200 iters after warmup):
Summed over sites: gates 2.6 ms/step (130 ms/image) vs measured 128 ms lossless denoise delta; GELU 2.5 ms/step (127 ms/image) vs measured 150 ms high-vs-lossless denoise delta. Sanity anchor: ERNIE-Image (#33734) got -2.6% e2e from 7,200 gate sites at hidden 4096 with CFG; FLUX's -1.1% lossless from 5,700 sites at hidden 3072 without CFG is proportionally consistent.
Benchmark configuration
All arms above run the default eager configuration (
performance_mode=auto; every archived server log showsenable_torch_compile: false, regional_compile: false) — i.e. the default production path for this model. Runs with--enable-torch-compileare unaffected: the fused sites step aside viatorch.compiler.is_compiling()and Inductor keeps its own fusion of these chains.Benchmark driver (local mode, one warmed process; 10 sequential requests, first dropped, median of 9):
Accuracy Tests
torch.equal, real FLUX shapes (4096/512/4608 x 3072), bf16/fp16True(atol=0, rtol=0)torch.equalTruecad50fb5beddfda2a7898a595e5e303con both, all 10 iterations)unique=1)quality=high vs lossless, same seed, full-image (3 prompts, first/middle/last sample images exported):
All comfortably above the
quality="high"bar (PSNR > 25).Side-by-side samples (lossless left, quality=high right):
cyberpunk city

mountain lake sunrise

craftsman portrait

Unit tests (extended in place,
base-b-kernel-unit):test_residual_gate_add.py: + real FLUX.1 1024^2 shapes (dual img/txt, single joint), bit-exact.test_fused_linear_gelu.py: +FluxFusedGELUProjsite (gate offtorch.equalvs reference, mountedassert_close, unmount restores bit-exactness).pre-commit run --files ...: all hooks pass.🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ❌ Run #31073539937
Latest PR Test (Extra): ❌ Run #31073552155