Fusing RMSNormTP in minimax_m2 (#14416)

Co-authored-by: xuebi <xuebi@minimaxi.com>
This commit is contained in:
Roger Young
2025-12-30 10:22:07 -08:00
committed by GitHub
co-authored by xuebi
parent ba67e006a7
commit d17b9e6392
+189 -2
View File
@@ -19,6 +19,8 @@ import logging
from typing import Iterable, Optional, Set, Tuple, Union from typing import Iterable, Optional, Set, Tuple, Union
import torch import torch
import triton
import triton.language as tl
from torch import nn from torch import nn
from transformers import PretrainedConfig from transformers import PretrainedConfig
@@ -73,6 +75,164 @@ from sglang.srt.utils import (
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@triton.jit
def rmsnorm_sumsq_kernel_serial(
x1_ptr, # T* [B, D]
x2_ptr, # T* [B, D]
stride_x1, # int
stride_x2, # int
sum_sq_ptr, # float* [B]
B, # int
D1, # int
D2, # int
BLOCK_SIZE1: tl.constexpr,
BLOCK_SIZE2: tl.constexpr,
):
row_id = tl.program_id(0)
x1_row = x1_ptr + row_id * stride_x1
x2_row = x2_ptr + row_id * stride_x2
offsets1 = tl.arange(0, BLOCK_SIZE1)
mask1 = offsets1 < D1
offsets2 = tl.arange(0, BLOCK_SIZE2)
mask2 = offsets2 < D2
x1 = tl.load(x1_row + offsets1, mask=mask1, other=0.0)
x2 = tl.load(x2_row + offsets2, mask=mask2, other=0.0)
x1_f32 = x1.to(tl.float32)
sum_sq1 = tl.sum(x1_f32 * x1_f32, axis=0)
x2_f32 = x2.to(tl.float32)
sum_sq2 = tl.sum(x2_f32 * x2_f32, axis=0)
tl.store(sum_sq_ptr + row_id, sum_sq1)
tl.store(sum_sq_ptr + row_id + B, sum_sq2)
@triton.jit
def rmsnorm_apply_kernel_serial(
x1_ptr, # T* [B, D]
x2_ptr, # T* [B, D]
w1_ptr, # T* [D]
w2_ptr, # T* [D]
sum_sq_ptr, # float* [B]
out1_ptr, # T* [B, D]
out2_ptr, # T* [B, D]
B, # int
D1, # int
D2, # int
stride_x1, # int
stride_x2, # int
tp_world, # int
eps, # float
BLOCK_SIZE1: tl.constexpr,
BLOCK_SIZE2: tl.constexpr,
):
row_id = tl.program_id(0)
x1_row = x1_ptr + row_id * stride_x1
x2_row = x2_ptr + row_id * stride_x2
out1_row = out1_ptr + row_id * stride_x1
out2_row = out2_ptr + row_id * stride_x2
sum_sq1 = tl.load(sum_sq_ptr + row_id)
sum_sq2 = tl.load(sum_sq_ptr + row_id + B)
inv_rms1 = tl.rsqrt(sum_sq1 / D1 / tp_world + eps)
inv_rms2 = tl.rsqrt(sum_sq2 / D2 / tp_world + eps)
offsets1 = tl.arange(0, BLOCK_SIZE1)
offsets2 = tl.arange(0, BLOCK_SIZE2)
mask1 = offsets1 < D1
mask2 = offsets2 < D2
x1 = tl.load(x1_row + offsets1, mask=mask1, other=0.0)
w1 = tl.load(w1_ptr + offsets1, mask=mask1, other=1.0)
x2 = tl.load(x2_row + offsets2, mask=mask2, other=0.0)
w2 = tl.load(w2_ptr + offsets2, mask=mask2, other=1.0)
out1 = (x1.to(tl.float32) * inv_rms1 * w1.to(tl.float32)).to(x1.dtype)
out2 = (x2.to(tl.float32) * inv_rms2 * w2.to(tl.float32)).to(x2.dtype)
tl.store(out1_row + offsets1, out1, mask=mask1)
tl.store(out2_row + offsets2, out2, mask=mask2)
def rms_sumsq_serial(x1: torch.Tensor, x2: torch.Tensor) -> torch.Tensor:
assert x1.is_cuda and x2.is_cuda
B, D1 = x1.shape
B2, D2 = x2.shape
assert B == B2
stride_x1 = x1.stride(0)
stride_x2 = x2.stride(0)
sum_sq = torch.empty(B + B2, device=x1.device, dtype=torch.float32)
BLOCK_SIZE1 = triton.next_power_of_2(D1)
BLOCK_SIZE2 = triton.next_power_of_2(D2)
grid = (B,)
rmsnorm_sumsq_kernel_serial[grid](
x1,
x2,
stride_x1,
stride_x2,
sum_sq,
B,
D1,
D2,
BLOCK_SIZE1,
BLOCK_SIZE2,
)
return sum_sq
def rms_apply_serial(
x1: torch.Tensor,
x2: torch.Tensor,
w1: torch.Tensor,
w2: torch.Tensor,
sum_sq: torch.Tensor,
tp_world: int = 1,
eps: float = 1e-5,
) -> torch.Tensor:
assert x1.is_cuda and x2.is_cuda and w1.is_cuda and w2.is_cuda and sum_sq.is_cuda
B, D1 = x1.shape
B2, D2 = x2.shape
assert B == B2
stride_x1 = x1.stride(0)
stride_x2 = x2.stride(0)
out1 = torch.empty(B, D1, device=x1.device, dtype=x1.dtype)
out2 = torch.empty(B, D2, device=x2.device, dtype=x2.dtype)
BLOCK_SIZE1 = triton.next_power_of_2(D1)
BLOCK_SIZE2 = triton.next_power_of_2(D2)
grid = (B,)
rmsnorm_apply_kernel_serial[grid](
x1,
x2,
w1,
w2,
sum_sq,
out1,
out2,
B,
D1,
D2,
stride_x1,
stride_x2,
tp_world,
eps,
BLOCK_SIZE1,
BLOCK_SIZE2,
)
return out1, out2
class MiniMaxM2RMSNormTP(nn.Module): class MiniMaxM2RMSNormTP(nn.Module):
"""RMSNorm with Tensor Parallel support for QK normalization.""" """RMSNorm with Tensor Parallel support for QK normalization."""
@@ -124,6 +284,30 @@ class MiniMaxM2RMSNormTP(nn.Module):
return x return x
@staticmethod
@torch.compile(dynamic=True, backend=get_compiler_backend())
def forward_qk(
q_norm: "MiniMaxM2RMSNormTP",
k_norm: "MiniMaxM2RMSNormTP",
q: torch.Tensor,
k: torch.Tensor,
) -> torch.Tensor:
sum_sq = rms_sumsq_serial(q, k)
if q_norm.tp_world > 1:
sum_sq = tensor_model_parallel_all_reduce(sum_sq)
q, k = rms_apply_serial(
q,
k,
q_norm.weight,
k_norm.weight,
sum_sq,
q_norm.tp_world,
q_norm.variance_epsilon,
)
return q, k
class MiniMaxM2MoE(nn.Module): class MiniMaxM2MoE(nn.Module):
"""MiniMax MoE implementation using DeepEP for Expert Parallel support.""" """MiniMax MoE implementation using DeepEP for Expert Parallel support."""
@@ -437,8 +621,11 @@ class MiniMaxM2Attention(nn.Module):
qkv, _ = self.qkv_proj(hidden_states) qkv, _ = self.qkv_proj(hidden_states)
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1) q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
if self.use_qk_norm: if self.use_qk_norm:
q = self.q_norm(q.contiguous()) # q = self.q_norm(q.contiguous())
k = self.k_norm(k.contiguous()) # k = self.k_norm(k.contiguous())
q, k = MiniMaxM2RMSNormTP.forward_qk(
self.q_norm, self.k_norm, q.contiguous(), k.contiguous()
)
else: else:
q, k = q.contiguous(), k.contiguous() q, k = q.contiguous(), k.contiguous()
q, k = self.rotary_emb(positions, q, k) q, k = self.rotary_emb(positions, q, k)