[Perf] Minimax-H3 support fused RMSNorm and RoPE opt (#5801)

Signed-off-by: Fan <fan@FandeMacBook-Air.local>
Co-authored-by: Fan <fan@FandeMacBook-Air.local>
This commit is contained in:
fan2956andFan authored and GitHub committed 2026-08-06 11:39:27 +08:00
1 parent 240e6655be
commit f2e3d2e25b
6 files changed
+145 -39

No files matched your search

+2 -2
View File
@@ -65,9 +65,9 @@ class LayerNorm(nn.LayerNorm, CustomOp):
class RMSNorm(CustomOp):
def __init__(self, hidden_size: int, eps: float = 1e-6) -> None:
def __init__(self, hidden_size: int, eps: float = 1e-6, dtype: torch.dtype = torch.float32) -> None:
super().__init__()
self.weight = nn.Parameter(torch.ones(hidden_size))
self.weight = nn.Parameter(torch.ones(hidden_size, dtype=dtype))
self.variance_epsilon = eps
def _forward_fused(self, x: torch.Tensor) -> torch.Tensor:
+21 -13
View File
@@ -89,10 +89,11 @@ class RotaryEmbedding(CustomOp):
of 1st half and 2nd half (GPT-NeoX style).
"""
def __init__(self, is_neox_style: bool = False) -> None:
def __init__(self, is_neox_style: bool = False, half_head_dim: bool = True) -> None:
super().__init__()
self.is_neox_style = is_neox_style
self.interleaved = not is_neox_style
self.half_head_dim = half_head_dim
self.apply_rotary_emb_flash_attn = None
self.has_mindie = False
# ``find_spec("flash_attn")`` is True as long as *any* package publishes
@@ -110,6 +111,21 @@ class RotaryEmbedding(CustomOp):
if find_spec("mindiesd") is not None:
self.has_mindie = True
def _prepare_half_head_dim_cos_sin(
self,
cos: torch.Tensor,
sin: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
"""Normalize shared cos/sin to the half-dim layout used by CUDA/native kernels."""
if cos.dim() == 3:
# All batch elements share the same rotary position encoding.
cos = cos[0]
sin = sin[0]
if not self.half_head_dim:
cos = cos[..., ::2] if self.interleaved else cos[..., : cos.shape[-1] // 2]
sin = sin[..., ::2] if self.interleaved else sin[..., : sin.shape[-1] // 2]
return cos, sin
def forward_cuda(
self,
x: torch.Tensor,
@@ -118,10 +134,7 @@ class RotaryEmbedding(CustomOp):
) -> torch.Tensor:
from vllm.vllm_flash_attn.layers.rotary import apply_rotary_emb
if cos.dim() == 3:
# (B, S, D/2) -> (S, D/2)
cos = cos[0]
sin = sin[0]
cos, sin = self._prepare_half_head_dim_cos_sin(cos, sin)
x, squeezed = _ensure_batch_dim(x)
output = apply_rotary_emb(
@@ -141,10 +154,7 @@ class RotaryEmbedding(CustomOp):
if self.apply_rotary_emb_flash_attn is None:
return self.forward_cuda(x, cos, sin)
if cos.dim() == 3:
# (B, S, D/2) -> (S, D/2)
cos = cos[0]
sin = sin[0]
cos, sin = self._prepare_half_head_dim_cos_sin(cos, sin)
x, squeezed = _ensure_batch_dim(x)
output = self.apply_rotary_emb_flash_attn(
@@ -162,7 +172,7 @@ class RotaryEmbedding(CustomOp):
sin: torch.Tensor,
) -> torch.Tensor:
if self.has_mindie:
return apply_rotary_emb_mindiesd(x, cos, sin, self.interleaved)
return apply_rotary_emb_mindiesd(x, cos, sin, self.interleaved, self.half_head_dim)
else:
return self.forward_native(x, cos, sin)
@@ -191,9 +201,7 @@ class RotaryEmbedding(CustomOp):
# All batch elements share the same rotary position encoding.
# Strip the batch dim so the underlying op broadcasts over the batch,
# consistent with forward_cuda / forward_hip / apply_rotary_emb_mindiesd.
if cos.dim() == 3:
cos = cos[0]
sin = sin[0]
cos, sin = self._prepare_half_head_dim_cos_sin(cos, sin)
return apply_rotary_emb_torch(
x,
cos,
@@ -33,6 +33,8 @@ from vllm_omni.diffusion.distributed.sp_plan import (
SequenceParallelInput,
SequenceParallelOutput,
)
from vllm_omni.diffusion.layers.norm import RMSNorm
from vllm_omni.diffusion.layers.rope import RotaryEmbedding
if TYPE_CHECKING:
from vllm.model_executor.layers.quantization.base_config import (
@@ -168,16 +170,11 @@ def _reorder_grouped_qkv_to_qkv(
)
def _norm(size: int, *, eps: float, dtype: torch.dtype = _BF16_DTYPE) -> nn.RMSNorm:
def _norm(size: int, *, eps: float, dtype: torch.dtype = _BF16_DTYPE) -> RMSNorm:
# RMSNorm uses fp32 accumulation with bf16 inputs and outputs.
# torch.nn.RMSNorm upcasts reduced-precision inputs for the variance
# reduction, matching that accumulation semantic.
return nn.RMSNorm(size, eps=eps, dtype=dtype)
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
x1, x2 = torch.chunk(x, 2, dim=-1)
return torch.cat((-x2, x1), dim=-1)
return RMSNorm(size, eps=eps, dtype=dtype)
def _modulate_scale_shift(
@@ -230,20 +227,6 @@ class MiniMaxH3Rope(nn.Module):
return torch.cat((half, half), dim=-1) # [S, 96]
def _apply_rope(x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
"""Rotate the first rot_dim head dims; pass the rest through.
x: [T, heads, head_dim]; freqs: [T, rot_dim]. In the unfused path, cos/sin
are cast to the activation dtype before the elementwise math.
"""
rot_dim = freqs.shape[-1]
x_rot, x_pass = x[..., :rot_dim], x[..., rot_dim:]
cos = torch.cos(freqs).to(x.dtype).unsqueeze(1) # [T, 1, rot_dim]
sin = torch.sin(freqs).to(x.dtype).unsqueeze(1)
x_rot = (x_rot * cos) + (_rotate_half(x_rot) * sin)
return torch.cat((x_rot, x_pass), dim=-1)
class MiniMaxH3TimeEmbedder(nn.Module):
def __init__(
self,
@@ -349,6 +332,7 @@ class MiniMaxH3Attention(nn.Module):
self.num_kv_heads = self.qkv_proj.num_kv_heads
self.q_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps)
self.k_norm = _norm(arch.attention_head_dim, eps=arch.qk_norm_eps)
self.rope = RotaryEmbedding(is_neox_style=True, half_head_dim=False)
self.out_proj = RowParallelLinear(
inner_dim,
arch.hidden_size,
@@ -367,6 +351,19 @@ class MiniMaxH3Attention(nn.Module):
skip_sequence_parallel=skip_sequence_parallel,
)
def _apply_rope(self, x: torch.Tensor, freqs: torch.Tensor) -> torch.Tensor:
"""Rotate the first rot_dim head dims; pass the rest through.
x: [T, heads, head_dim]; freqs: [T, rot_dim]. In the unfused path, cos/sin
are cast to the activation dtype before the elementwise math.
"""
rot_dim = freqs.shape[-1]
x_rot, x_pass = x[..., :rot_dim], x[..., rot_dim:]
cos = torch.cos(freqs).to(x.dtype) # [T, rot_dim]
sin = torch.sin(freqs).to(x.dtype)
x_rot = self.rope(x_rot, cos, sin)
return torch.cat((x_rot, x_pass), dim=-1)
@torch.compiler.disable
def _run_packed_attention(
self,
@@ -450,8 +447,8 @@ class MiniMaxH3Attention(nn.Module):
q = self.q_norm(q)
k = self.k_norm(k)
if rope_freqs is not None:
q = _apply_rope(q, rope_freqs)
k = _apply_rope(k, rope_freqs)
q = self._apply_rope(q, rope_freqs)
k = self._apply_rope(k, rope_freqs)
# The packed layout uses a second document for alignment padding.
# Local/Ulysses backends unpad it, while Ring keeps aligned rows for