94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
134 lines
4.1 KiB
Python
134 lines
4.1 KiB
Python
import sys
|
|
|
|
import pytest
|
|
import torch
|
|
from sgl_kernel import gptq_gemm
|
|
|
|
from sglang.srt.layers.quantization.utils import pack_cols, pack_rows
|
|
|
|
|
|
def torch_dequantize(q_weight, q_zeros, scales, g_idx, use_shuffle, bit, K, N):
|
|
assert bit == 4, "Reference dequantization only supports 4-bit"
|
|
group_size = K // scales.shape[0]
|
|
pack_factor = 32 // bit
|
|
|
|
# unpack q_weight: (K//pack_factor, N) -> (K, N)
|
|
unpacked_q_weight = torch.empty(
|
|
q_weight.shape[0] * pack_factor,
|
|
q_weight.shape[1],
|
|
dtype=torch.uint8,
|
|
device=q_weight.device,
|
|
)
|
|
for i in range(pack_factor):
|
|
unpacked_q_weight[i::pack_factor, :] = (q_weight >> (i * 4)) & 0x0F
|
|
|
|
# unpack q_zeros: (num_groups, N//pack_factor) -> (num_groups, N)
|
|
unpacked_q_zeros = torch.empty(
|
|
q_zeros.shape[0],
|
|
q_zeros.shape[1] * pack_factor,
|
|
dtype=torch.uint8,
|
|
device=q_zeros.device,
|
|
)
|
|
for i in range(pack_factor):
|
|
unpacked_q_zeros[:, i::pack_factor] = (q_zeros >> (i * 4)) & 0x0F
|
|
|
|
unpacked_q_zeros += 1
|
|
unpacked_q_zeros = unpacked_q_zeros.to(scales.dtype)
|
|
|
|
scale_zeros = unpacked_q_zeros * scales # (num_groups, N)
|
|
|
|
current_g_idx = torch.tensor(
|
|
[i // group_size for i in range(K)], dtype=torch.int32, device=q_weight.device
|
|
)
|
|
|
|
scale_mat = scales[current_g_idx] # (K, N)
|
|
scale_zeros_mat = scale_zeros[current_g_idx] # (K, N)
|
|
|
|
# dequant: weight * scale - scale_zeros
|
|
dequantized_b = unpacked_q_weight.to(scales.dtype) * scale_mat - scale_zeros_mat
|
|
|
|
return dequantized_b.reshape(K, N)
|
|
|
|
|
|
def torch_gptq_gemm(
|
|
a, b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx, use_shuffle, bit
|
|
):
|
|
K, N = a.shape[1], b_q_weight.shape[1]
|
|
|
|
b_dequant = torch_dequantize(
|
|
b_q_weight, b_gptq_qzeros, b_gptq_scales, b_g_idx, use_shuffle, bit, K, N
|
|
)
|
|
c = torch.matmul(a, b_dequant)
|
|
return c
|
|
|
|
|
|
def _test_gptq_gemm_once(M, N, K, bit, group_size, use_shuffle, dtype, device="cuda"):
|
|
|
|
b_fp = torch.randn(K, N, dtype=dtype, device=device)
|
|
|
|
assert K % group_size == 0, "K must be divisible by group_size"
|
|
num_groups = K // group_size
|
|
|
|
if use_shuffle:
|
|
return
|
|
else:
|
|
g_idx = torch.tensor(
|
|
[i // group_size for i in range(K)], dtype=torch.int32, device=device
|
|
)
|
|
b_shuffled = b_fp[g_idx]
|
|
|
|
b_grouped = b_shuffled.reshape(num_groups, group_size, N)
|
|
|
|
b_max = torch.max(b_grouped, dim=1, keepdim=True)[0]
|
|
b_min = torch.min(b_grouped, dim=1, keepdim=True)[0]
|
|
|
|
scales = (b_max - b_min) / (2**bit - 1)
|
|
scales = scales.clamp(min=1e-6)
|
|
|
|
zeros_float = (-b_min / scales).round()
|
|
|
|
q_b = (
|
|
(b_grouped / scales + zeros_float).round().clamp(0, 2**bit - 1).to(torch.uint8)
|
|
)
|
|
|
|
q_zeros_unpacked = zeros_float.to(torch.uint8) - 1
|
|
|
|
b_q_weight = pack_rows(q_b.reshape(K, N), bit, K, N)
|
|
|
|
q_zeros_unpacked = q_zeros_unpacked.reshape(num_groups, N)
|
|
b_gptq_qzeros = pack_cols(q_zeros_unpacked, bit, num_groups, N)
|
|
b_gptq_scales = scales.squeeze(1)
|
|
|
|
a = torch.randn(M, K, dtype=dtype, device=device)
|
|
|
|
c_ref = torch_gptq_gemm(
|
|
a, b_q_weight, b_gptq_qzeros, b_gptq_scales, g_idx, use_shuffle, bit
|
|
)
|
|
c_out = gptq_gemm(
|
|
a, b_q_weight, b_gptq_qzeros, b_gptq_scales, g_idx, use_shuffle, bit
|
|
)
|
|
|
|
rtol = 4e-2
|
|
atol = 4e-2
|
|
torch.testing.assert_close(c_ref, c_out, rtol=rtol, atol=atol)
|
|
print(
|
|
f"✅ Test passed: M={M}, N={N}, K={K}, bit={bit}, group_size={group_size}, use_shuffle={use_shuffle}, dtype={dtype}"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize("M", [1, 8, 128])
|
|
@pytest.mark.parametrize("N", [2048, 4096])
|
|
@pytest.mark.parametrize("K", [2048, 4096])
|
|
@pytest.mark.parametrize("bit", [4])
|
|
@pytest.mark.parametrize("group_size", [128])
|
|
@pytest.mark.parametrize("use_shuffle", [False])
|
|
@pytest.mark.parametrize("dtype", [torch.float16])
|
|
def test_gptq_gemm(M, N, K, bit, group_size, use_shuffle, dtype):
|
|
if not torch.cuda.is_available():
|
|
pytest.skip("CUDA not available")
|
|
_test_gptq_gemm_once(M, N, K, bit, group_size, use_shuffle, dtype, "cuda")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(pytest.main([__file__, "-v"]))
|