| |
| |
|
|
| 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,) |
| _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,) |
| _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,) |
| _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"])] |
| _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 |
| ) |
| return y |
|
|
|
|
| class _RMSNorm(torch.autograd.Function): |
|
|
| @staticmethod |
| 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 |
|
|
| @staticmethod |
| 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): |
|
|
| @staticmethod |
| 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 |
|
|
| @staticmethod |
| 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 |
|
|
| |
| aux = None |
| if USE_BLOCKED: |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) |
|
|
| grid = lambda meta: (NUM_PRGMS,) |
| _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) |
|
|
| |
| aux = None |
| if USE_BLOCKED: |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) |
|
|
| grid = lambda meta: (NUM_PRGMS,) |
| _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) |
|
|
| |
| aux = None |
| if USE_BLOCKED: |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) |
|
|
| grid = lambda meta: (NUM_PRGMS,) |
| _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) |
|
|
| |
| aux = None |
| if USE_BLOCKED: |
| aux = torch.empty(n_rows, n_cols, dtype=torch.float32, device=input.device) |
|
|
| grid = lambda meta: (NUM_PRGMS,) |
| _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 |
|
|