59a0a3844c
PR Test AMD / cancel-on-close (push) Has been skipped
PR Test NVIDIA ARM / scan (push) Has been skipped
PR Test NVIDIA / cancel-on-close (push) Has been skipped
PR Test AMD / scan (push) Has been skipped
PR Test NVIDIA ARM / cancel-on-close (push) Has been skipped
PR Test NVIDIA / scan (push) Has been skipped
Release Docker Images / build (cu129-torch-2.11.0) (push) Has been skipped
Release Docker Images / build (cu130-torch-2.11.0) (push) Has been skipped
Release PyPI / publish (push) Has been skipped
Scheduler Python Test / test (push) Successful in 27m19s
Docs / build (push) Successful in 28m8s
Scheduler C++ Test / test (push) Successful in 28m19s
Scheduler C++ Test / test-flat (push) Successful in 28m18s
Docs / deploy (push) Has been cancelled
PR Test AMD / finish (push) Has been cancelled
PR Test NVIDIA / finish (push) Has been cancelled
PR Test NVIDIA ARM / finish (push) Has been cancelled
PR Test NVIDIA ARM / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test AMD / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
PR Test NVIDIA / ${{ matrix.name }} (${{ matrix.runner }}) (push) Has been cancelled
100 lines
3.8 KiB
Python
100 lines
3.8 KiB
Python
# Copyright (c) 2026 LightSeek Foundation
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
|
|
"""Reference fp8 quantization kernels.
|
|
|
|
Each reference returns ``qweight.float()`` — the fp8 quantized values cast back
|
|
to fp32 for comparison. The scale tensor's layout differs across producers
|
|
(SM90 vs SM100, row-major vs column-major) so we don't compare it directly;
|
|
the qweight values tell us whether the per-group statistics + rounding
|
|
agree.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import torch
|
|
from tokenspeed_kernel.platform import Platform
|
|
from tokenspeed_kernel.registry import register_kernel
|
|
from tokenspeed_kernel.signature import format_signatures
|
|
|
|
_FP8_DTYPE = Platform.get().fp8e4m3fn.dtype
|
|
_FP8_FINFO = torch.finfo(_FP8_DTYPE)
|
|
_FP8_MAX = _FP8_FINFO.max # 448 for e4m3fn
|
|
|
|
|
|
def _quantize_fp8(x_fp32: torch.Tensor, max_abs: torch.Tensor) -> torch.Tensor:
|
|
"""scale = max_abs/FP8_MAX (clamped), quantize → fp8, cast back to fp32."""
|
|
scale = (max_abs / _FP8_MAX).clamp(min=1e-10)
|
|
return (x_fp32 / scale).clamp(-_FP8_MAX, _FP8_MAX).to(_FP8_DTYPE).float()
|
|
|
|
|
|
@register_kernel(
|
|
"quantize",
|
|
"fp8_token_group_128",
|
|
name="torch_fp8_token_group_128",
|
|
solution="reference",
|
|
signatures=format_signatures("x", "dense", {torch.bfloat16, torch.float16}),
|
|
traits={},
|
|
priority=10,
|
|
tags={"determinism", "portability"},
|
|
)
|
|
def torch_fp8_token_group_128(x: torch.Tensor) -> torch.Tensor:
|
|
"""Per-token grouped fp8 quantization with group size 128."""
|
|
assert x.dim() == 2, f"expected 2D input, got {x.shape}"
|
|
M, K = x.shape
|
|
assert K % 128 == 0, f"K={K} must be divisible by group_size=128"
|
|
x_grouped = x.float().view(M, K // 128, 128)
|
|
max_abs = x_grouped.abs().amax(dim=-1, keepdim=True)
|
|
return _quantize_fp8(x_grouped, max_abs).view(M, K)
|
|
|
|
|
|
@register_kernel(
|
|
"quantize",
|
|
"fp8_token",
|
|
name="torch_fp8_token",
|
|
solution="reference",
|
|
signatures=format_signatures("x", "dense", {torch.bfloat16, torch.float16}),
|
|
traits={},
|
|
priority=10,
|
|
tags={"determinism", "portability"},
|
|
)
|
|
def torch_fp8_token(x: torch.Tensor) -> torch.Tensor:
|
|
"""Per-token fp8 quantization (one scale per row)."""
|
|
assert x.dim() == 2, f"expected 2D input, got {x.shape}"
|
|
x_fp32 = x.float()
|
|
return _quantize_fp8(x_fp32, x_fp32.abs().amax(dim=-1, keepdim=True))
|
|
|
|
|
|
@register_kernel(
|
|
"quantize",
|
|
"fp8_tensor",
|
|
name="torch_fp8_tensor",
|
|
solution="reference",
|
|
signatures=format_signatures("x", "dense", {torch.bfloat16, torch.float16}),
|
|
traits={},
|
|
priority=10,
|
|
tags={"determinism", "portability"},
|
|
)
|
|
def torch_fp8_tensor(x: torch.Tensor) -> torch.Tensor:
|
|
"""Per-tensor fp8 quantization (one scalar scale for the whole tensor)."""
|
|
assert x.dim() == 2, f"expected 2D input, got {x.shape}"
|
|
x_fp32 = x.float()
|
|
return _quantize_fp8(x_fp32, x_fp32.abs().amax())
|