| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| from typing import Tuple |
|
|
| import torch |
| import triton |
| import triton.language as tl |
|
|
|
|
| |
| |
| |
| |
| @triton.jit |
| def _per_token_group_quant_fp8( |
| y_ptr, |
| y_q_ptr, |
| y_s_ptr, |
| y_stride, |
| N, |
| eps, |
| fp8_min, |
| fp8_max, |
| BLOCK: tl.constexpr, |
| ): |
| """A Triton-accelerated function for per-token-group quantization.""" |
| g_id = tl.program_id(0) |
| y_ptr += g_id * y_stride |
| y_q_ptr += g_id * y_stride |
| y_s_ptr += g_id |
|
|
| cols = tl.arange(0, BLOCK) |
| mask = cols < N |
|
|
| y = tl.load(y_ptr + cols, mask=mask, other=0.0).to(tl.float32) |
| _absmax = tl.maximum(tl.max(tl.abs(y)), eps) |
| y_s = _absmax / fp8_max |
| y_s_inv = 1.0 / y_s |
| y_q = tl.clamp(y * y_s_inv, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty) |
|
|
| tl.store(y_q_ptr + cols, y_q, mask=mask) |
| tl.store(y_s_ptr, y_s) |
|
|
|
|
| @triton.jit |
| def _per_token_group_quant_fp8_colmajor( |
| y_ptr, |
| y_q_ptr, |
| y_s_ptr, |
| group_size, |
| y_num_columns, |
| y_s_col_stride, |
| eps, |
| fp8_min, |
| fp8_max, |
| BLOCK: tl.constexpr, |
| ): |
| """A Triton-accelerated function for per-token-group quantization.""" |
| g_id = tl.program_id(0) |
| y_ptr += g_id * group_size |
| y_q_ptr += g_id * group_size |
|
|
| blocks_per_row = y_num_columns // group_size |
| scale_col = g_id % blocks_per_row |
| scale_row = g_id // blocks_per_row |
| y_s_ptr += scale_col * y_s_col_stride + scale_row |
|
|
| cols = tl.arange(0, BLOCK) |
| mask = cols < group_size |
|
|
| y = tl.load(y_ptr + cols, mask=mask, other=0.0).to(tl.float32) |
| _absmax = tl.maximum(tl.max(tl.abs(y)), eps) |
| y_s = _absmax / fp8_max |
| y_q = tl.clamp(y / y_s, fp8_min, fp8_max).to(y_q_ptr.dtype.element_ty) |
|
|
| tl.store(y_q_ptr + cols, y_q, mask=mask) |
| tl.store(y_s_ptr, y_s) |
|
|
|
|
| def fp8_per_token_group_quant_triton( |
| x: torch.Tensor, |
| group_size: int, |
| eps: float = 1e-10, |
| dtype: torch.dtype = torch.float8_e4m3fn, |
| column_major_scales: bool = False, |
| scale_tma_aligned: bool = False, |
| ) -> Tuple[torch.Tensor, torch.Tensor]: |
| """Function to perform per-token-group quantization on an input tensor `x`.""" |
| assert ( |
| x.shape[-1] % group_size == 0 |
| ), "the last dimension of `x` cannot be divisible by `group_size`" |
| assert x.is_contiguous(), "`x` is not contiguous" |
|
|
| finfo = torch.finfo(dtype) |
| fp8_max = finfo.max |
| fp8_min = -fp8_max |
|
|
| x_q = torch.empty_like(x, device=x.device, dtype=dtype) |
| M = x.numel() // group_size |
| N = group_size |
|
|
| if column_major_scales: |
| if scale_tma_aligned: |
| aligned_size = (x.shape[-2] + 3) // 4 * 4 |
| x_s = torch.empty( |
| x.shape[:-2] + (x.shape[-1] // group_size, aligned_size), |
| device=x.device, |
| dtype=torch.float32, |
| ).permute(-1, -2)[: x.shape[-2], :] |
| else: |
| x_s = torch.empty( |
| (x.shape[-1] // group_size,) + x.shape[:-1], |
| device=x.device, |
| dtype=torch.float32, |
| ).permute(-1, -2) |
| else: |
| x_s = torch.empty( |
| x.shape[:-1] + (x.shape[-1] // group_size,), |
| device=x.device, |
| dtype=torch.float32, |
| ) |
|
|
| BLOCK = triton.next_power_of_2(N) |
| num_warps = min(max(BLOCK // 256, 1), 8) |
| num_stages = 1 |
|
|
| if column_major_scales: |
| _per_token_group_quant_fp8_colmajor[(M,)]( |
| x, |
| x_q, |
| x_s, |
| group_size, |
| x.shape[1], |
| x_s.stride(1), |
| eps, |
| fp8_min=fp8_min, |
| fp8_max=fp8_max, |
| BLOCK=BLOCK, |
| num_warps=num_warps, |
| num_stages=num_stages, |
| ) |
| else: |
| _per_token_group_quant_fp8[(M,)]( |
| x, |
| x_q, |
| x_s, |
| group_size, |
| N, |
| eps, |
| fp8_min=fp8_min, |
| fp8_max=fp8_max, |
| BLOCK=BLOCK, |
| num_warps=num_warps, |
| num_stages=num_stages, |
| ) |
|
|
| return x_q, x_s |
|
|