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 .._triton_kernels.normalization.norm import ( | |
| _layernorm_kernel, | |
| _fused_add_layernorm_kernel, | |
| _quant_layernorm_kernel, | |
| _quant_fused_add_layernorm_kernel, | |
| _layernorm_bwd_dx_fused_triton, | |
| _layernorm_bwd_dwdb_triton, | |
| _layernorm_bwd_dwdb_triton_v2, | |
| ) | |
| from ..utils.types import get_dtype_max | |
| from ..utils.logger import AiterTritonLogger | |
| _LOGGER = AiterTritonLogger() | |
| def _layernorm_forward( | |
| y: torch.Tensor, | |
| x: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| mean: torch.Tensor, | |
| rstd: torch.Tensor, | |
| eps: float = 1e-5, | |
| ): | |
| M, N = x.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // x.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| _layernorm_kernel[(M,)]( | |
| x, y, weight, bias, mean, rstd, x.stride(0), y.stride(0), M, N, eps, BLOCK_SIZE | |
| ) | |
| return | |
| def _layernorm_forward_with_add( | |
| y: torch.Tensor, | |
| x: torch.Tensor, | |
| res_in: torch.Tensor, | |
| res_out: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| mean: torch.Tensor, | |
| rstd: torch.Tensor, | |
| epsilon: float, | |
| ): | |
| M, N = x.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // x.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| _fused_add_layernorm_kernel[(M,)]( | |
| x, | |
| y, | |
| res_in, | |
| res_out, | |
| weight, | |
| bias, | |
| mean, | |
| rstd, | |
| x.stride(0), | |
| y.stride(0), | |
| M, | |
| N, | |
| epsilon, | |
| BLOCK_SIZE, | |
| ) | |
| return | |
| def _layernorm_backward( | |
| dy: torch.Tensor, | |
| dx: torch.Tensor, | |
| dw: torch.Tensor, | |
| db: torch.Tensor, | |
| x: torch.Tensor, | |
| gamma: torch.Tensor, | |
| mu: torch.Tensor, | |
| rsigma: torch.Tensor, | |
| ): | |
| M, N = x.shape | |
| # calculate dw and db separately when M is small | |
| IGNORE_DW_DB_IN_FUSED = M <= 512 | |
| tile_num = max(min(256, M // 4), 1) | |
| if M <= 512 and M * N < 64 * 1024 * 1024: | |
| tile_num = M | |
| elif M >= 8192: | |
| tile_num = 2048 | |
| max_fused_size = 32768 // x.element_size() | |
| next_power = triton.next_power_of_2(N) | |
| BLOCK_SIZE = min(max_fused_size, next_power) | |
| # For cases with small M and large N, decrease block size to help with occupancy and register spill | |
| if tile_num == M: | |
| if tile_num > 256: | |
| BLOCK_SIZE = min(BLOCK_SIZE, 2048) | |
| else: | |
| BLOCK_SIZE = min(BLOCK_SIZE, 4096) | |
| USE_BLOCKED = N > BLOCK_SIZE | |
| num_warps = min(max(BLOCK_SIZE // 256, 1), 8) | |
| if not IGNORE_DW_DB_IN_FUSED: | |
| _dw = torch.zeros((tile_num, N), dtype=torch.float32, device=gamma.device) | |
| _db = torch.zeros((tile_num, N), dtype=torch.float32, device=gamma.device) | |
| else: | |
| _dw = None | |
| _db = None | |
| grid_bwd = (tile_num,) | |
| _layernorm_bwd_dx_fused_triton[grid_bwd]( | |
| dx, | |
| dy, | |
| _dw, | |
| _db, | |
| x, | |
| gamma, | |
| mu, | |
| rsigma, | |
| x.stride(0), | |
| N, | |
| NUM_ROWS=M, | |
| BLOCK_SIZE_N=BLOCK_SIZE, | |
| USE_BLOCKED=USE_BLOCKED, | |
| num_warps=num_warps, | |
| IGNORE_DW_DB=IGNORE_DW_DB_IN_FUSED, | |
| ) | |
| grid_reduce = lambda meta: (triton.cdiv(N, meta["BLOCK_SIZE_N"]),) # noqa: E731 | |
| if not IGNORE_DW_DB_IN_FUSED: | |
| dwdb_block_n = max(16, N // 256) | |
| dwdb_block_n = triton.next_power_of_2(dwdb_block_n) | |
| dwdb_block_m = (64 * 128) // dwdb_block_n | |
| dwdb_block_m = min(triton.next_power_of_2(tile_num), dwdb_block_m) | |
| _layernorm_bwd_dwdb_triton[grid_reduce]( | |
| _dw, | |
| _db, | |
| dw, | |
| db, | |
| min(tile_num, M), | |
| N, | |
| BLOCK_SIZE_M=dwdb_block_m, | |
| BLOCK_SIZE_N=dwdb_block_n, | |
| ) | |
| else: | |
| dwdb_block_n = max(16, N // 256) | |
| dwdb_block_n = triton.next_power_of_2(dwdb_block_n) | |
| dwdb_block_m = (64 * 128) // dwdb_block_n | |
| dwdb_block_m = min(triton.next_power_of_2(M), dwdb_block_m) | |
| _layernorm_bwd_dwdb_triton_v2[grid_reduce]( | |
| x, | |
| dy, | |
| mu, | |
| rsigma, | |
| x.stride(0), | |
| dw, | |
| db, | |
| M, | |
| N, | |
| BLOCK_SIZE_M=dwdb_block_m, | |
| BLOCK_SIZE_N=dwdb_block_n, | |
| ) | |
| return dx, dw, db | |
| class _LayerNorm(torch.autograd.Function): | |
| def forward(ctx, x, weight, bias, eps, is_grad_enabled): | |
| is_grad = is_grad_enabled and any( | |
| tensor.requires_grad for tensor in [x, weight, bias] | |
| ) | |
| y = torch.empty_like(x) | |
| M = x.shape[0] | |
| mean = torch.empty((M,), dtype=torch.float32, device=x.device) | |
| rstd = torch.empty((M,), dtype=torch.float32, device=x.device) | |
| _layernorm_forward(y, x, weight, bias, mean, rstd, eps) | |
| if is_grad: | |
| ctx.save_for_backward(x, weight, mean, rstd) | |
| return y | |
| def backward(ctx, dy): | |
| x, w, m, v = ctx.saved_tensors | |
| N = w.shape[0] | |
| dw = torch.empty((N,), dtype=w.dtype, device=w.device) | |
| db = torch.empty((N,), dtype=w.dtype, device=w.device) | |
| dx = torch.empty_like(dy) | |
| _layernorm_backward(dy, dx, dw, db, x, w, m, v) | |
| return dx, dw, db, None, None | |
| class _Layernorm2dFwdWithAdd(torch.autograd.Function): | |
| def forward(ctx, y, x, res_in, res_out, weight, bias, eps, is_grad_enabled): | |
| is_grad = is_grad_enabled and any( | |
| tensor.requires_grad for tensor in [x, weight, bias] | |
| ) | |
| M = x.shape[0] | |
| mean = torch.empty((M,), dtype=torch.float32, device=x.device) | |
| rstd = torch.empty((M,), dtype=torch.float32, device=x.device) | |
| _layernorm_forward_with_add( | |
| y, x, res_in, res_out, weight, bias, mean, rstd, eps | |
| ) | |
| if is_grad: | |
| ctx.save_for_backward(res_out, weight, mean, rstd) | |
| return y | |
| def backward(ctx, dy): | |
| x, w, m, v = ctx.saved_tensors | |
| N = w.shape[0] | |
| dw = torch.empty((N,), dtype=w.dtype, device=w.device) | |
| db = torch.empty((N,), dtype=w.dtype, device=w.device) | |
| dx = torch.empty_like(dy) | |
| _layernorm_backward(dy, dx, dw, db, x, w, m, v) | |
| return None, dx, None, None, dw, db, None, None | |
| def layer_norm( | |
| input: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| eps: float = 1e-5, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Applies 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, ). | |
| - bias: The learnable bias tensor with shape (N, ) | |
| - eps: A value added to the denominator for numerical stability. | |
| Returns: | |
| - Output: The output tensor with shape (M, N). | |
| """ | |
| _LOGGER.info(f"LAYERNORM: input={tuple(input.shape)} weight={tuple(weight.shape)} ") | |
| return _LayerNorm.apply(input, weight, bias, eps, torch.is_grad_enabled()) | |
| def layernorm2d_fwd_with_add( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| epsilon: float, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Adds two inputs and then applies Layer Normalization | |
| Key parameters: | |
| - out: The output of layer normalization with shape (M, N). Allocated by the caller | |
| - input: The input tensor to be normalized with shape (M, N). | |
| - residual_in: Tensor added to the input and same shape as input (M, N) | |
| - residual_out: Output tensor that is input + residual_in with shape (M, N). Must be allocated by the caller | |
| - weight: The learnable weights tensor with shape (N, ). | |
| - bias: Bias added to the result of layer norm with shape (N,) | |
| - epsilon: A value added to the denominator for numerical stability. | |
| Returns: | |
| - out: The output tensor with shape (M, N). | |
| - residual_out: Output tensor that is input + residual_in with shape (M, N). | |
| """ | |
| _LOGGER.info( | |
| f"LAYERNORM_2D_FWD_ADD: input={tuple(input.shape)} weight={tuple(weight.shape)} residual_in={tuple(residual_in.shape)} " | |
| ) | |
| return _Layernorm2dFwdWithAdd.apply( | |
| out, | |
| input, | |
| residual_in, | |
| residual_out, | |
| weight, | |
| bias, | |
| epsilon, | |
| torch.is_grad_enabled(), | |
| ) | |
| def layernorm2d_fwd_with_dynamicquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| epsilon: float = 1e-5, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Applies Layer Normalization and then quantizes the output | |
| Key parameters: | |
| - out: The output of layer normalization with shape (M, N). Allocated by the caller | |
| - input: The input tensor to be normalized with shape (M, N) and dtype in (fp32, fp16 or bf16) | |
| - yscale: Output scale tensor with shape (M,) and dtype fp32. Allocated by the caller | |
| - weight: The learnable weights tensor with shape (N, ). | |
| - bias: Bias added to the result of layer norm with shape (N,) | |
| - eps: A value added to the denominator for numerical stability. | |
| Returns: | |
| - out: The output tensor with shape (M, N). | |
| - yscale: Output scale tensor with shape (M,). Allocated by the caller | |
| """ | |
| _LOGGER.info( | |
| f"LAYERNORM_2D_FWD_DYNAMICQUANT: input={tuple(input.shape)} weight={tuple(weight.shape)} yscale={tuple(yscale.shape)} " | |
| ) | |
| M, N = input.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // input.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| 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 = torch.empty(M, N, dtype=torch.float32, device=input.device) | |
| _quant_layernorm_kernel[(M,)]( | |
| input, | |
| out, | |
| weight, | |
| bias, | |
| xscale, | |
| yscale, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0), | |
| M, | |
| N, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| BLOCK_SIZE, | |
| ) | |
| return | |
| def layernorm2d_fwd_with_smoothquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| xscale: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| epsilon: float = 1e-5, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Applies Layer Normalization and then quantizes the output | |
| Key parameters: | |
| - input: The input tensor to be normalized with shape (M, N). | |
| - xscale: Input scale tensor which is multiplied with the output of layer normalization before quantization. | |
| - yscale: Output scale tensor with shape (M,) and dtype fp32. Allocated by the caller | |
| - weight: The learnable weights tensor with shape (N, ). | |
| - bias: Bias added to the result of layer norm with shape (N,) | |
| - eps: A value added to the denominator for numerical stability. | |
| Returns: | |
| - Output: The output tensor with shape (M, N). | |
| """ | |
| _LOGGER.info( | |
| f"RMSNORM_2D_FWD_SMOOTHQUANT: input={tuple(input.shape)} weight={tuple(weight.shape)} xscale={tuple(xscale.shape)} yscale={tuple(yscale.shape)} " | |
| ) | |
| M, N = input.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // input.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| 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 = torch.empty(M, N, dtype=torch.float32, device=input.device) | |
| _quant_layernorm_kernel[(M,)]( | |
| input, | |
| out, | |
| weight, | |
| bias, | |
| xscale, | |
| yscale, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0), | |
| M, | |
| N, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| BLOCK_SIZE, | |
| ) | |
| return | |
| def layernorm2d_fwd_with_add_dynamicquant( | |
| out: torch.Tensor, | |
| input: torch.Tensor, | |
| residual_in: torch.Tensor, | |
| residual_out: torch.Tensor, | |
| yscale: torch.Tensor, | |
| weight: torch.Tensor, | |
| bias: torch.Tensor, | |
| epsilon: float = 1e-5, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Adds two input toegether, then does layer Normalization before quantizing the final output | |
| Key parameters: | |
| - out: The output of layer normalization with shape (M, N). Allocated by the caller | |
| - input: The input tensor to be normalized with shape (M, N) and dtype in (fp32, fp16 or bf16) | |
| - residual_in: Tensor added to the input and same shape as input (M, N) | |
| - residual_out: Output tensor that is input + residual_in with shape (M, N). Must be allocated by the caller | |
| - yscale: Output scale tensor with shape (M,) and dtype fp32. Allocated by the caller | |
| - weight: The learnable weights tensor with shape (N, ). | |
| - bias: Bias added to the result of layer norm with shape (N,) | |
| - eps: A value added to the denominator for numerical stability. | |
| Returns: | |
| - out: The output tensor with shape (M, N). | |
| - yscale: Output scale tensor with shape (M,). Allocated by the caller | |
| """ | |
| _LOGGER.info( | |
| f"LAYERNORM_2D_FWD_ADD_DYNAMICQUANT: input={input.shape} weight={weight.shape} residual_in={residual_in.shape} yscale={yscale.shape} " | |
| ) | |
| M, N = input.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // input.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| 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 = torch.empty(M, N, dtype=torch.float32, device=input.device) | |
| _quant_fused_add_layernorm_kernel[(M,)]( | |
| input, | |
| out, | |
| residual_in, | |
| residual_out, | |
| weight, | |
| bias, | |
| xscale, | |
| yscale, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0), | |
| M, | |
| N, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| BLOCK_SIZE, | |
| ) | |
| return | |
| def layernorm2d_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, | |
| bias: torch.Tensor, | |
| epsilon: float = 1e-5, | |
| x_bias: Optional[torch.Tensor] = None, | |
| ) -> torch.Tensor: | |
| """ | |
| Applies Layer Normalization and then quantizes the output | |
| Key parameters: | |
| - input: The input tensor to be normalized with shape (M, N). | |
| - residual_in: Tensor added to the input and same shape as input (M, N) | |
| - residual_out: Output tensor that is input + residual_in with shape (M, N). Must be allocated by the caller | |
| - xscale: Input scale tensor which is multiplied with the output of layer normalization before quantization. | |
| - yscale: Output scale tensor with shape (M,) and dtype fp32. Allocated by the caller | |
| - weight: The learnable weights tensor with shape (N, ). | |
| - bias: Bias added to the result of layer norm with shape (N,) | |
| - eps: A value added to the denominator for numerical stability. | |
| Returns: | |
| - Output: The output tensor with shape (M, N). | |
| - yscale: Output scale tensor with shape (M,). Allocated by the caller | |
| """ | |
| _LOGGER.info( | |
| f"LAYERNORM_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)} " | |
| ) | |
| M, N = input.shape | |
| # Less than 64KB per feature: enqueue fused kernel | |
| MAX_FUSED_SIZE = 65536 // input.element_size() | |
| BLOCK_SIZE = min(MAX_FUSED_SIZE, triton.next_power_of_2(N)) | |
| 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 = torch.empty(M, N, dtype=torch.float32, device=input.device) | |
| _quant_fused_add_layernorm_kernel[(M,)]( | |
| input, | |
| out, | |
| residual_in, | |
| residual_out, | |
| weight, | |
| bias, | |
| xscale, | |
| yscale, | |
| aux, | |
| input.stride(0), | |
| out.stride(0), | |
| aux.stride(0), | |
| M, | |
| N, | |
| epsilon, | |
| DTYPE_MAX, | |
| IS_SMOOTH, | |
| BLOCK_SIZE, | |
| ) | |
| return | |