[diffusion][kernel] Accelerate Sana BCG with bit-exact conv post-processing (#34928)

This commit is contained in:
Xiaoyu Zhang
2026-08-16 20:05:57 +08:00
committed by GitHub
parent 3d3194f6c3
commit 095ec6c997
3 changed files with 296 additions and 8 deletions
@@ -0,0 +1,123 @@
# SPDX-License-Identifier: Apache-2.0
"""Bit-exact post-processing kernels for Sana's channels-last GLUMB convs."""
from __future__ import annotations
import torch
import triton # type: ignore
import triton.language as tl # type: ignore
from sglang.kernels.ops.diffusion.triton.numerics import round_bf16_to_fp32
@triton.jit
def _bias_silu_kernel(out_ptr, x_ptr, bias_ptr, numel, channels: tl.constexpr):
offsets = tl.program_id(0).to(tl.int64) * 1024 + tl.arange(0, 1024)
mask = offsets < numel
channel = offsets % channels
x = tl.load(x_ptr + offsets, mask=mask, other=0.0).to(tl.float32)
bias = tl.load(bias_ptr + channel, mask=mask, other=0.0).to(tl.float32)
# nn.Conv2d applies its bf16 bias before nn.SiLU, so preserve the
# intermediate bf16 rounding boundary rather than contracting the chain.
biased = round_bf16_to_fp32(x + bias)
tl.store(out_ptr + offsets, biased * tl.sigmoid(biased), mask=mask)
@triton.jit
def _bias_glu_kernel(
out_ptr,
x_ptr,
bias_ptr,
out_numel,
channels: tl.constexpr,
):
offsets = tl.program_id(0).to(tl.int64) * 1024 + tl.arange(0, 1024)
mask = offsets < out_numel
channel = offsets % channels
pixel = offsets // channels
in_base = pixel * (2 * channels) + channel
hidden = tl.load(x_ptr + in_base, mask=mask, other=0.0).to(tl.float32)
gate = tl.load(x_ptr + in_base + channels, mask=mask, other=0.0).to(tl.float32)
hidden_bias = tl.load(bias_ptr + channel, mask=mask, other=0.0).to(tl.float32)
gate_bias = tl.load(bias_ptr + channels + channel, mask=mask, other=0.0).to(
tl.float32
)
hidden = round_bf16_to_fp32(hidden + hidden_bias)
gate = round_bf16_to_fp32(gate + gate_bias)
# SiLU materializes a bf16 tensor before the following multiply in eager.
gate = round_bf16_to_fp32(gate * tl.sigmoid(gate))
tl.store(out_ptr + offsets, hidden * gate, mask=mask)
def _is_channels_last_bf16(x: torch.Tensor) -> bool:
return (
x.is_cuda
and x.dtype is torch.bfloat16
and x.dim() == 4
and x.numel() > 0
and x.is_contiguous(memory_format=torch.channels_last)
)
def can_use_fused_bias_silu(x: torch.Tensor, bias: torch.Tensor) -> bool:
return (
_is_channels_last_bf16(x)
and bias.is_cuda
and bias.dtype is x.dtype
and bias.device == x.device
and bias.dim() == 1
and bias.shape[0] == x.shape[1]
and bias.is_contiguous()
)
def fused_bias_silu(x: torch.Tensor, bias: torch.Tensor) -> torch.Tensor:
if not can_use_fused_bias_silu(x, bias):
raise RuntimeError("unsupported input for Sana fused bias-SiLU")
out = torch.empty_like(x, memory_format=torch.preserve_format)
with torch.cuda.device(x.device):
_bias_silu_kernel[(triton.cdiv(x.numel(), 1024),)](
out, x, bias, x.numel(), channels=x.shape[1]
)
return out
def can_use_fused_bias_glu(x: torch.Tensor, bias: torch.Tensor) -> bool:
return (
_is_channels_last_bf16(x)
and x.shape[1] % 2 == 0
and bias.is_cuda
and bias.dtype is x.dtype
and bias.device == x.device
and bias.dim() == 1
and bias.shape[0] == x.shape[1]
and bias.is_contiguous()
)
def fused_bias_glu(x: torch.Tensor, bias: torch.Tensor) -> torch.Tensor:
if not can_use_fused_bias_glu(x, bias):
raise RuntimeError("unsupported input for Sana fused bias-GLU")
batch, double_channels, height, width = x.shape
channels = double_channels // 2
out = torch.empty(
(batch, channels, height, width),
dtype=x.dtype,
device=x.device,
memory_format=torch.channels_last,
)
with torch.cuda.device(x.device):
_bias_glu_kernel[(triton.cdiv(out.numel(), 1024),)](
out, x, bias, out.numel(), channels=channels
)
return out
__all__ = [
"can_use_fused_bias_glu",
"can_use_fused_bias_silu",
"fused_bias_glu",
"fused_bias_silu",
]
@@ -6,11 +6,18 @@ import torch.nn.functional as F
from diffusers.models.embeddings import PixArtAlphaTextProjection, TimestepEmbedding
from sglang.kernels.ops.diffusion.bitexact_gate import BitExactFusionGate
from sglang.kernels.ops.diffusion.residual_gate_add import residual_gate_add
from sglang.kernels.ops.diffusion.triton.layernorm_modulate import (
can_use_fused_layernorm_modulate,
fused_layernorm_modulate_raw,
is_plain_layer_norm,
)
from sglang.kernels.ops.diffusion.triton.sana_conv_post import (
can_use_fused_bias_glu,
can_use_fused_bias_silu,
fused_bias_glu,
fused_bias_silu,
)
from sglang.multimodal_gen.configs.models.dits.sana import SanaConfig
from sglang.multimodal_gen.runtime.layers.layernorm import RMSNorm
from sglang.multimodal_gen.runtime.layers.linear import MergedColumnParallelLinear
@@ -29,6 +36,8 @@ _SANA_LN_MOD = BitExactFusionGate("Sana fused LN+modulate", per_signature=True)
_SANA_LN_MOD_SIGS = _SANA_LN_MOD.verified_sigs
assert _SANA_LN_MOD_SIGS is not None
_SANA_LN_MOD_DISABLED = False
_SANA_CONV_SILU = BitExactFusionGate("Sana fused conv bias-SiLU")
_SANA_CONV_GLU = BitExactFusionGate("Sana fused conv bias-GLU")
def _eager_ln_modulate(
@@ -149,6 +158,103 @@ def _mps_safe_conv2d(conv: nn.Conv2d, x: torch.Tensor) -> torch.Tensor:
).to(x.dtype)
def _use_sana_bcg_fast_path(x: torch.Tensor) -> bool:
if torch.compiler.is_compiling() or not x.is_cuda:
return False
return torch.cuda.is_current_stream_capturing() or (
torch.cuda.current_stream() != torch.cuda.default_stream()
)
def _conv2d_without_bias(conv: nn.Conv2d, x: torch.Tensor) -> torch.Tensor:
return F.conv2d(
x,
conv.weight,
None,
conv.stride,
conv.padding,
conv.dilation,
conv.groups,
)
def _sana_conv_bias_silu(conv: nn.Conv2d, x: torch.Tensor) -> torch.Tensor:
if conv.bias is None or not _use_sana_bcg_fast_path(x):
return F.silu(_mps_safe_conv2d(conv, x))
raw = _conv2d_without_bias(conv, x)
if not can_use_fused_bias_silu(raw, conv.bias):
return F.silu(raw + conv.bias[None, :, None, None])
verified = _SANA_CONV_SILU.verified
if not verified and not _SANA_CONV_SILU.can_attempt_once():
return F.silu(raw + conv.bias[None, :, None, None])
try:
out = fused_bias_silu(raw, conv.bias)
except Exception as exc:
_SANA_CONV_SILU.on_exception(exc, logger=logger)
return F.silu(raw + conv.bias[None, :, None, None])
if verified:
return out
return _SANA_CONV_SILU.accept_or_fallback(
out,
F.silu(raw + conv.bias[None, :, None, None]),
logger=logger,
mismatch_msg=(
"Sana fused conv bias-SiLU path is not bit-exact on this "
"platform; falling back to eager"
),
)
def _sana_conv_bias_glu(conv: nn.Conv2d, x: torch.Tensor) -> torch.Tensor:
if conv.bias is None or not _use_sana_bcg_fast_path(x):
hidden_states = _mps_safe_conv2d(conv, x)
hidden_states, gate = torch.chunk(hidden_states, 2, dim=1)
return hidden_states * F.silu(gate)
raw = _conv2d_without_bias(conv, x)
if not can_use_fused_bias_glu(raw, conv.bias):
hidden_states, gate = torch.chunk(
raw + conv.bias[None, :, None, None], 2, dim=1
)
return hidden_states * F.silu(gate)
verified = _SANA_CONV_GLU.verified
if not verified and not _SANA_CONV_GLU.can_attempt_once():
hidden_states, gate = torch.chunk(
raw + conv.bias[None, :, None, None], 2, dim=1
)
return hidden_states * F.silu(gate)
try:
out = fused_bias_glu(raw, conv.bias)
except Exception as exc:
_SANA_CONV_GLU.on_exception(exc, logger=logger)
hidden_states, gate = torch.chunk(
raw + conv.bias[None, :, None, None], 2, dim=1
)
return hidden_states * F.silu(gate)
if verified:
return out
biased = raw + conv.bias[None, :, None, None]
hidden_states, gate = torch.chunk(biased, 2, dim=1)
return _SANA_CONV_GLU.accept_or_fallback(
out,
hidden_states * F.silu(gate),
logger=logger,
mismatch_msg=(
"Sana fused conv bias-GLU path is not bit-exact on this "
"platform; falling back to eager"
),
)
def _sana_residual_gate_add(
residual: torch.Tensor, update: torch.Tensor, gate: torch.Tensor
) -> torch.Tensor:
if torch.compiler.is_compiling():
return residual + gate * update
return residual_gate_add(residual, update, gate)
def _mps_match_dtype(tensor: torch.Tensor, ref: torch.Tensor) -> torch.Tensor:
if ref.device.type == "mps" and tensor.dtype != ref.dtype:
return tensor.to(dtype=ref.dtype)
@@ -225,11 +331,8 @@ class GLUMBConv(nn.Module):
self.conv_point = nn.Conv2d(hidden_channels, out_channels, 1, 1, 0, bias=False)
def forward(self, hidden_states):
hidden_states = _mps_safe_conv2d(self.conv_inverted, hidden_states)
hidden_states = self.nonlinearity(hidden_states)
hidden_states = _mps_safe_conv2d(self.conv_depth, hidden_states)
hidden_states, gate = torch.chunk(hidden_states, 2, dim=1)
hidden_states = hidden_states * self.nonlinearity(gate)
hidden_states = _sana_conv_bias_silu(self.conv_inverted, hidden_states)
hidden_states = _sana_conv_bias_glu(self.conv_depth, hidden_states)
hidden_states = _mps_safe_conv2d(self.conv_point, hidden_states)
return hidden_states
@@ -376,7 +479,7 @@ class SanaTransformerBlock(nn.Module):
norm_hidden = _sana_ln_modulate(self.norm1, hidden_states, scale_msa, shift_msa)
attn_output = self.attn1(norm_hidden)
hidden_states = hidden_states + gate_msa * attn_output
hidden_states = _sana_residual_gate_add(hidden_states, attn_output, gate_msa)
attn_output = self.attn2(
hidden_states, encoder_hidden_states, encoder_attention_mask
@@ -387,7 +490,7 @@ class SanaTransformerBlock(nn.Module):
norm_hidden = norm_hidden.unflatten(1, (height, width)).permute(0, 3, 1, 2)
ff_output = self.ff(norm_hidden)
ff_output = ff_output.flatten(2, 3).permute(0, 2, 1)
hidden_states = hidden_states + gate_mlp * ff_output
hidden_states = _sana_residual_gate_add(hidden_states, ff_output, gate_mlp)
return hidden_states
@@ -484,7 +587,9 @@ class SanaTransformer2DModel(CachableDiT, LayerwiseOffloadableModuleMixin):
post_patch_width = width // p
hidden_states = _mps_safe_conv2d(self.patch_embed["proj"], hidden_states)
hidden_states = hidden_states.flatten(2).transpose(1, 2)
# One layout conversion here prevents every downstream LayerNorm from
# copying the transposed patch view independently.
hidden_states = hidden_states.flatten(2).transpose(1, 2).contiguous()
timestep_emb, embedded_timestep = self.time_embed(
timestep, hidden_dtype=hidden_states.dtype