[Cherry-pick][v0.18.0.post1][PERF] Wan2.2 support adalayernorm on NPU (#2575)

Signed-off-by: fan2956 <zhoufan53@huawei.com>
Signed-off-by: gcanlin <canlinguosdu@gmail.com>
Co-authored-by: gcanlin <canlinguosdu@gmail.com>
This commit is contained in:
fan2956andgcanlin authored and GitHub committed 2026-04-16 08:47:38 +08:00
1 parent 91fa5c51d7
commit f70fd1f58b
4 files changed
+45 -81

No files matched your search

+2 -1
View File
@@ -221,7 +221,8 @@ def extract_qwen_context(
block = module.transformer_blocks[0]
img_mod_params = block.img_mod(temb)
img_mod1, _ = img_mod_params.chunk(2, dim=-1)
img_modulated, _ = block.img_norm1(hidden_states, img_mod1)
img_scale1, img_shift1, _ = block._modulate(img_mod1)
img_modulated = block.img_norm1(hidden_states, img_scale1, img_shift1)
# ============================================================================
# DEFINE TRANSFORMER EXECUTION (Qwen-specific)
+18 -62
View File
@@ -24,102 +24,58 @@ class AdaLayerNorm(CustomOp):
self.hidden_size = hidden_size
self.layernorm = nn.LayerNorm(self.hidden_size, elementwise_affine=self.elementwise_affine, eps=self.eps)
def preprocess(
self,
mod_params: torch.Tensor,
index: torch.Tensor = None,
) -> torch.Tensor:
# shift: b d, scale: b d, gate: b d
shift, scale, gate = mod_params.chunk(3, dim=-1)
if index is not None:
# Assuming mod_params batch dim is 2*actual_batch (chunked into 2 parts)
# So shift, scale, gate have shape [2*actual_batch, d]
actual_batch = shift.size(0) // 2
shift_0, shift_1 = shift[:actual_batch], shift[actual_batch:] # each: [actual_batch, d]
scale_0, scale_1 = scale[:actual_batch], scale[actual_batch:]
gate_0, gate_1 = gate[:actual_batch], gate[actual_batch:]
# index: [b, l] where b is actual batch size
# Expand to [b, l, 1] to match feature dimension
index_expanded = index.unsqueeze(-1) # [b, l, 1]
# Expand chunks to [b, 1, d] then broadcast to [b, l, d]
shift_0_exp = shift_0.unsqueeze(1) # [b, 1, d]
shift_1_exp = shift_1.unsqueeze(1) # [b, 1, d]
scale_0_exp = scale_0.unsqueeze(1)
scale_1_exp = scale_1.unsqueeze(1)
gate_0_exp = gate_0.unsqueeze(1)
gate_1_exp = gate_1.unsqueeze(1)
# Use torch.where to select based on index
shift_result = torch.where(index_expanded == 0, shift_0_exp, shift_1_exp)
scale_result = torch.where(index_expanded == 0, scale_0_exp, scale_1_exp)
gate_result = torch.where(index_expanded == 0, gate_0_exp, gate_1_exp)
else:
shift_result = shift.unsqueeze(1)
scale_result = scale.unsqueeze(1)
gate_result = gate.unsqueeze(1)
return shift_result, scale_result, gate_result
def forward_cuda(
self,
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor = None,
scale: torch.Tensor,
shift: torch.Tensor,
) -> torch.Tensor:
return self.forward_native(x, mod_params, index)
return self.forward_native(x, scale, shift)
def forward_hip(
self,
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor = None,
scale: torch.Tensor,
shift: torch.Tensor,
) -> torch.Tensor:
return self.forward_native(x, mod_params, index)
return self.forward_native(x, scale, shift)
def forward_npu(
self,
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor = None,
scale: torch.Tensor,
shift: torch.Tensor,
) -> torch.Tensor:
shift_result, scale_result, gate_result = self.preprocess(mod_params, index)
if _HAS_MINDIESD:
try:
from mindiesd import layernorm_scale_shift
output = layernorm_scale_shift(self.layernorm, x, scale_result, shift_result, fused=True)
output = layernorm_scale_shift(self.layernorm, x, scale, shift, fused=True)
return output, gate_result
return output
except ImportError as e:
logger.warning_once(f"mindiesd import failed, falling back to torch_npu: {e}")
import torch_npu
output = (
torch_npu.npu_layer_norm_eval(x, normalized_shape=[self.hidden_size], eps=self.eps) * (1 + scale_result)
+ shift_result
torch_npu.npu_layer_norm_eval(x, normalized_shape=[self.hidden_size], eps=self.eps) * (1 + scale) + shift
)
return output, gate_result
return output
def forward_xpu(
self,
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor = None,
scale: torch.Tensor,
shift: torch.Tensor,
) -> torch.Tensor:
return self.forward_native(x, mod_params, index)
return self.forward_native(x, scale, shift)
def forward_native(
self,
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor = None,
scale: torch.Tensor,
shift: torch.Tensor,
) -> torch.Tensor:
shift_result, scale_result, gate_result = self.preprocess(mod_params, index)
return self.layernorm(x) * (1 + scale_result) + shift_result, gate_result
return self.layernorm(x) * (1 + scale) + shift
@@ -743,9 +743,9 @@ class QwenImageTransformerBlock(nn.Module):
self.zero_cond_t = zero_cond_t
def _modulate(self, x, mod_params, index=None):
def _modulate(self, mod_params, index=None):
"""Apply modulation to input tensor"""
# x: b l d, shift: b d, scale: b d, gate: b d
# shift: b d, scale: b d, gate: b d
shift, scale, gate = mod_params.chunk(3, dim=-1)
if index is not None:
@@ -777,7 +777,7 @@ class QwenImageTransformerBlock(nn.Module):
scale_result = scale.unsqueeze(1)
gate_result = gate.unsqueeze(1)
return x * (1 + scale_result) + shift_result, gate_result
return scale_result, shift_result, gate_result
def forward(
self,
@@ -803,10 +803,12 @@ class QwenImageTransformerBlock(nn.Module):
txt_mod1, txt_mod2 = txt_mod_params.chunk(2, dim=-1) # Each [B, 3*dim]
# Process image stream - norm1 + modulation
img_modulated, img_gate1 = self.img_norm1(hidden_states, img_mod1, modulate_index)
img_scale1, img_shift1, img_gate1 = self._modulate(img_mod1, modulate_index)
img_modulated = self.img_norm1(hidden_states, img_scale1, img_shift1)
# Process text stream - norm1 + modulation
txt_modulated, txt_gate1 = self.txt_norm1(encoder_hidden_states, txt_mod1)
txt_scale1, txt_shift1, txt_gate1 = self._modulate(txt_mod1)
txt_modulated = self.txt_norm1(encoder_hidden_states, txt_scale1, txt_shift1)
# Use QwenAttnProcessor2_0 for joint attention computation
# This directly implements the DoubleStreamLayerMegatron logic:
@@ -831,13 +833,16 @@ class QwenImageTransformerBlock(nn.Module):
encoder_hidden_states = encoder_hidden_states + txt_gate1 * txt_attn_output
# Process image stream - norm2 + MLP
img_modulated2, img_gate2 = self.img_norm2(hidden_states, img_mod2, modulate_index)
img_scale2, img_shift2, img_gate2 = self._modulate(img_mod2, modulate_index)
img_modulated2 = self.img_norm2(hidden_states, img_scale2, img_shift2)
img_mlp_output = self.img_mlp(img_modulated2)
hidden_states = hidden_states + img_gate2 * img_mlp_output
# Process text stream - norm2 + MLP
txt_modulated2, txt_gate2 = self.txt_norm2(encoder_hidden_states, txt_mod2)
txt_scale2, txt_shift2, txt_gate2 = self._modulate(txt_mod2)
txt_modulated2 = self.txt_norm2(encoder_hidden_states, txt_scale2, txt_shift2)
txt_mlp_output = self.txt_mlp(txt_modulated2)
encoder_hidden_states = encoder_hidden_states + txt_gate2 * txt_mlp_output
@@ -24,6 +24,7 @@ from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
from vllm_omni.diffusion.attention.layer import Attention
from vllm_omni.diffusion.layers.adalayernorm import AdaLayerNorm
from vllm_omni.diffusion.distributed.sp_plan import (
SequenceParallelInput,
SequenceParallelOutput,
@@ -619,7 +620,8 @@ class WanTransformerBlock(nn.Module):
head_dim = dim // num_heads
# 1. Self-attention
self.norm1 = FP32LayerNorm(dim, eps, elementwise_affine=False)
self.norm1 = AdaLayerNorm(dim, elementwise_affine=False, eps=eps)
self.attn1 = WanSelfAttention(
dim=dim,
num_heads=num_heads,
@@ -639,7 +641,7 @@ class WanTransformerBlock(nn.Module):
# 3. Feed-forward
self.ffn = WanFeedForward(dim=dim, inner_dim=ffn_dim, dim_out=dim)
self.norm3 = FP32LayerNorm(dim, eps, elementwise_affine=False)
self.norm3 = AdaLayerNorm(dim, elementwise_affine=False, eps=eps)
# Scale-shift table for modulation
self.scale_shift_table = nn.Parameter(torch.randn(1, 6, dim) / dim**0.5)
@@ -655,7 +657,7 @@ class WanTransformerBlock(nn.Module):
if temb.ndim == 4:
# temb: batch_size, seq_len, 6, inner_dim (wan2.2 ti2v)
shift_msa, scale_msa, gate_msa, c_shift_msa, c_scale_msa, c_gate_msa = (
self.scale_shift_table.unsqueeze(0) + temb.float()
self.scale_shift_table.unsqueeze(0) + temb
).chunk(6, dim=2)
shift_msa = shift_msa.squeeze(2)
scale_msa = scale_msa.squeeze(2)
@@ -666,25 +668,25 @@ class WanTransformerBlock(nn.Module):
else:
# temb: batch_size, 6, inner_dim (wan2.1/wan2.2 14B)
shift_msa, scale_msa, gate_msa, c_shift_msa, c_scale_msa, c_gate_msa = (
self.scale_shift_table + temb.float()
self.scale_shift_table + temb
).chunk(6, dim=1)
# 1. Self-attention
norm_hidden_states = (self.norm1(hidden_states.float()) * (1 + scale_msa) + shift_msa).type_as(hidden_states)
norm_hidden_states = self.norm1(hidden_states, scale_msa, shift_msa).type_as(hidden_states)
attn_output = self.attn1(norm_hidden_states, rotary_emb, hidden_states_mask)
hidden_states = (hidden_states.float() + attn_output * gate_msa).type_as(hidden_states)
hidden_states = (hidden_states + attn_output * gate_msa).type_as(hidden_states)
# 2. Cross-attention
norm_hidden_states = self.norm2(hidden_states.float()).type_as(hidden_states)
norm_hidden_states = self.norm2(hidden_states).type_as(hidden_states)
attn_output = self.attn2(norm_hidden_states, encoder_hidden_states)
hidden_states = hidden_states + attn_output
# 3. Feed-forward
norm_hidden_states = (self.norm3(hidden_states.float()) * (1 + c_scale_msa) + c_shift_msa).type_as(
norm_hidden_states = self.norm3(hidden_states, c_scale_msa, c_shift_msa).type_as(
hidden_states
)
ff_output = self.ffn(norm_hidden_states)
hidden_states = (hidden_states.float() + ff_output.float() * c_gate_msa).type_as(hidden_states)
hidden_states = (hidden_states + ff_output * c_gate_msa).type_as(hidden_states)
return hidden_states
@@ -853,7 +855,7 @@ class WanTransformer3DModel(nn.Module):
)
# 4. Output norm & projection
self.norm_out = FP32LayerNorm(inner_dim, eps, elementwise_affine=False)
self.norm_out = AdaLayerNorm(inner_dim, elementwise_affine=False, eps=eps)
self.proj_out = nn.Linear(inner_dim, out_channels * math.prod(patch_size))
# SP helper modules
@@ -941,7 +943,7 @@ class WanTransformer3DModel(nn.Module):
shift = shift.unsqueeze(1)
scale = scale.unsqueeze(1)
hidden_states = (self.norm_out(hidden_states.float()) * (1 + scale) + shift).type_as(hidden_states)
hidden_states = self.norm_out(hidden_states, scale, shift).type_as(hidden_states)
hidden_states = self.proj_out(hidden_states)
hidden_states = hidden_states.reshape(