[Diffusion] Qwen image edit support qknorm optimization (#16062)

This commit is contained in:
Xiaoyu Zhang
2025-12-29 21:37:24 +08:00
committed by GitHub
parent d48723b77d
commit 24616c5234
2 changed files with 63 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ import torch.nn as nn
import torch.nn.functional as F
from sgl_kernel import fused_add_rmsnorm, rmsnorm
from sglang.jit_kernel.norm import can_use_fused_inplace_qknorm, fused_inplace_qknorm
from sglang.multimodal_gen.runtime.layers.custom_op import CustomOp
from sglang.multimodal_gen.runtime.layers.triton_ops import (
fuse_scale_shift_kernel,
@@ -421,3 +422,43 @@ class LayerNormScaleShift(nn.Module):
output = output.to(x.dtype)
return output
def apply_qk_norm(
q: torch.Tensor,
k: torch.Tensor,
q_norm: "RMSNorm",
k_norm: "RMSNorm",
head_dim: int,
allow_inplace: bool = True,
) -> Tuple[torch.Tensor, torch.Tensor]:
"""Apply QK normalization for query and key tensors.
Minimal multimodal_gen-only implementation: only the JIT fused inplace
QK-norm kernel path is supported (no fallback).
"""
batch_size = q.size(0)
q_eps = q_norm.variance_epsilon
k_eps = k_norm.variance_epsilon
# Only try fused path on CUDA and when it won't introduce implicit copies.
if (
q.is_cuda
and allow_inplace
and (q_eps == k_eps)
and can_use_fused_inplace_qknorm(head_dim)
):
fused_inplace_qknorm(
q=q.view(batch_size, -1, head_dim),
k=k.view(batch_size, -1, head_dim),
q_weight=q_norm.weight,
k_weight=k_norm.weight,
head_dim=head_dim,
eps=q_eps,
)
return q, k
raise RuntimeError(
"apply_qk_norm: fused inplace QK-norm is not applicable "
"(expected CUDA, contiguous q/k, matching eps, and supported head_dim)"
)

View File

@@ -17,7 +17,11 @@ from diffusers.models.normalization import AdaLayerNormContinuous
from sglang.multimodal_gen.configs.models.dits.qwenimage import QwenImageDitConfig
from sglang.multimodal_gen.runtime.layers.attention import USPAttention
from sglang.multimodal_gen.runtime.layers.layernorm import LayerNorm, RMSNorm
from sglang.multimodal_gen.runtime.layers.layernorm import (
LayerNorm,
RMSNorm,
apply_qk_norm,
)
from sglang.multimodal_gen.runtime.layers.linear import ReplicatedLinear
from sglang.multimodal_gen.runtime.layers.rotary_embedding import (
apply_flashinfer_rope_qk_inplace,
@@ -547,14 +551,23 @@ class QwenImageCrossAttention(nn.Module):
txt_value = txt_value.unflatten(-1, (self.num_heads, -1))
# Apply QK normalization
if self.norm_q is not None:
img_query = self.norm_q(img_query)
if self.norm_k is not None:
img_key = self.norm_k(img_key)
if self.norm_added_q is not None:
txt_query = self.norm_added_q(txt_query)
if self.norm_added_k is not None:
txt_key = self.norm_added_k(txt_key)
if self.qk_norm:
img_query, img_key = apply_qk_norm(
q=img_query,
k=img_key,
q_norm=self.norm_q,
k_norm=self.norm_k,
head_dim=img_query.shape[-1],
allow_inplace=True,
)
txt_query, txt_key = apply_qk_norm(
q=txt_query,
k=txt_key,
q_norm=self.norm_added_q,
k_norm=self.norm_added_k,
head_dim=txt_query.shape[-1],
allow_inplace=True,
)
# Apply RoPE
if image_rotary_emb is not None: