Instructions to use kernels-community/aiter-kernels with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Kernels
How to use kernels-community/aiter-kernels with Kernels:
# !pip install kernels from kernels import get_kernel kernel = get_kernel("kernels-community/aiter-kernels") - Notebooks
- Google Colab
- Kaggle
| # SPDX-License-Identifier: MIT | |
| # Copyright (C) 2024-2026, Advanced Micro Devices, Inc. All rights reserved. | |
| import torch | |
| import triton | |
| from typing import Optional | |
| from ..utils.types import get_dtype_max | |
| from ..utils.device_info import get_num_sms | |
| from .._triton_kernels.normalization.rmsnorm import ( | |
| _rms_norm_kernel, | |
| _quant_rms_norm_kernel, | |
| _fused_add_rmsnorm_kernel, | |
| _quant_fused_add_rmsnorm_kernel, | |
| _rmsnorm_bwd_triton, | |
| _rmsnorm_bwd_dg_reduce_triton, | |
| _rmsnorm_kernel_large_m_small_n, | |
| ) | |
| from ..utils.logger import AiterTritonLogger | |
| _LOGGER = AiterTritonLogger() | |
| def num_programs(x): | |
| return min(x.shape[0], get_num_sms()) | |
| def block_size(x): | |
| return min(65536 // x.element_size(), triton.next_power_of_2(x.shape[1])) | |
| def use_blocked(x): | |
| return x.shape[1] > block_size(x) | |
| def dg_tmp_rows(x): | |
| return x.shape[0] if use_blocked(x) else num_programs(x) | |
| def _rmsnorm_forward(x: torch.Tensor, weight: torch.Tensor, epsilon: float): | |
| n_rows, n_cols = x.shape | |
| y = torch.empty_like(x) | |
| rsigma = torch.empty((n_rows,), dtype=torch.float32, device=x.device) | |
| blk_size = block_size(x) | |
| USE_BLOCKED = use_blocked(x) | |
| NUM_PRGMS = num_programs(x) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _rms_norm_kernel[grid]( | |
| x, | |
| y, | |
| weight, | |
| rsigma, | |
| x.stride(0), | |
| y.stride(0), | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| return y, rsigma | |
| def _rmsnorm_forward_with_add( | |
| out: torch.Tensor, | |
| x: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| weight: torch.Tensor, | |
| rsigma: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| n_rows, n_cols = x.shape | |
| blk_size = block_size(x) | |
| USE_BLOCKED = use_blocked(x) | |
| NUM_PRGMS = num_programs(x) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _fused_add_rmsnorm_kernel[grid]( | |
| x, | |
| out, | |
| residual_in, | |
| residual_out, | |
| weight, | |
| rsigma, | |
| x.stride(0), | |
| out.stride(0), | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| def _rmsnorm_backward(dz, x, gamma, rsigma): | |
| dz_ = dz.contiguous() | |
| x_ = x.contiguous() | |
| gamma_ = gamma.contiguous() | |
| rsigma_ = rsigma.contiguous() | |
| dx = torch.empty_like(x_) | |
| dgamma = torch.empty_like(gamma_) | |
| M, N = x_.shape | |
| blk_size = block_size(x_) | |
| USE_BLOCKED = use_blocked(x_) | |
| NUM_PRGMS = num_programs(x_) | |
| need_reduction = N > 1 | |
| dg_tmp = ( | |
| torch.empty( | |
| dg_tmp_rows(x_), N, device="cuda", dtype=torch.float32, requires_grad=False | |
| ) | |
| if need_reduction | |
| else None | |
| ) | |
| grid_bwd = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _rmsnorm_bwd_triton[grid_bwd]( | |
| dz_, | |
| x_, | |
| gamma_, | |
| rsigma_, | |
| dx, | |
| dg_tmp if need_reduction else dgamma, | |
| x_.stride(0), | |
| dz_.stride(0), | |
| M, | |
| N, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| num_warps=8, | |
| ) | |
| if need_reduction: | |
| grid_reduce = lambda meta: [triton.cdiv(N, meta["BLOCK_SIZE_N"])] # noqa: E731 | |
| _rmsnorm_bwd_dg_reduce_triton[grid_reduce]( | |
| dg_tmp, | |
| dgamma, | |
| dg_tmp.stride(0), | |
| dg_tmp.shape[0], | |
| dg_tmp.shape[1], | |
| BLOCK_SIZE_M=128, | |
| BLOCK_SIZE_N=64, | |
| ) | |
| return dx, dgamma | |
| def _should_use_large_m_small_n(M: int, N: int) -> bool: | |
| if M > 8192 and N <= 2048: | |
| return True | |
| return False | |
| def rmsnorm_forward_inference(x: torch.Tensor, weight: torch.Tensor, eps: float): | |
| assert x.ndim == 2 and weight.ndim == 1 and x.shape[1] == weight.shape[0] | |
| x = x.contiguous() | |
| weight = weight.contiguous() | |
| M, N = x.shape | |
| if _should_use_large_m_small_n(M, N): | |
| return _rmsnorm_forward_large_m_small_n(x, weight, eps, return_rsigma=False) | |
| else: | |
| y, _ = _rmsnorm_forward( | |
| x, weight, eps | |
| ) # always returns rsigma, but we discard it | |
| return y | |
| class _RMSNorm(torch.autograd.Function): | |
| def forward(ctx, x, weight, epsilon, is_grad_enabled): | |
| is_grad = is_grad_enabled and any( | |
| tensor.requires_grad for tensor in [x, weight] | |
| ) | |
| M, N = x.shape | |
| if _should_use_large_m_small_n(M, N): | |
| out = _rmsnorm_forward_large_m_small_n( | |
| x, weight, epsilon, return_rsigma=is_grad | |
| ) | |
| if is_grad: | |
| y, rsigma = out | |
| else: | |
| y = out | |
| rsigma = None | |
| else: | |
| y, rsigma = _rmsnorm_forward(x, weight, epsilon) | |
| if is_grad: | |
| ctx.save_for_backward(x, weight, rsigma) | |
| return y | |
| def backward(ctx, grad_output): | |
| x, weight, rsigma = ctx.saved_tensors | |
| dx, dg = _rmsnorm_backward(grad_output, x, weight, rsigma) | |
| return dx, dg, None, None | |
| class _RMSNorm2dFwdWithAdd(torch.autograd.Function): | |
| def forward(ctx, y, x, res_in, res_out, weight, epsilon, is_grad_enabled): | |
| is_grad = is_grad_enabled and any( | |
| tensor.requires_grad for tensor in [x, weight] | |
| ) | |
| M = x.shape[0] | |
| rsigma = torch.empty((M,), dtype=torch.float32, device=x.device) | |
| _rmsnorm_forward_with_add(y, x, res_in, res_out, weight, rsigma, epsilon) | |
| if is_grad: | |
| ctx.save_for_backward(res_out, weight, rsigma) | |
| return y | |
| def backward(ctx, grad_output): | |
| x, weight, rsigma = ctx.saved_tensors | |
| dx, dg = _rmsnorm_backward(grad_output, x, weight, rsigma) | |
| return None, dx, None, None, dg, None, None | |
| def rms_norm(input: torch.Tensor, weight: torch.Tensor, epsilon: float): | |
| """ | |
| Applies Root Mean Square Layer Normalization over a mini-batch of inputs. | |
| Key parameters: | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| Returns: | |
| - Output: The output tensor with shape (M, N). | |
| """ | |
| _LOGGER.info(f"RMSNORM: input={tuple(input.shape)} weight={tuple(weight.shape)} ") | |
| return _RMSNorm.apply(input, weight, epsilon, torch.is_grad_enabled()) | |
| def rmsnorm2d_fwd_with_add( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| weight: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| """ | |
| Performs an addition between two inputs and then applies Root Mean Square Layer Normalization over | |
| the addition result. | |
| Key parameters: | |
| - Out: The tensor where the output will be stored with shape (M, N). | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Residual_in: The tensor to be added to the Input tensor with shape (M, N). | |
| - Residual_out: The tensor in which the addition result will be stored with shape (M, N). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| Returns: | |
| - Output: The output tensor with shape (M, N). | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_ADD: input={tuple(input.shape)} weight={tuple(weight.shape)} residual_in={tuple(residual_in.shape)} " | |
| ) | |
| return _RMSNorm2dFwdWithAdd.apply( | |
| out, input, residual_in, residual_out, weight, epsilon, torch.is_grad_enabled() | |
| ) | |
| def rmsnorm2d_fwd_with_smoothquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| xscale: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| """ | |
| Applies Root Mean Square Layer Normalization over a mini-batch of inputs and quantizes the result. | |
| Key parameters: | |
| - Out: The tensor where the output will be stored with shape (M, N). | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Xscale: The tensor to be multiplied by the RMSNorm output, with shape (N, ). | |
| - Yscale: The tensor where the scale for each row will be stored with shape (M, ). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_SMOOTHQUANT: input={tuple(input.shape)} weight={tuple(weight.shape)} " | |
| + f"xscale={tuple(xscale.shape)} yscale={tuple(yscale.shape)} " | |
| ) | |
| n_rows, n_cols = input.shape | |
| blk_size = block_size(input) | |
| USE_BLOCKED = use_blocked(input) | |
| NUM_PRGMS = num_programs(input) | |
| IS_SMOOTH = True | |
| DTYPE_MAX = get_dtype_max(out.dtype) | |
| scale_ub = None | |
| out_rmsnorm = None | |
| CLAMP_MAX = False | |
| clamp_out = False | |
| dump_rms_norm = False | |
| # Auxiliary tensor to store the RMSNorm output as fp32 before applying the quantization when using the blocked approach | |
| aux = None | |
| if USE_BLOCKED: | |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _quant_rms_norm_kernel[grid]( | |
| input, | |
| out, | |
| xscale, | |
| yscale, | |
| weight, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0) if USE_BLOCKED else None, | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| scale_ub, | |
| out_rmsnorm, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| CLAMP_MAX, | |
| clamp_out, | |
| dump_rms_norm, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| def rmsnorm2d_fwd_with_dynamicquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| epsilon: float, | |
| scale_ub: Optional[torch.Tensor] = None, | |
| clamp_out: bool = False, | |
| dump_rms_norm: bool = False, | |
| ): | |
| """ | |
| Applies Root Mean Square Layer Normalization over a mini-batch of inputs and quantizes the result. | |
| Key parameters: | |
| - Out: The tensor where the output will be stored with shape (M, N). | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Yscale: The tensor where the scale for each row will be stored with shape (M, ). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_DYNAMICQUANT: input={tuple(input.shape)} weight={tuple(weight.shape)} yscale={tuple(yscale.shape)} " | |
| ) | |
| n_rows, n_cols = input.shape | |
| blk_size = block_size(input) | |
| USE_BLOCKED = use_blocked(input) | |
| NUM_PRGMS = num_programs(input) | |
| xscale = None | |
| IS_SMOOTH = False | |
| DTYPE_MAX = get_dtype_max(out.dtype) | |
| CLAMP_MAX = scale_ub is not None | |
| out_rms_norm = None | |
| if dump_rms_norm: | |
| out_rms_norm = torch.empty_like(input) | |
| # Auxiliary tensor to store the RMSNorm output as fp32 before applying the quantization when using the blocked approach | |
| aux = None | |
| if USE_BLOCKED: | |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _quant_rms_norm_kernel[grid]( | |
| input, | |
| out, | |
| xscale, | |
| yscale, | |
| weight, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0) if USE_BLOCKED else None, | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| scale_ub, | |
| out_rms_norm, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| CLAMP_MAX, | |
| clamp_out, | |
| dump_rms_norm, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| return out_rms_norm | |
| def rmsnorm2d_fwd_with_add_smoothquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| xscale: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| """ | |
| Performs an addition between two inputs and then applies Root Mean Square Layer Normalization over | |
| the addition result followed by a quantization. | |
| Key parameters: | |
| - Out: The tensor where the output will be stored with shape (M, N). | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Residual_in: The tensor to be added to the Input tensor with shape (M, N). | |
| - Residual_out: The tensor in which the addition result will be stored with shape (M, N). | |
| - Xscale: The tensor to be multiplied by the RMSNorm output, with shape (N, ). | |
| - Yscale: The tensor where the scale for each row will be stored with shape (M, ). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_ADD_SMOOTHQUANT: input={tuple(input.shape)} weight={tuple(weight.shape)} " | |
| + f"residual_in={tuple(residual_in.shape)} xscale={tuple(xscale.shape)} yscale={tuple(yscale.shape)} " | |
| ) | |
| n_rows, n_cols = input.shape | |
| blk_size = block_size(input) | |
| USE_BLOCKED = use_blocked(input) | |
| NUM_PRGMS = num_programs(input) | |
| IS_SMOOTH = True | |
| DTYPE_MAX = get_dtype_max(out.dtype) | |
| # Auxiliary tensor to store the RMSNorm output as fp32 before applying the quantization when using the blocked approach | |
| aux = None | |
| if USE_BLOCKED: | |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _quant_fused_add_rmsnorm_kernel[grid]( | |
| input, | |
| out, | |
| residual_in, | |
| residual_out, | |
| xscale, | |
| yscale, | |
| weight, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0) if USE_BLOCKED else None, | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| def rmsnorm2d_fwd_with_add_dynamicquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| """ | |
| Performs an addition between two inputs and then applies Root Mean Square Layer Normalization over | |
| the addition result followed by a quantization. | |
| Key parameters: | |
| - Out: The tensor where the output will be stored with shape (M, N). | |
| - Input: The input tensor to be normalized with shape (M, N). | |
| - Residual_in: The tensor to be added to the Input tensor with shape (M, N). | |
| - Residual_out: The tensor in which the addition result will be stored with shape (M, N). | |
| - Yscale: The tensor where the scale for each row will be stored with shape (M, ). | |
| - Weight: The learnable weights tensor with shape (N, ). | |
| - Epsilon: A value added to the denominator for numerical stability. | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_ADD_DYNAMICQUANT: input={input.shape} weight={weight.shape} residual_in={residual_in.shape} yscale={yscale.shape} " | |
| ) | |
| n_rows, n_cols = input.shape | |
| blk_size = block_size(input) | |
| USE_BLOCKED = use_blocked(input) | |
| NUM_PRGMS = num_programs(input) | |
| xscale = None | |
| IS_SMOOTH = False | |
| DTYPE_MAX = get_dtype_max(out.dtype) | |
| # Auxiliary tensor to store the RMSNorm output as fp32 before applying the quantization when using the blocked approach | |
| aux = None | |
| if USE_BLOCKED: | |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) | |
| grid = lambda meta: (NUM_PRGMS,) # noqa: E731 | |
| _quant_fused_add_rmsnorm_kernel[grid]( | |
| input, | |
| out, | |
| residual_in, | |
| residual_out, | |
| xscale, | |
| yscale, | |
| weight, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0) if USE_BLOCKED else None, | |
| n_rows, | |
| n_cols, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| blk_size, | |
| USE_BLOCKED, | |
| NUM_PRGMS, | |
| ) | |
| def _rmsnorm_forward_large_m_small_n( | |
| x: torch.Tensor, | |
| weight: torch.Tensor, | |
| eps: float, | |
| return_rsigma: bool = False, | |
| ): | |
| assert x.ndim == 2 and weight.ndim == 1 and x.shape[1] == weight.shape[0] | |
| x, weight = x.contiguous(), weight.contiguous() | |
| M, N = x.shape | |
| y = torch.empty_like(x) | |
| rsigma = ( | |
| torch.empty(M, dtype=torch.float32, device=x.device) if return_rsigma else None | |
| ) | |
| BLOCK_N = triton.next_power_of_2(N) | |
| BLOCK_M = min(16384 // BLOCK_N, 32) | |
| BLOCK_M = max(BLOCK_M, 8) | |
| grid = (triton.cdiv(M, BLOCK_M),) | |
| _rmsnorm_kernel_large_m_small_n[grid]( | |
| x, | |
| y, | |
| weight, | |
| rsigma, | |
| M, | |
| N, | |
| eps, | |
| x.stride(0), | |
| x.stride(1), | |
| y.stride(0), | |
| y.stride(1), | |
| BLOCK_M=BLOCK_M, | |
| BLOCK_N=BLOCK_N, | |
| num_warps=8, | |
| num_stages=2, | |
| ) | |
| return (y, rsigma) if return_rsigma else y | |