chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Fused Triton kernels for MiniMax-M3 on AMD ROCm (gfx94x / gfx95x).
|
||||
|
||||
Model-scoped JIT kernels (mirrors ``jit_kernel/dsv4``), split by op type:
|
||||
* ``rmsnorm`` -- fused fp32 Gemma RMSNorm (plain + fused-add-residual)
|
||||
* ``swiglu`` -- fused fp32 SwiGLU-OAI (split layout)
|
||||
"""
|
||||
|
||||
from sglang.jit_kernel.minimax_m3.rmsnorm import (
|
||||
_num_warps,
|
||||
gemma_fused_add_rmsnorm,
|
||||
gemma_rmsnorm,
|
||||
)
|
||||
from sglang.jit_kernel.minimax_m3.swiglu import (
|
||||
swiglu_oai_mxfp8_quant,
|
||||
swiglu_oai_split,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"gemma_rmsnorm",
|
||||
"gemma_fused_add_rmsnorm",
|
||||
"swiglu_oai_split",
|
||||
"swiglu_oai_mxfp8_quant",
|
||||
"_num_warps",
|
||||
]
|
||||
@@ -0,0 +1,659 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Fused MiniMax-M3 per-head Gemma Q/K RMSNorm + partial RoPE for ROCm."""
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import torch
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _qk_gemma_rmsnorm_rope_kernel(
|
||||
q_ptr,
|
||||
k_ptr,
|
||||
q_out_ptr,
|
||||
k_out_ptr,
|
||||
q_weight_ptr,
|
||||
k_weight_ptr,
|
||||
positions_ptr,
|
||||
cos_sin_cache_ptr,
|
||||
q_stride_m,
|
||||
q_stride_d,
|
||||
k_stride_m,
|
||||
k_stride_d,
|
||||
q_heads: tl.constexpr,
|
||||
k_heads: tl.constexpr,
|
||||
head_dim: tl.constexpr,
|
||||
rotary_dim: tl.constexpr,
|
||||
eps: tl.constexpr,
|
||||
is_neox_style: tl.constexpr,
|
||||
BLOCK_HD: tl.constexpr,
|
||||
):
|
||||
token_id = tl.program_id(0)
|
||||
head_program = tl.program_id(1)
|
||||
cols = tl.arange(0, BLOCK_HD)
|
||||
mask = cols < head_dim
|
||||
half_rotary: tl.constexpr = rotary_dim // 2
|
||||
|
||||
is_q = head_program < q_heads
|
||||
head_id = tl.where(is_q, head_program, head_program - q_heads)
|
||||
in_ptr = tl.where(is_q, q_ptr, k_ptr)
|
||||
out_ptr = tl.where(is_q, q_out_ptr, k_out_ptr)
|
||||
weight_ptr = tl.where(is_q, q_weight_ptr, k_weight_ptr)
|
||||
stride_m = tl.where(is_q, q_stride_m, k_stride_m)
|
||||
stride_d = tl.where(is_q, q_stride_d, k_stride_d)
|
||||
n_heads = tl.where(is_q, q_heads, k_heads)
|
||||
|
||||
base_in = in_ptr + token_id * stride_m + head_id * head_dim * stride_d
|
||||
x = tl.load(base_in + cols * stride_d, mask=mask, other=0.0).to(tl.float32)
|
||||
w = tl.load(weight_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
||||
var = tl.sum(x * x, axis=0) / head_dim
|
||||
rstd = tl.rsqrt(var + eps)
|
||||
normed = x * rstd * (1.0 + w)
|
||||
# Match the unfused path: GemmaRMSNorm writes bf16/fp16, then RoPE reads
|
||||
# that rounded value in the following kernel.
|
||||
normed = normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
rotary_mask = cols < rotary_dim
|
||||
if is_neox_style:
|
||||
partner_cols = tl.where(
|
||||
cols < half_rotary, cols + half_rotary, cols - half_rotary
|
||||
)
|
||||
cos_cols = tl.where(cols < half_rotary, cols, cols - half_rotary)
|
||||
sign = tl.where(cols < half_rotary, -1.0, 1.0)
|
||||
else:
|
||||
partner_cols = tl.where((cols % 2) == 0, cols + 1, cols - 1)
|
||||
cos_cols = cols // 2
|
||||
sign = tl.where((cols % 2) == 0, -1.0, 1.0)
|
||||
|
||||
partner_mask = partner_cols < head_dim
|
||||
x_partner = tl.load(
|
||||
base_in + partner_cols * stride_d,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
w_partner = tl.load(
|
||||
weight_ptr + partner_cols,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
partner_normed = x_partner * rstd * (1.0 + w_partner)
|
||||
partner_normed = partner_normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
pos = tl.load(positions_ptr + token_id).to(tl.int64)
|
||||
cos_sin_base = cos_sin_cache_ptr + pos * rotary_dim
|
||||
cos = tl.load(cos_sin_base + cos_cols, mask=rotary_mask, other=1.0).to(tl.float32)
|
||||
sin = tl.load(
|
||||
cos_sin_base + half_rotary + cos_cols,
|
||||
mask=rotary_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
rotated = normed * cos + sign * partner_normed * sin
|
||||
out = tl.where(rotary_mask, rotated, normed)
|
||||
|
||||
base_out = out_ptr + token_id * n_heads * head_dim + head_id * head_dim
|
||||
tl.store(base_out + cols, out.to(out_ptr.dtype.element_ty), mask=mask)
|
||||
|
||||
|
||||
def qk_gemma_rmsnorm_rope(
|
||||
q: torch.Tensor,
|
||||
k: torch.Tensor,
|
||||
q_weight: torch.Tensor,
|
||||
k_weight: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
cos_sin_cache: torch.Tensor,
|
||||
eps: float,
|
||||
head_dim: int,
|
||||
rotary_dim: int,
|
||||
is_neox_style: bool,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor]:
|
||||
"""Return normalized+rotated Q/K tensors with the same shapes as ``q``/``k``."""
|
||||
assert q.dim() == 2 and k.dim() == 2
|
||||
assert positions.dim() == 1
|
||||
assert q.shape[0] == k.shape[0] == positions.shape[0]
|
||||
assert q.shape[1] % head_dim == 0
|
||||
assert k.shape[1] % head_dim == 0
|
||||
assert rotary_dim <= head_dim and rotary_dim % 2 == 0
|
||||
|
||||
q_heads = q.shape[1] // head_dim
|
||||
k_heads = k.shape[1] // head_dim
|
||||
q_out = torch.empty(q.shape, dtype=q.dtype, device=q.device)
|
||||
k_out = torch.empty(k.shape, dtype=k.dtype, device=k.device)
|
||||
block_hd = triton.next_power_of_2(head_dim)
|
||||
|
||||
_qk_gemma_rmsnorm_rope_kernel[(q.shape[0], q_heads + k_heads)](
|
||||
q,
|
||||
k,
|
||||
q_out,
|
||||
k_out,
|
||||
q_weight,
|
||||
k_weight,
|
||||
positions,
|
||||
cos_sin_cache,
|
||||
q.stride(0),
|
||||
q.stride(1),
|
||||
k.stride(0),
|
||||
k.stride(1),
|
||||
q_heads,
|
||||
k_heads,
|
||||
head_dim,
|
||||
rotary_dim,
|
||||
eps,
|
||||
is_neox_style,
|
||||
BLOCK_HD=block_hd,
|
||||
num_warps=4,
|
||||
)
|
||||
return q_out, k_out
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _sparse_qk_index_gemma_rmsnorm_rope_kernel(
|
||||
q_ptr,
|
||||
k_ptr,
|
||||
idx_q_ptr,
|
||||
idx_k_ptr,
|
||||
q_out_ptr,
|
||||
k_out_ptr,
|
||||
idx_q_out_ptr,
|
||||
idx_k_out_ptr,
|
||||
q_weight_ptr,
|
||||
k_weight_ptr,
|
||||
idx_q_weight_ptr,
|
||||
idx_k_weight_ptr,
|
||||
positions_ptr,
|
||||
cos_sin_cache_ptr,
|
||||
q_stride_m,
|
||||
q_stride_d,
|
||||
k_stride_m,
|
||||
k_stride_d,
|
||||
idx_q_stride_m,
|
||||
idx_q_stride_d,
|
||||
idx_k_stride_m,
|
||||
idx_k_stride_d,
|
||||
q_heads: tl.constexpr,
|
||||
k_heads: tl.constexpr,
|
||||
idx_q_heads: tl.constexpr,
|
||||
head_dim: tl.constexpr,
|
||||
rotary_dim: tl.constexpr,
|
||||
eps: tl.constexpr,
|
||||
is_neox_style: tl.constexpr,
|
||||
BLOCK_HD: tl.constexpr,
|
||||
):
|
||||
token_id = tl.program_id(0)
|
||||
head_program = tl.program_id(1)
|
||||
cols = tl.arange(0, BLOCK_HD)
|
||||
mask = cols < head_dim
|
||||
half_rotary: tl.constexpr = rotary_dim // 2
|
||||
|
||||
main_heads: tl.constexpr = q_heads + k_heads
|
||||
idx_k_program: tl.constexpr = q_heads + k_heads + idx_q_heads
|
||||
|
||||
is_q = head_program < q_heads
|
||||
is_k = (head_program >= q_heads) & (head_program < main_heads)
|
||||
is_idx_q = (head_program >= main_heads) & (head_program < idx_k_program)
|
||||
|
||||
head_id = tl.where(
|
||||
is_q,
|
||||
head_program,
|
||||
tl.where(
|
||||
is_k,
|
||||
head_program - q_heads,
|
||||
tl.where(is_idx_q, head_program - main_heads, 0),
|
||||
),
|
||||
)
|
||||
|
||||
in_ptr = tl.where(
|
||||
is_q,
|
||||
q_ptr,
|
||||
tl.where(is_k, k_ptr, tl.where(is_idx_q, idx_q_ptr, idx_k_ptr)),
|
||||
)
|
||||
out_ptr = tl.where(
|
||||
is_q,
|
||||
q_out_ptr,
|
||||
tl.where(is_k, k_out_ptr, tl.where(is_idx_q, idx_q_out_ptr, idx_k_out_ptr)),
|
||||
)
|
||||
weight_ptr = tl.where(
|
||||
is_q,
|
||||
q_weight_ptr,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_weight_ptr,
|
||||
tl.where(is_idx_q, idx_q_weight_ptr, idx_k_weight_ptr),
|
||||
),
|
||||
)
|
||||
stride_m = tl.where(
|
||||
is_q,
|
||||
q_stride_m,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_stride_m,
|
||||
tl.where(is_idx_q, idx_q_stride_m, idx_k_stride_m),
|
||||
),
|
||||
)
|
||||
stride_d = tl.where(
|
||||
is_q,
|
||||
q_stride_d,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_stride_d,
|
||||
tl.where(is_idx_q, idx_q_stride_d, idx_k_stride_d),
|
||||
),
|
||||
)
|
||||
out_heads = tl.where(
|
||||
is_q,
|
||||
q_heads,
|
||||
tl.where(is_k, k_heads, tl.where(is_idx_q, idx_q_heads, 1)),
|
||||
)
|
||||
|
||||
base_in = in_ptr + token_id * stride_m + head_id * head_dim * stride_d
|
||||
x = tl.load(base_in + cols * stride_d, mask=mask, other=0.0).to(tl.float32)
|
||||
w = tl.load(weight_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
||||
var = tl.sum(x * x, axis=0) / head_dim
|
||||
rstd = tl.rsqrt(var + eps)
|
||||
normed = x * rstd * (1.0 + w)
|
||||
normed = normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
rotary_mask = cols < rotary_dim
|
||||
if is_neox_style:
|
||||
partner_cols = tl.where(
|
||||
cols < half_rotary, cols + half_rotary, cols - half_rotary
|
||||
)
|
||||
cos_cols = tl.where(cols < half_rotary, cols, cols - half_rotary)
|
||||
sign = tl.where(cols < half_rotary, -1.0, 1.0)
|
||||
else:
|
||||
partner_cols = tl.where((cols % 2) == 0, cols + 1, cols - 1)
|
||||
cos_cols = cols // 2
|
||||
sign = tl.where((cols % 2) == 0, -1.0, 1.0)
|
||||
|
||||
partner_mask = partner_cols < head_dim
|
||||
x_partner = tl.load(
|
||||
base_in + partner_cols * stride_d,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
w_partner = tl.load(
|
||||
weight_ptr + partner_cols,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
partner_normed = x_partner * rstd * (1.0 + w_partner)
|
||||
partner_normed = partner_normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
pos = tl.load(positions_ptr + token_id).to(tl.int64)
|
||||
cos_sin_base = cos_sin_cache_ptr + pos * rotary_dim
|
||||
cos = tl.load(cos_sin_base + cos_cols, mask=rotary_mask, other=1.0).to(tl.float32)
|
||||
sin = tl.load(
|
||||
cos_sin_base + half_rotary + cos_cols,
|
||||
mask=rotary_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
rotated = normed * cos + sign * partner_normed * sin
|
||||
out = tl.where(rotary_mask, rotated, normed)
|
||||
|
||||
base_out = out_ptr + token_id * out_heads * head_dim + head_id * head_dim
|
||||
tl.store(base_out + cols, out.to(q_out_ptr.dtype.element_ty), mask=mask)
|
||||
|
||||
|
||||
def sparse_qk_index_gemma_rmsnorm_rope(
|
||||
q: torch.Tensor,
|
||||
k: torch.Tensor,
|
||||
idx_q: torch.Tensor,
|
||||
idx_k: torch.Tensor,
|
||||
q_weight: torch.Tensor,
|
||||
k_weight: torch.Tensor,
|
||||
idx_q_weight: torch.Tensor,
|
||||
idx_k_weight: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
cos_sin_cache: torch.Tensor,
|
||||
eps: float,
|
||||
head_dim: int,
|
||||
rotary_dim: int,
|
||||
is_neox_style: bool,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
"""Fuse main and sparse-index Gemma Q/K RMSNorm + RoPE into one launch."""
|
||||
assert q.dim() == k.dim() == idx_q.dim() == idx_k.dim() == 2
|
||||
assert positions.dim() == 1
|
||||
assert q.shape[0] == k.shape[0] == idx_q.shape[0] == idx_k.shape[0]
|
||||
assert q.shape[0] == positions.shape[0]
|
||||
assert q.shape[1] % head_dim == 0
|
||||
assert k.shape[1] % head_dim == 0
|
||||
assert idx_q.shape[1] % head_dim == 0
|
||||
assert idx_k.shape[1] == head_dim
|
||||
assert rotary_dim <= head_dim and rotary_dim % 2 == 0
|
||||
|
||||
q_heads = q.shape[1] // head_dim
|
||||
k_heads = k.shape[1] // head_dim
|
||||
idx_q_heads = idx_q.shape[1] // head_dim
|
||||
q_out = torch.empty(q.shape, dtype=q.dtype, device=q.device)
|
||||
k_out = torch.empty(k.shape, dtype=k.dtype, device=k.device)
|
||||
idx_q_out = torch.empty(idx_q.shape, dtype=idx_q.dtype, device=idx_q.device)
|
||||
idx_k_out = torch.empty(idx_k.shape, dtype=idx_k.dtype, device=idx_k.device)
|
||||
block_hd = triton.next_power_of_2(head_dim)
|
||||
|
||||
_sparse_qk_index_gemma_rmsnorm_rope_kernel[
|
||||
(q.shape[0], q_heads + k_heads + idx_q_heads + 1)
|
||||
](
|
||||
q,
|
||||
k,
|
||||
idx_q,
|
||||
idx_k,
|
||||
q_out,
|
||||
k_out,
|
||||
idx_q_out,
|
||||
idx_k_out,
|
||||
q_weight,
|
||||
k_weight,
|
||||
idx_q_weight,
|
||||
idx_k_weight,
|
||||
positions,
|
||||
cos_sin_cache,
|
||||
q.stride(0),
|
||||
q.stride(1),
|
||||
k.stride(0),
|
||||
k.stride(1),
|
||||
idx_q.stride(0),
|
||||
idx_q.stride(1),
|
||||
idx_k.stride(0),
|
||||
idx_k.stride(1),
|
||||
q_heads,
|
||||
k_heads,
|
||||
idx_q_heads,
|
||||
head_dim,
|
||||
rotary_dim,
|
||||
eps,
|
||||
is_neox_style,
|
||||
BLOCK_HD=block_hd,
|
||||
num_warps=4,
|
||||
)
|
||||
return q_out, k_out, idx_q_out, idx_k_out
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _sparse_qk_index_gemma_rmsnorm_rope_cache_kernel(
|
||||
q_ptr,
|
||||
k_ptr,
|
||||
v_ptr,
|
||||
idx_q_ptr,
|
||||
idx_k_ptr,
|
||||
q_out_ptr,
|
||||
k_out_ptr,
|
||||
idx_q_out_ptr,
|
||||
idx_k_out_ptr,
|
||||
k_cache_ptr,
|
||||
v_cache_ptr,
|
||||
idx_k_cache_ptr,
|
||||
loc_ptr,
|
||||
q_weight_ptr,
|
||||
k_weight_ptr,
|
||||
idx_q_weight_ptr,
|
||||
idx_k_weight_ptr,
|
||||
positions_ptr,
|
||||
cos_sin_cache_ptr,
|
||||
q_stride_m,
|
||||
q_stride_d,
|
||||
k_stride_m,
|
||||
k_stride_d,
|
||||
v_stride_m,
|
||||
v_stride_d,
|
||||
idx_q_stride_m,
|
||||
idx_q_stride_d,
|
||||
idx_k_stride_m,
|
||||
idx_k_stride_d,
|
||||
k_cache_stride_s,
|
||||
k_cache_stride_h,
|
||||
k_cache_stride_d,
|
||||
v_cache_stride_s,
|
||||
v_cache_stride_h,
|
||||
v_cache_stride_d,
|
||||
idx_k_cache_stride_s,
|
||||
idx_k_cache_stride_h,
|
||||
idx_k_cache_stride_d,
|
||||
q_heads: tl.constexpr,
|
||||
k_heads: tl.constexpr,
|
||||
idx_q_heads: tl.constexpr,
|
||||
head_dim: tl.constexpr,
|
||||
rotary_dim: tl.constexpr,
|
||||
eps: tl.constexpr,
|
||||
is_neox_style: tl.constexpr,
|
||||
BLOCK_HD: tl.constexpr,
|
||||
):
|
||||
token_id = tl.program_id(0)
|
||||
head_program = tl.program_id(1)
|
||||
cols = tl.arange(0, BLOCK_HD)
|
||||
mask = cols < head_dim
|
||||
half_rotary: tl.constexpr = rotary_dim // 2
|
||||
|
||||
main_heads: tl.constexpr = q_heads + k_heads
|
||||
idx_k_program: tl.constexpr = q_heads + k_heads + idx_q_heads
|
||||
|
||||
is_q = head_program < q_heads
|
||||
is_k = (head_program >= q_heads) & (head_program < main_heads)
|
||||
is_idx_q = (head_program >= main_heads) & (head_program < idx_k_program)
|
||||
|
||||
head_id = tl.where(
|
||||
is_q,
|
||||
head_program,
|
||||
tl.where(
|
||||
is_k,
|
||||
head_program - q_heads,
|
||||
tl.where(is_idx_q, head_program - main_heads, 0),
|
||||
),
|
||||
)
|
||||
|
||||
in_ptr = tl.where(
|
||||
is_q,
|
||||
q_ptr,
|
||||
tl.where(is_k, k_ptr, tl.where(is_idx_q, idx_q_ptr, idx_k_ptr)),
|
||||
)
|
||||
out_ptr = tl.where(
|
||||
is_q,
|
||||
q_out_ptr,
|
||||
tl.where(is_k, k_out_ptr, tl.where(is_idx_q, idx_q_out_ptr, idx_k_out_ptr)),
|
||||
)
|
||||
weight_ptr = tl.where(
|
||||
is_q,
|
||||
q_weight_ptr,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_weight_ptr,
|
||||
tl.where(is_idx_q, idx_q_weight_ptr, idx_k_weight_ptr),
|
||||
),
|
||||
)
|
||||
stride_m = tl.where(
|
||||
is_q,
|
||||
q_stride_m,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_stride_m,
|
||||
tl.where(is_idx_q, idx_q_stride_m, idx_k_stride_m),
|
||||
),
|
||||
)
|
||||
stride_d = tl.where(
|
||||
is_q,
|
||||
q_stride_d,
|
||||
tl.where(
|
||||
is_k,
|
||||
k_stride_d,
|
||||
tl.where(is_idx_q, idx_q_stride_d, idx_k_stride_d),
|
||||
),
|
||||
)
|
||||
out_heads = tl.where(
|
||||
is_q,
|
||||
q_heads,
|
||||
tl.where(is_k, k_heads, tl.where(is_idx_q, idx_q_heads, 1)),
|
||||
)
|
||||
|
||||
base_in = in_ptr + token_id * stride_m + head_id * head_dim * stride_d
|
||||
x = tl.load(base_in + cols * stride_d, mask=mask, other=0.0).to(tl.float32)
|
||||
w = tl.load(weight_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
||||
var = tl.sum(x * x, axis=0) / head_dim
|
||||
rstd = tl.rsqrt(var + eps)
|
||||
normed = x * rstd * (1.0 + w)
|
||||
normed = normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
rotary_mask = cols < rotary_dim
|
||||
if is_neox_style:
|
||||
partner_cols = tl.where(
|
||||
cols < half_rotary, cols + half_rotary, cols - half_rotary
|
||||
)
|
||||
cos_cols = tl.where(cols < half_rotary, cols, cols - half_rotary)
|
||||
sign = tl.where(cols < half_rotary, -1.0, 1.0)
|
||||
else:
|
||||
partner_cols = tl.where((cols % 2) == 0, cols + 1, cols - 1)
|
||||
cos_cols = cols // 2
|
||||
sign = tl.where((cols % 2) == 0, -1.0, 1.0)
|
||||
|
||||
partner_mask = partner_cols < head_dim
|
||||
x_partner = tl.load(
|
||||
base_in + partner_cols * stride_d,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
w_partner = tl.load(
|
||||
weight_ptr + partner_cols,
|
||||
mask=partner_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
partner_normed = x_partner * rstd * (1.0 + w_partner)
|
||||
partner_normed = partner_normed.to(q_out_ptr.dtype.element_ty).to(tl.float32)
|
||||
|
||||
pos = tl.load(positions_ptr + token_id).to(tl.int64)
|
||||
cos_sin_base = cos_sin_cache_ptr + pos * rotary_dim
|
||||
cos = tl.load(cos_sin_base + cos_cols, mask=rotary_mask, other=1.0).to(tl.float32)
|
||||
sin = tl.load(
|
||||
cos_sin_base + half_rotary + cos_cols,
|
||||
mask=rotary_mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
rotated = normed * cos + sign * partner_normed * sin
|
||||
out = tl.where(rotary_mask, rotated, normed)
|
||||
out_typed = out.to(q_out_ptr.dtype.element_ty)
|
||||
|
||||
base_out = out_ptr + token_id * out_heads * head_dim + head_id * head_dim
|
||||
tl.store(base_out + cols, out_typed, mask=mask)
|
||||
|
||||
loc = tl.load(loc_ptr + token_id)
|
||||
cache_k_base = (
|
||||
k_cache_ptr
|
||||
+ loc * k_cache_stride_s
|
||||
+ head_id * k_cache_stride_h
|
||||
+ cols * k_cache_stride_d
|
||||
)
|
||||
tl.store(cache_k_base, out_typed, mask=mask & is_k)
|
||||
|
||||
v_base = v_ptr + token_id * v_stride_m + head_id * head_dim * v_stride_d
|
||||
v_val = tl.load(v_base + cols * v_stride_d, mask=mask & is_k, other=0.0)
|
||||
cache_v_base = (
|
||||
v_cache_ptr
|
||||
+ loc * v_cache_stride_s
|
||||
+ head_id * v_cache_stride_h
|
||||
+ cols * v_cache_stride_d
|
||||
)
|
||||
tl.store(cache_v_base, v_val, mask=mask & is_k)
|
||||
|
||||
is_idx_k = head_program == idx_k_program
|
||||
idx_cache_base = (
|
||||
idx_k_cache_ptr + loc * idx_k_cache_stride_s + cols * idx_k_cache_stride_d
|
||||
)
|
||||
tl.store(idx_cache_base, out_typed, mask=mask & is_idx_k)
|
||||
|
||||
|
||||
def sparse_qk_index_gemma_rmsnorm_rope_cache(
|
||||
q: torch.Tensor,
|
||||
k: torch.Tensor,
|
||||
v: torch.Tensor,
|
||||
idx_q: torch.Tensor,
|
||||
idx_k: torch.Tensor,
|
||||
k_cache: torch.Tensor,
|
||||
v_cache: torch.Tensor,
|
||||
idx_k_cache: torch.Tensor,
|
||||
out_cache_loc: torch.Tensor,
|
||||
q_weight: torch.Tensor,
|
||||
k_weight: torch.Tensor,
|
||||
idx_q_weight: torch.Tensor,
|
||||
idx_k_weight: torch.Tensor,
|
||||
positions: torch.Tensor,
|
||||
cos_sin_cache: torch.Tensor,
|
||||
eps: float,
|
||||
head_dim: int,
|
||||
rotary_dim: int,
|
||||
is_neox_style: bool,
|
||||
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||
"""Fuse sparse Q/K/index norm+RoPE with main KV and index-K cache stores."""
|
||||
assert q.dim() == k.dim() == v.dim() == idx_q.dim() == idx_k.dim() == 2
|
||||
assert k_cache.dim() == v_cache.dim() == idx_k_cache.dim() == 3
|
||||
assert out_cache_loc.dim() == positions.dim() == 1
|
||||
assert q.shape[0] == k.shape[0] == v.shape[0] == idx_q.shape[0] == idx_k.shape[0]
|
||||
assert q.shape[0] == positions.shape[0] == out_cache_loc.shape[0]
|
||||
assert q.shape[1] % head_dim == 0
|
||||
assert k.shape[1] % head_dim == 0
|
||||
assert v.shape[1] == k.shape[1]
|
||||
assert idx_q.shape[1] % head_dim == 0
|
||||
assert idx_k.shape[1] == head_dim
|
||||
assert rotary_dim <= head_dim and rotary_dim % 2 == 0
|
||||
|
||||
q_heads = q.shape[1] // head_dim
|
||||
k_heads = k.shape[1] // head_dim
|
||||
idx_q_heads = idx_q.shape[1] // head_dim
|
||||
assert k_cache.shape[1] == v_cache.shape[1] == k_heads
|
||||
assert idx_k_cache.shape[1] == 1
|
||||
|
||||
q_out = torch.empty(q.shape, dtype=q.dtype, device=q.device)
|
||||
k_out = torch.empty(k.shape, dtype=k.dtype, device=k.device)
|
||||
idx_q_out = torch.empty(idx_q.shape, dtype=idx_q.dtype, device=idx_q.device)
|
||||
idx_k_out = torch.empty(idx_k.shape, dtype=idx_k.dtype, device=idx_k.device)
|
||||
block_hd = triton.next_power_of_2(head_dim)
|
||||
|
||||
_sparse_qk_index_gemma_rmsnorm_rope_cache_kernel[
|
||||
(q.shape[0], q_heads + k_heads + idx_q_heads + 1)
|
||||
](
|
||||
q,
|
||||
k,
|
||||
v,
|
||||
idx_q,
|
||||
idx_k,
|
||||
q_out,
|
||||
k_out,
|
||||
idx_q_out,
|
||||
idx_k_out,
|
||||
k_cache,
|
||||
v_cache,
|
||||
idx_k_cache,
|
||||
out_cache_loc,
|
||||
q_weight,
|
||||
k_weight,
|
||||
idx_q_weight,
|
||||
idx_k_weight,
|
||||
positions,
|
||||
cos_sin_cache,
|
||||
q.stride(0),
|
||||
q.stride(1),
|
||||
k.stride(0),
|
||||
k.stride(1),
|
||||
v.stride(0),
|
||||
v.stride(1),
|
||||
idx_q.stride(0),
|
||||
idx_q.stride(1),
|
||||
idx_k.stride(0),
|
||||
idx_k.stride(1),
|
||||
k_cache.stride(0),
|
||||
k_cache.stride(1),
|
||||
k_cache.stride(2),
|
||||
v_cache.stride(0),
|
||||
v_cache.stride(1),
|
||||
v_cache.stride(2),
|
||||
idx_k_cache.stride(0),
|
||||
idx_k_cache.stride(1),
|
||||
idx_k_cache.stride(2),
|
||||
q_heads,
|
||||
k_heads,
|
||||
idx_q_heads,
|
||||
head_dim,
|
||||
rotary_dim,
|
||||
eps,
|
||||
is_neox_style,
|
||||
BLOCK_HD=block_hd,
|
||||
num_warps=4,
|
||||
)
|
||||
return q_out, k_out, idx_q_out, idx_k_out
|
||||
@@ -0,0 +1,148 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Fused Gemma RMSNorm Triton kernels for MiniMax-M3 on AMD ROCm.
|
||||
|
||||
Gemma RMSNorm = ``normalize(x) * (1 + weight)``, computed in a single fp32 pass.
|
||||
On ROCm with AITER, ``GemmaRMSNorm.forward_hip`` otherwise falls back to a
|
||||
~8-op PyTorch sequence: ``sgl_kernel``'s Gemma kernels are CUDA-only, and
|
||||
AITER's ``rmsnorm2d_fwd`` requires weight.dtype == activation.dtype (fp32
|
||||
weight + bf16 activation silently corrupts on gfx950). These kernels read
|
||||
strided inputs, so they serve both the full-hidden norms and the per-head
|
||||
q/k/index norms (non-contiguous ``qkv.split`` views).
|
||||
"""
|
||||
|
||||
import torch
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _gemma_rmsnorm_kernel(
|
||||
x_ptr,
|
||||
w_ptr,
|
||||
out_ptr,
|
||||
n_cols,
|
||||
stride_row,
|
||||
stride_col,
|
||||
eps,
|
||||
BLOCK_N: tl.constexpr,
|
||||
):
|
||||
row = tl.program_id(0)
|
||||
cols = tl.arange(0, BLOCK_N)
|
||||
mask = cols < n_cols
|
||||
x = tl.load(x_ptr + row * stride_row + cols * stride_col, mask=mask, other=0.0).to(
|
||||
tl.float32
|
||||
)
|
||||
var = tl.sum(x * x, axis=0) / n_cols
|
||||
rstd = 1.0 / tl.sqrt(var + eps)
|
||||
w = tl.load(w_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
||||
out = x * rstd * (1.0 + w)
|
||||
tl.store(
|
||||
out_ptr + row * n_cols + cols,
|
||||
out.to(out_ptr.dtype.element_ty),
|
||||
mask=mask,
|
||||
)
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _gemma_fused_add_rmsnorm_kernel(
|
||||
x_ptr,
|
||||
res_ptr,
|
||||
w_ptr,
|
||||
out_ptr,
|
||||
res_out_ptr,
|
||||
n_cols,
|
||||
stride_xrow,
|
||||
stride_xcol,
|
||||
stride_rrow,
|
||||
stride_rcol,
|
||||
eps,
|
||||
BLOCK_N: tl.constexpr,
|
||||
):
|
||||
row = tl.program_id(0)
|
||||
cols = tl.arange(0, BLOCK_N)
|
||||
mask = cols < n_cols
|
||||
x = tl.load(
|
||||
x_ptr + row * stride_xrow + cols * stride_xcol, mask=mask, other=0.0
|
||||
).to(tl.float32)
|
||||
r = tl.load(
|
||||
res_ptr + row * stride_rrow + cols * stride_rcol, mask=mask, other=0.0
|
||||
).to(tl.float32)
|
||||
s = x + r
|
||||
# residual_out is the pre-norm sum (consumed by the next layer's add).
|
||||
tl.store(
|
||||
res_out_ptr + row * n_cols + cols,
|
||||
s.to(res_out_ptr.dtype.element_ty),
|
||||
mask=mask,
|
||||
)
|
||||
var = tl.sum(s * s, axis=0) / n_cols
|
||||
rstd = 1.0 / tl.sqrt(var + eps)
|
||||
w = tl.load(w_ptr + cols, mask=mask, other=0.0).to(tl.float32)
|
||||
out = s * rstd * (1.0 + w)
|
||||
tl.store(
|
||||
out_ptr + row * n_cols + cols,
|
||||
out.to(out_ptr.dtype.element_ty),
|
||||
mask=mask,
|
||||
)
|
||||
|
||||
|
||||
def _num_warps(block_n: int) -> int:
|
||||
if block_n >= 4096:
|
||||
return 16
|
||||
if block_n >= 1024:
|
||||
return 8
|
||||
return 4
|
||||
|
||||
|
||||
def gemma_rmsnorm(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor:
|
||||
"""Gemma RMSNorm = normalize(x) * (1 + weight), fp32 math, single pass."""
|
||||
orig_shape = x.shape
|
||||
n = orig_shape[-1]
|
||||
x2 = x.reshape(-1, n)
|
||||
m = x2.shape[0]
|
||||
out = torch.empty((m, n), dtype=x.dtype, device=x.device)
|
||||
block_n = triton.next_power_of_2(n)
|
||||
_gemma_rmsnorm_kernel[(m,)](
|
||||
x2,
|
||||
weight,
|
||||
out,
|
||||
n,
|
||||
x2.stride(0),
|
||||
x2.stride(1),
|
||||
eps,
|
||||
BLOCK_N=block_n,
|
||||
num_warps=_num_warps(block_n),
|
||||
)
|
||||
return out.reshape(orig_shape)
|
||||
|
||||
|
||||
def gemma_fused_add_rmsnorm(
|
||||
x: torch.Tensor,
|
||||
residual: torch.Tensor,
|
||||
weight: torch.Tensor,
|
||||
eps: float,
|
||||
):
|
||||
"""Fused (x + residual) then Gemma RMSNorm; returns (normed, pre-norm sum)."""
|
||||
orig_shape = x.shape
|
||||
n = orig_shape[-1]
|
||||
x2 = x.reshape(-1, n)
|
||||
r2 = residual.reshape(-1, n)
|
||||
m = x2.shape[0]
|
||||
out = torch.empty((m, n), dtype=x.dtype, device=x.device)
|
||||
res_out = torch.empty((m, n), dtype=x.dtype, device=x.device)
|
||||
block_n = triton.next_power_of_2(n)
|
||||
_gemma_fused_add_rmsnorm_kernel[(m,)](
|
||||
x2,
|
||||
r2,
|
||||
weight,
|
||||
out,
|
||||
res_out,
|
||||
n,
|
||||
x2.stride(0),
|
||||
x2.stride(1),
|
||||
r2.stride(0),
|
||||
r2.stride(1),
|
||||
eps,
|
||||
BLOCK_N=block_n,
|
||||
num_warps=_num_warps(block_n),
|
||||
)
|
||||
return out.reshape(orig_shape), res_out.reshape(orig_shape)
|
||||
@@ -0,0 +1,201 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
"""Fused SwiGLU-OAI (split layout) Triton kernel for MiniMax-M3 on AMD ROCm.
|
||||
|
||||
SwiGLU-OAI on a ``[*, 2I]`` split-layout tensor (gate = first half, up = second
|
||||
half): ``gate * sigmoid(alpha * gate) * (up + beta)`` with optional clamp,
|
||||
computed in fp32. Used by the dense MLP / shared experts ``swigluoai``
|
||||
activation on ROCm in place of the ``@torch.compile`` bf16 elementwise variant.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
import triton
|
||||
import triton.language as tl
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _swiglu_oai_kernel(
|
||||
g_ptr,
|
||||
out_ptr,
|
||||
n_inter,
|
||||
stride_gm,
|
||||
stride_gn,
|
||||
stride_om,
|
||||
stride_on,
|
||||
alpha,
|
||||
beta,
|
||||
limit,
|
||||
HAS_LIMIT: tl.constexpr,
|
||||
BLOCK_I: tl.constexpr,
|
||||
):
|
||||
row = tl.program_id(0)
|
||||
pid_i = tl.program_id(1)
|
||||
cols = pid_i * BLOCK_I + tl.arange(0, BLOCK_I)
|
||||
mask = cols < n_inter
|
||||
gate = tl.load(g_ptr + row * stride_gm + cols * stride_gn, mask=mask, other=0.0).to(
|
||||
tl.float32
|
||||
)
|
||||
up = tl.load(
|
||||
g_ptr + row * stride_gm + (n_inter + cols) * stride_gn,
|
||||
mask=mask,
|
||||
other=0.0,
|
||||
).to(tl.float32)
|
||||
if HAS_LIMIT:
|
||||
gate = tl.minimum(gate, limit)
|
||||
up = tl.minimum(tl.maximum(up, -limit), limit)
|
||||
out = gate * tl.sigmoid(alpha * gate) * (up + beta)
|
||||
tl.store(
|
||||
out_ptr + row * stride_om + cols * stride_on,
|
||||
out.to(out_ptr.dtype.element_ty),
|
||||
mask=mask,
|
||||
)
|
||||
|
||||
|
||||
def swiglu_oai_split(
|
||||
gate_up: torch.Tensor,
|
||||
alpha: float,
|
||||
beta: float,
|
||||
limit: Optional[float],
|
||||
out_dtype: Optional[torch.dtype] = None,
|
||||
) -> torch.Tensor:
|
||||
"""SwiGLU-OAI on a split-layout ``[*, 2I]`` tensor -> ``[*, I]`` (fp32 math)."""
|
||||
orig_shape = gate_up.shape
|
||||
two_i = orig_shape[-1]
|
||||
n_inter = two_i // 2
|
||||
x2 = gate_up.reshape(-1, two_i)
|
||||
m = x2.shape[0]
|
||||
dt = out_dtype if out_dtype is not None else gate_up.dtype
|
||||
out = torch.empty((m, n_inter), dtype=dt, device=gate_up.device)
|
||||
# Adaptive tile (tuned on gfx950). A 512-wide tile only helps
|
||||
# once the (TP-sharded) per-rank slice is large enough to be bandwidth-bound
|
||||
# (~1.25-1.35x faster than 256 at TP=1 prefill for the dense MLP I=12288).
|
||||
# For small sharded slices (high TP) / decode the kernel is launch-bound, so
|
||||
# fall back to 256. num_warps is pinned to 4 (8 underfills this tile).
|
||||
block_i = 512 if n_inter >= 2048 else 256
|
||||
grid = (m, triton.cdiv(n_inter, block_i))
|
||||
_swiglu_oai_kernel[grid](
|
||||
x2,
|
||||
out,
|
||||
n_inter,
|
||||
x2.stride(0),
|
||||
x2.stride(1),
|
||||
out.stride(0),
|
||||
out.stride(1),
|
||||
float(alpha),
|
||||
float(beta),
|
||||
0.0 if limit is None else float(limit),
|
||||
HAS_LIMIT=limit is not None,
|
||||
BLOCK_I=block_i,
|
||||
num_warps=4,
|
||||
)
|
||||
return out.reshape(*orig_shape[:-1], n_inter)
|
||||
|
||||
|
||||
@triton.jit
|
||||
def _swiglu_oai_mxfp8_quant_kernel(
|
||||
g_ptr,
|
||||
q_ptr,
|
||||
scale_ptr,
|
||||
n_inter,
|
||||
stride_gm,
|
||||
stride_gn,
|
||||
stride_qm,
|
||||
stride_qn,
|
||||
stride_sm,
|
||||
stride_sn,
|
||||
alpha,
|
||||
beta,
|
||||
limit,
|
||||
HAS_LIMIT: tl.constexpr,
|
||||
BLOCK_I: tl.constexpr,
|
||||
):
|
||||
row = tl.program_id(0)
|
||||
pid_i = tl.program_id(1)
|
||||
cols = pid_i * BLOCK_I + tl.arange(0, BLOCK_I)
|
||||
mask = cols < n_inter
|
||||
|
||||
gate = tl.load(g_ptr + row * stride_gm + cols * stride_gn, mask=mask, other=0.0)
|
||||
up = tl.load(
|
||||
g_ptr + row * stride_gm + (n_inter + cols) * stride_gn,
|
||||
mask=mask,
|
||||
other=0.0,
|
||||
)
|
||||
gate = gate.to(tl.float32)
|
||||
up = up.to(tl.float32)
|
||||
if HAS_LIMIT:
|
||||
gate = tl.minimum(gate, limit)
|
||||
up = tl.minimum(tl.maximum(up, -limit), limit)
|
||||
|
||||
# Keep the activation in fp32 all the way to the E8M0 scale selection (no
|
||||
# bf16 round-trip to HBM). Matches the vLLM/ame fused swiglu+quant kernel:
|
||||
# marginally more accurate than the unfused bf16 two-kernel chain.
|
||||
activated = gate * tl.sigmoid(alpha * gate) * (up + beta)
|
||||
|
||||
groups: tl.constexpr = BLOCK_I // 32
|
||||
activated_2d = tl.reshape(activated, (groups, 32))
|
||||
valid_groups = pid_i * groups + tl.arange(0, groups) < (n_inter // 32)
|
||||
|
||||
amax = tl.maximum(tl.max(tl.abs(activated_2d), axis=1), 1e-30)
|
||||
# Round the E8M0 exponent up (ceil(log2(amax / e4m3_max))) so the block amax
|
||||
# stays inside the e4m3 range and the full dynamic range is used.
|
||||
scale_biased = tl.ceil(tl.log2(amax / 448.0)) + 127.0
|
||||
scale_biased = tl.minimum(tl.maximum(scale_biased, 0.0), 254.0)
|
||||
descale = tl.reshape(tl.exp2(scale_biased - 127.0), (groups, 1))
|
||||
|
||||
q_2d = tl.clamp(activated_2d / descale, -448.0, 448.0)
|
||||
q = tl.reshape(q_2d, (BLOCK_I,)).to(q_ptr.dtype.element_ty)
|
||||
|
||||
tl.store(q_ptr + row * stride_qm + cols * stride_qn, q, mask=mask)
|
||||
tl.store(
|
||||
scale_ptr
|
||||
+ row * stride_sm
|
||||
+ (pid_i * groups + tl.arange(0, groups)) * stride_sn,
|
||||
scale_biased.to(tl.uint8),
|
||||
mask=valid_groups,
|
||||
)
|
||||
|
||||
|
||||
def swiglu_oai_mxfp8_quant(
|
||||
gate_up: torch.Tensor,
|
||||
alpha: float,
|
||||
beta: float,
|
||||
limit: Optional[float],
|
||||
) -> tuple[torch.Tensor, torch.Tensor]:
|
||||
"""SwiGLU-OAI on split layout, then MiniMax MXFP8 quant, in one launch.
|
||||
|
||||
The activation stays in fp32 through the E8M0 scale selection (no bf16
|
||||
round-trip), matching the vLLM/ame fused swiglu+quant kernel.
|
||||
"""
|
||||
orig_shape = gate_up.shape
|
||||
two_i = orig_shape[-1]
|
||||
n_inter = two_i // 2
|
||||
assert n_inter % 32 == 0, "MiniMax MXFP8 quant requires I divisible by 32."
|
||||
|
||||
x2 = gate_up.reshape(-1, two_i)
|
||||
m = x2.shape[0]
|
||||
q = torch.empty((m, n_inter), dtype=torch.float8_e4m3fn, device=gate_up.device)
|
||||
scales = torch.empty((m, n_inter // 32), dtype=torch.uint8, device=gate_up.device)
|
||||
block_i = 512 if n_inter >= 2048 else 256
|
||||
grid = (m, triton.cdiv(n_inter, block_i))
|
||||
_swiglu_oai_mxfp8_quant_kernel[grid](
|
||||
x2,
|
||||
q,
|
||||
scales,
|
||||
n_inter,
|
||||
x2.stride(0),
|
||||
x2.stride(1),
|
||||
q.stride(0),
|
||||
q.stride(1),
|
||||
scales.stride(0),
|
||||
scales.stride(1),
|
||||
float(alpha),
|
||||
float(beta),
|
||||
0.0 if limit is None else float(limit),
|
||||
HAS_LIMIT=limit is not None,
|
||||
BLOCK_I=block_i,
|
||||
num_warps=4,
|
||||
)
|
||||
return q.reshape(*orig_shape[:-1], n_inter), scales.reshape(
|
||||
*orig_shape[:-1], n_inter // 32
|
||||
)
|
||||
Reference in New Issue
Block a user