Files
vllm-omni/vllm_omni/diffusion/layers/qwen_select01_modulation.py
T

492 lines
15 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
from __future__ import annotations
import torch
import torch.nn.functional as F
from vllm.platforms import current_platform
from vllm.triton_utils import HAS_TRITON, tl, triton
@triton.jit
def _layernorm_select01_kernel(
output_ptr,
gate_out_ptr,
x_ptr,
weight_ptr,
bias_ptr,
scale0_ptr,
shift0_ptr,
gate0_ptr,
scale1_ptr,
shift1_ptr,
gate1_ptr,
index_ptr,
inner_dim,
seq_len,
stride_x_row,
stride_out_row,
stride_gate_out_row,
stride_w,
stride_b,
stride_s0_b,
stride_s0_c,
stride_sh0_b,
stride_sh0_c,
stride_g0_b,
stride_g0_c,
stride_s1_b,
stride_s1_c,
stride_sh1_b,
stride_sh1_c,
stride_g1_b,
stride_g1_c,
stride_i_b,
stride_i_l,
eps,
has_weight: tl.constexpr,
has_bias: tl.constexpr,
block_n: tl.constexpr,
):
row = tl.program_id(0)
cols = tl.arange(0, block_n)
mask = cols < inner_dim
x_row_ptr = x_ptr + row * stride_x_row
x = tl.load(x_row_ptr + cols, mask=mask, other=0.0).to(tl.float32)
mean = tl.sum(x, axis=0) / inner_dim
xbar = tl.where(mask, x - mean, 0.0)
var = tl.sum(xbar * xbar, axis=0) / inner_dim
x_hat = (x - mean) * tl.rsqrt(var + eps)
if has_weight:
weight = tl.load(weight_ptr + cols * stride_w, mask=mask, other=1.0).to(tl.float32)
x_hat = x_hat * weight
if has_bias:
bias = tl.load(bias_ptr + cols * stride_b, mask=mask, other=0.0).to(tl.float32)
x_hat = x_hat + bias
batch_idx = row // seq_len
seq_idx = row % seq_len
idx = tl.load(index_ptr + batch_idx * stride_i_b + seq_idx * stride_i_l).to(tl.int1)
scale0_ptrs = scale0_ptr + batch_idx * stride_s0_b + cols * stride_s0_c
shift0_ptrs = shift0_ptr + batch_idx * stride_sh0_b + cols * stride_sh0_c
gate0_ptrs = gate0_ptr + batch_idx * stride_g0_b + cols * stride_g0_c
scale1_ptrs = scale1_ptr + batch_idx * stride_s1_b + cols * stride_s1_c
shift1_ptrs = shift1_ptr + batch_idx * stride_sh1_b + cols * stride_sh1_c
gate1_ptrs = gate1_ptr + batch_idx * stride_g1_b + cols * stride_g1_c
scale_ptrs = tl.where(idx, scale1_ptrs, scale0_ptrs)
shift_ptrs = tl.where(idx, shift1_ptrs, shift0_ptrs)
gate_ptrs = tl.where(idx, gate1_ptrs, gate0_ptrs)
scale = tl.load(scale_ptrs, mask=mask, other=0.0).to(tl.float32)
shift = tl.load(shift_ptrs, mask=mask, other=0.0).to(tl.float32)
gate = tl.load(gate_ptrs, mask=mask, other=0.0)
y = x_hat * (1.0 + scale) + shift
tl.store(output_ptr + row * stride_out_row + cols, y, mask=mask)
tl.store(gate_out_ptr + row * stride_gate_out_row + cols, gate, mask=mask)
@triton.jit
def _residual_layernorm_select01_kernel(
output_ptr,
residual_out_ptr,
gate_out_ptr,
x_ptr,
residual_ptr,
residual_gate_ptr,
weight_ptr,
bias_ptr,
scale0_ptr,
shift0_ptr,
gate0_ptr,
scale1_ptr,
shift1_ptr,
gate1_ptr,
index_ptr,
inner_dim,
seq_len,
stride_x_row,
stride_res_row,
stride_res_gate_row,
stride_out_row,
stride_res_out_row,
stride_gate_out_row,
stride_w,
stride_b,
stride_s0_b,
stride_s0_c,
stride_sh0_b,
stride_sh0_c,
stride_g0_b,
stride_g0_c,
stride_s1_b,
stride_s1_c,
stride_sh1_b,
stride_sh1_c,
stride_g1_b,
stride_g1_c,
stride_i_b,
stride_i_l,
eps,
has_weight: tl.constexpr,
has_bias: tl.constexpr,
block_n: tl.constexpr,
):
row = tl.program_id(0)
cols = tl.arange(0, block_n)
mask = cols < inner_dim
x = tl.load(x_ptr + row * stride_x_row + cols, mask=mask, other=0.0).to(tl.float32)
residual = tl.load(residual_ptr + row * stride_res_row + cols, mask=mask, other=0.0).to(tl.float32)
residual_gate = tl.load(
residual_gate_ptr + row * stride_res_gate_row + cols,
mask=mask,
other=0.0,
).to(tl.float32)
residual_out = residual + residual_gate * x
tl.store(residual_out_ptr + row * stride_res_out_row + cols, residual_out, mask=mask)
mean = tl.sum(residual_out, axis=0) / inner_dim
xbar = tl.where(mask, residual_out - mean, 0.0)
var = tl.sum(xbar * xbar, axis=0) / inner_dim
x_hat = (residual_out - mean) * tl.rsqrt(var + eps)
if has_weight:
weight = tl.load(weight_ptr + cols * stride_w, mask=mask, other=1.0).to(tl.float32)
x_hat = x_hat * weight
if has_bias:
bias = tl.load(bias_ptr + cols * stride_b, mask=mask, other=0.0).to(tl.float32)
x_hat = x_hat + bias
batch_idx = row // seq_len
seq_idx = row % seq_len
idx = tl.load(index_ptr + batch_idx * stride_i_b + seq_idx * stride_i_l).to(tl.int1)
scale0_ptrs = scale0_ptr + batch_idx * stride_s0_b + cols * stride_s0_c
shift0_ptrs = shift0_ptr + batch_idx * stride_sh0_b + cols * stride_sh0_c
gate0_ptrs = gate0_ptr + batch_idx * stride_g0_b + cols * stride_g0_c
scale1_ptrs = scale1_ptr + batch_idx * stride_s1_b + cols * stride_s1_c
shift1_ptrs = shift1_ptr + batch_idx * stride_sh1_b + cols * stride_sh1_c
gate1_ptrs = gate1_ptr + batch_idx * stride_g1_b + cols * stride_g1_c
scale_ptrs = tl.where(idx, scale1_ptrs, scale0_ptrs)
shift_ptrs = tl.where(idx, shift1_ptrs, shift0_ptrs)
gate_ptrs = tl.where(idx, gate1_ptrs, gate0_ptrs)
scale = tl.load(scale_ptrs, mask=mask, other=0.0).to(tl.float32)
shift = tl.load(shift_ptrs, mask=mask, other=0.0).to(tl.float32)
gate = tl.load(gate_ptrs, mask=mask, other=0.0)
y = x_hat * (1.0 + scale) + shift
tl.store(output_ptr + row * stride_out_row + cols, y, mask=mask)
tl.store(gate_out_ptr + row * stride_gate_out_row + cols, gate, mask=mask)
def split_select01_mod_params(mod_params: torch.Tensor) -> tuple[torch.Tensor, ...]:
shift, scale, gate = mod_params.chunk(3, dim=-1)
actual_batch = shift.size(0) // 2
if actual_batch == 0 or shift.size(0) != actual_batch * 2:
raise ValueError("select01 modulation expects mod_params batch dim to be 2 * batch")
return (
scale[:actual_batch],
shift[:actual_batch],
gate[:actual_batch],
scale[actual_batch:],
shift[actual_batch:],
gate[actual_batch:],
)
def select01_modulation_native(
mod_params: torch.Tensor,
index: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
scale0, shift0, gate0, scale1, shift1, gate1 = split_select01_mod_params(mod_params)
idx = index.to(dtype=torch.bool).unsqueeze(-1)
scale = torch.where(idx, scale1.unsqueeze(1), scale0.unsqueeze(1))
shift = torch.where(idx, shift1.unsqueeze(1), shift0.unsqueeze(1))
gate = torch.where(idx, gate1.unsqueeze(1), gate0.unsqueeze(1))
return scale, shift, gate
def _validate_select01_inputs(
x: torch.Tensor,
index: torch.Tensor,
tensors: tuple[torch.Tensor, ...],
) -> None:
if x.dim() != 3:
raise ValueError("x must be 3D [B, L, C]")
if index.dim() != 2 or index.shape[1] != x.shape[1] or index.shape[0] not in (1, x.shape[0]):
raise ValueError("index must be 2D [B or 1, L] and match x batch/sequence")
batch_size, _, hidden_size = x.shape
for tensor in tensors:
if tensor.dim() != 2 or tensor.shape != (batch_size, hidden_size):
raise ValueError("scale/shift/gate tensors must be 2D [B, C]")
def _maybe_contiguous(x: torch.Tensor) -> torch.Tensor:
return x if x.is_contiguous() else x.contiguous()
def _launch_layernorm_select01(
x: torch.Tensor,
weight: torch.Tensor | None,
bias: torch.Tensor | None,
scale0: torch.Tensor,
shift0: torch.Tensor,
gate0: torch.Tensor,
scale1: torch.Tensor,
shift1: torch.Tensor,
gate1: torch.Tensor,
index: torch.Tensor,
eps: float,
) -> tuple[torch.Tensor, torch.Tensor]:
_validate_select01_inputs(x, index, (scale0, shift0, gate0, scale1, shift1, gate1))
batch_size, seq_len, hidden_size = x.shape
output = torch.empty_like(x)
gate_out = torch.empty_like(x)
x_2d = _maybe_contiguous(x).view(batch_size * seq_len, hidden_size)
output_2d = output.view(batch_size * seq_len, hidden_size)
gate_out_2d = gate_out.view(batch_size * seq_len, hidden_size)
index = _maybe_contiguous(index)
scale0 = _maybe_contiguous(scale0)
shift0 = _maybe_contiguous(shift0)
gate0 = _maybe_contiguous(gate0)
scale1 = _maybe_contiguous(scale1)
shift1 = _maybe_contiguous(shift1)
gate1 = _maybe_contiguous(gate1)
weight_arg = _maybe_contiguous(weight) if weight is not None else x_2d
bias_arg = _maybe_contiguous(bias) if bias is not None else x_2d
block_n = min(65536 // x_2d.element_size(), triton.next_power_of_2(hidden_size))
if hidden_size > block_n:
raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
_layernorm_select01_kernel[(batch_size * seq_len,)](
output_2d,
gate_out_2d,
x_2d,
weight_arg,
bias_arg,
scale0,
shift0,
gate0,
scale1,
shift1,
gate1,
index,
hidden_size,
seq_len,
x_2d.stride(0),
output_2d.stride(0),
gate_out_2d.stride(0),
weight_arg.stride(0) if weight is not None else 0,
bias_arg.stride(0) if bias is not None else 0,
scale0.stride(0),
scale0.stride(1),
shift0.stride(0),
shift0.stride(1),
gate0.stride(0),
gate0.stride(1),
scale1.stride(0),
scale1.stride(1),
shift1.stride(0),
shift1.stride(1),
gate1.stride(0),
gate1.stride(1),
index.stride(0) if index.shape[0] > 1 else 0,
index.stride(1),
eps,
has_weight=weight is not None,
has_bias=bias is not None,
block_n=block_n,
num_warps=4,
num_stages=4,
)
return output, gate_out
def _launch_residual_layernorm_select01(
x: torch.Tensor,
residual: torch.Tensor,
residual_gate: torch.Tensor,
weight: torch.Tensor | None,
bias: torch.Tensor | None,
scale0: torch.Tensor,
shift0: torch.Tensor,
gate0: torch.Tensor,
scale1: torch.Tensor,
shift1: torch.Tensor,
gate1: torch.Tensor,
index: torch.Tensor,
eps: float,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
_validate_select01_inputs(x, index, (scale0, shift0, gate0, scale1, shift1, gate1))
if residual.shape != x.shape:
raise ValueError("residual must have the same shape as x")
if residual_gate.shape != x.shape:
raise ValueError("residual_gate must have the same shape as x")
batch_size, seq_len, hidden_size = x.shape
output = torch.empty_like(x)
residual_out = torch.empty_like(x)
gate_out = torch.empty_like(x)
x_2d = _maybe_contiguous(x).view(batch_size * seq_len, hidden_size)
residual_2d = _maybe_contiguous(residual).view(batch_size * seq_len, hidden_size)
residual_gate_2d = _maybe_contiguous(residual_gate).view(batch_size * seq_len, hidden_size)
output_2d = output.view(batch_size * seq_len, hidden_size)
residual_out_2d = residual_out.view(batch_size * seq_len, hidden_size)
gate_out_2d = gate_out.view(batch_size * seq_len, hidden_size)
index = _maybe_contiguous(index)
scale0 = _maybe_contiguous(scale0)
shift0 = _maybe_contiguous(shift0)
gate0 = _maybe_contiguous(gate0)
scale1 = _maybe_contiguous(scale1)
shift1 = _maybe_contiguous(shift1)
gate1 = _maybe_contiguous(gate1)
weight_arg = _maybe_contiguous(weight) if weight is not None else x_2d
bias_arg = _maybe_contiguous(bias) if bias is not None else x_2d
block_n = min(65536 // x_2d.element_size(), triton.next_power_of_2(hidden_size))
if hidden_size > block_n:
raise RuntimeError("This layer norm doesn't support feature dim >= 64KB.")
_residual_layernorm_select01_kernel[(batch_size * seq_len,)](
output_2d,
residual_out_2d,
gate_out_2d,
x_2d,
residual_2d,
residual_gate_2d,
weight_arg,
bias_arg,
scale0,
shift0,
gate0,
scale1,
shift1,
gate1,
index,
hidden_size,
seq_len,
x_2d.stride(0),
residual_2d.stride(0),
residual_gate_2d.stride(0),
output_2d.stride(0),
residual_out_2d.stride(0),
gate_out_2d.stride(0),
weight_arg.stride(0) if weight is not None else 0,
bias_arg.stride(0) if bias is not None else 0,
scale0.stride(0),
scale0.stride(1),
shift0.stride(0),
shift0.stride(1),
gate0.stride(0),
gate0.stride(1),
scale1.stride(0),
scale1.stride(1),
shift1.stride(0),
shift1.stride(1),
gate1.stride(0),
gate1.stride(1),
index.stride(0) if index.shape[0] > 1 else 0,
index.stride(1),
eps,
has_weight=weight is not None,
has_bias=bias is not None,
block_n=block_n,
num_warps=4,
num_stages=4,
)
return output, residual_out, gate_out
def can_use_qwen_select01_triton(x: torch.Tensor) -> bool:
return HAS_TRITON and current_platform.is_cuda() and x.is_cuda
def fused_layernorm_select01(
x: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor,
eps: float,
weight: torch.Tensor | None = None,
bias: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor]:
scale0, shift0, gate0, scale1, shift1, gate1 = split_select01_mod_params(mod_params)
is_compiling = torch.compiler.is_compiling()
if can_use_qwen_select01_triton(x) and not is_compiling:
return _launch_layernorm_select01(
x,
weight,
bias,
scale0,
shift0,
gate0,
scale1,
shift1,
gate1,
index,
eps,
)
scale, shift, gate = select01_modulation_native(mod_params, index)
out = F.layer_norm(
x.float(),
(x.shape[-1],),
weight=weight.float() if weight is not None else None,
bias=bias.float() if bias is not None else None,
eps=eps,
).to(x.dtype)
return out * (1 + scale) + shift, gate
def fused_residual_layernorm_select01(
x: torch.Tensor,
residual: torch.Tensor,
residual_gate: torch.Tensor,
mod_params: torch.Tensor,
index: torch.Tensor,
eps: float,
weight: torch.Tensor | None = None,
bias: torch.Tensor | None = None,
) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
scale0, shift0, gate0, scale1, shift1, gate1 = split_select01_mod_params(mod_params)
is_compiling = torch.compiler.is_compiling()
if can_use_qwen_select01_triton(x) and not is_compiling:
return _launch_residual_layernorm_select01(
x,
residual,
residual_gate,
weight,
bias,
scale0,
shift0,
gate0,
scale1,
shift1,
gate1,
index,
eps,
)
scale, shift, gate = select01_modulation_native(mod_params, index)
residual_out = residual + residual_gate * x
out = F.layer_norm(
residual_out.float(),
(residual_out.shape[-1],),
weight=weight.float() if weight is not None else None,
bias=bias.float() if bias is not None else None,
eps=eps,
).to(residual_out.dtype)
return out * (1 + scale) + shift, residual_out, gate