Skip to main content
Diffusion transformers and VAEs spend a large share of their non-GEMM time on short elementwise chains — adaLN modulate, residual gating, QK-norm, RoPE, norm epilogues — each of which is a separate kernel launch and a separate HBM round trip in eager PyTorch. SGLang Diffusion replaces these chains with fused kernels under sglang/kernels/ops/diffusion. This page is an inventory: what each kernel fuses, what its numerical contract is, and which models use it. It is not a lever you tune — most of these kernels are on by default and require no flag. The one switch is --quality, described below.

Two numerical contracts

Multi-step denoising amplifies a per-step rounding difference into visible quality loss, so “close enough” and “bit-exact” are different products here. Every kernel in the package falls into one of two classes. Bit-exact — mounted unconditionally. The kernel reproduces every rounding boundary of the eager chain, so torch.equal holds against the reference. Some go quite far to get there: the fused LayerNorm+modulate kernel replicates PyTorch’s vectorized_layer_norm_kernel down to its Welford update order, guarded reciprocal, and warp-fold tree; the fused RMSNorm+scale/shift kernel replicates FlashInfer’s CuTe-DSL RMSNormKernel fragment order and shfl.bfly fold. Because the dispatch they replicate can change underneath them, each one still verifies itself against the live eager chain on first sight and falls back permanently on any mismatch. Not bit-exact — request-gated. These differ from eager only at half-precision rounding-order level, but that is enough to matter, so they are mounted only for quality="high" requests, at batch boundaries, all-or-nothing per transformer. The default quality="lossless" runs the unmodified reference chain.
A plain fp32 single-pass norm fusion looks harmless and is not. On ERNIE-Image it moved the 50-step trajectory to 18.83 dB PSNR at quality=high, which is what motivated the bit-exact rewrite of that path.

Enabling the request-gated set

The server default stays lossless; the OpenAI-compatible endpoints carry it per request. Images:
Video, same field:
The quality field in a video response body is unrelated. It is Sora-compatible response metadata and is always reported as "standard"; it does not reflect the sampling quality the request ran with.
quality participates in the dynamic-batch signature, so mixed-quality traffic is batched separately and the transition happens safely at a batch boundary. Mounting is all-or-nothing: if any marked site on a transformer fails its static guards, no site on that transformer is fused. These fusion families mount under quality="high":

Kernel inventory

34 operators are registered in the kernel registry across 38 implementations (some operators carry several backends). Backends are named by provenance, not device: JIT compiles under nvcc and hipcc, TRITON runs on CUDA and ROCm, CUTE_DSL needs CUTLASS, FLYDSL is ROCm gfx950 only, AOT comes from the sgl_kernel wheel.

Normalization

adaLN modulation and gating

temb_table_slices is worth knowing about. The eager (scale_shift_table + temb.float()).chunk(6, dim=2) materializes roughly 8 GB of fp32 at 704p/121f and hands six strided slices downstream, whose .contiguous() calls then copy each one again. The fused kernel produces the six slices in one pass, each naturally contiguous, so the downstream copies become no-ops.

RoPE and QK-norm

Activation

Attention

Data movement

Every kernel here only moves values (plus zero fill, plus at most one same-order add), so each is bitwise identical to the aten chain it replaces.

Coverage by model

Kernels are written against a specific eager chain in a specific model, so coverage is per-model rather than universal.

Inspecting what is registered

Every kernel is described by a KernelSpec in the process-wide registry, so the inventory is queryable without importing any backend:
Registration is metadata only — it imports neither torch nor a backend and triggers no JIT build. To pick a specific implementation of an operator that has several:

Importing the kernels

Runtime code imports from the package, never from a submodule:
Resolution is lazy: the backends have disjoint, heavy dependencies (Triton, CUTLASS/CuTe-DSL, FlyDSL on ROCm, MLX on Apple), so an eager re-export would make every one of them an import-time requirement on every platform. Each public kernel is a predicate-plus-kernel pair — call can_use_<op>(...) first and fall back to the reference chain when it returns False; the kernel raises on an unsupported input rather than silently returning None. The package README.md carries a selection matrix for the cases where several kernels look interchangeable and are not. The normalization domain alone holds more than a dozen implementations that differ by numerical contract, activation layout, and backend rather than by speed.

References