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
383 lines
14 KiB
Python
383 lines
14 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Callable, Optional
|
|
|
|
import torch
|
|
|
|
from sglang.srt.layers.moe import MoeRunner, MoeRunnerBackend, get_moe_runner_backend
|
|
from sglang.srt.layers.moe.moe_runner.marlin import MarlinMoeQuantInfo
|
|
from sglang.srt.layers.parameter import BasevLLMParameter, permute_param_layout_
|
|
from sglang.srt.layers.quantization.marlin_utils import (
|
|
apply_gptq_marlin_linear,
|
|
check_marlin_supports_shape,
|
|
marlin_is_k_full,
|
|
marlin_make_empty_g_idx,
|
|
marlin_make_workspace,
|
|
marlin_moe_permute_scales,
|
|
marlin_permute_scales,
|
|
marlin_sort_g_idx,
|
|
marlin_zero_points,
|
|
)
|
|
from sglang.srt.layers.quantization.utils import (
|
|
get_scalar_types,
|
|
replace_parameter,
|
|
unpack_cols,
|
|
)
|
|
|
|
if TYPE_CHECKING:
|
|
from sglang.srt.layers.moe import MoeRunnerConfig
|
|
from sglang.srt.layers.moe.token_dispatcher import (
|
|
CombineInput,
|
|
StandardDispatchOutput,
|
|
)
|
|
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
|
|
|
ScalarType, _ = get_scalar_types()
|
|
|
|
|
|
def _unsupported_kernel(*args, **kwargs):
|
|
raise RuntimeError("GPTQ CUDA kernels are unavailable on the current platform.")
|
|
|
|
|
|
gptq_gemm = _unsupported_kernel
|
|
gptq_marlin_repack = _unsupported_kernel
|
|
gptq_shuffle = _unsupported_kernel
|
|
|
|
try:
|
|
from sgl_kernel import gptq_gemm, gptq_shuffle
|
|
|
|
from sglang.jit_kernel.gptq_marlin_repack import gptq_marlin_repack
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
@dataclass
|
|
class MarlinLinearLayerConfig:
|
|
full_weight_shape: tuple[int, int] # [in, out]
|
|
partition_weight_shape: tuple[int, int]
|
|
weight_type: ScalarType
|
|
act_type: torch.dtype
|
|
group_size: int
|
|
zero_points: bool
|
|
has_g_idx: bool
|
|
|
|
|
|
def gptq_marlin_moe_repack(
|
|
b_q_weight: torch.Tensor,
|
|
perm: torch.Tensor,
|
|
size_k: int,
|
|
size_n: int,
|
|
num_bits: int,
|
|
) -> torch.Tensor:
|
|
num_experts = b_q_weight.shape[0]
|
|
assert size_k % 16 == 0
|
|
output = torch.empty(
|
|
(num_experts, size_k // 16, size_n * (num_bits // 2)),
|
|
device=b_q_weight.device,
|
|
dtype=b_q_weight.dtype,
|
|
)
|
|
for e in range(num_experts):
|
|
output[e] = gptq_marlin_repack(b_q_weight[e], perm[e], size_k, size_n, num_bits)
|
|
return output
|
|
|
|
|
|
class GPTQLinearKernel:
|
|
def __init__(self, quant_config: Optional[QuantizationConfig] = None):
|
|
self.quant_config = quant_config
|
|
self.use_shuffle = True
|
|
|
|
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
|
# for torch.compile
|
|
layer.qzeros = torch.nn.Parameter(layer.qzeros.data, requires_grad=False)
|
|
layer.qweight = torch.nn.Parameter(layer.qweight.data, requires_grad=False)
|
|
layer.g_idx = torch.nn.Parameter(layer.g_idx.data, requires_grad=False)
|
|
layer.scales = torch.nn.Parameter(layer.scales.data, requires_grad=False)
|
|
|
|
# exllama needs to shuffle the weight after the weight is loaded
|
|
# here we do the shuffle on first forward pass
|
|
if self.use_shuffle:
|
|
if self.quant_config.desc_act:
|
|
layer.g_idx.data = torch.argsort(layer.g_idx).to(torch.int)
|
|
else:
|
|
layer.g_idx.data = torch.empty(
|
|
(0,), dtype=torch.int, device=layer.g_idx.device
|
|
)
|
|
gptq_shuffle(layer.qweight, layer.g_idx, self.quant_config.weight_bits)
|
|
|
|
def apply(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
x: torch.Tensor,
|
|
bias: Optional[torch.Tensor] = None,
|
|
) -> torch.Tensor:
|
|
out_shape = x.shape[:-1] + (layer.qweight.shape[-1],)
|
|
reshaped_x = x.reshape(-1, x.shape[-1])
|
|
|
|
output = gptq_gemm(
|
|
reshaped_x,
|
|
layer.qweight,
|
|
layer.qzeros,
|
|
layer.scales,
|
|
layer.g_idx,
|
|
self.use_shuffle,
|
|
self.quant_config.weight_bits,
|
|
)
|
|
if bias is not None:
|
|
output.add_(bias)
|
|
return output.reshape(out_shape)
|
|
|
|
|
|
class GPTQMarlinLinearKernel:
|
|
def __init__(self, quant_config: Optional[QuantizationConfig] = None):
|
|
self.quant_config = quant_config
|
|
|
|
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
|
device = getattr(layer, "qweight").device
|
|
c = self.kernel_config
|
|
|
|
check_marlin_supports_shape(
|
|
c.partition_weight_shape[1], # out_features
|
|
c.partition_weight_shape[0], # in_features
|
|
c.full_weight_shape[0], # in_features
|
|
c.group_size,
|
|
)
|
|
|
|
row_parallel = c.partition_weight_shape[0] != c.full_weight_shape[0]
|
|
self.is_k_full = marlin_is_k_full(c.has_g_idx, row_parallel)
|
|
|
|
# Allocate marlin workspace.
|
|
self.workspace = marlin_make_workspace(device)
|
|
|
|
# Default names since marlin requires empty parameters for these,
|
|
# TODO: remove this requirement from marlin (allow optional tensors)
|
|
self.w_q_name = "qweight"
|
|
self.w_s_name = "scales"
|
|
self.w_zp_name = "qzeros"
|
|
self.w_gidx_name = "g_idx"
|
|
|
|
def _transform_param(
|
|
layer: torch.nn.Module, name: Optional[str], fn: Callable
|
|
) -> None:
|
|
if name is not None and getattr(layer, name, None) is not None:
|
|
|
|
old_param = getattr(layer, name)
|
|
new_param = fn(old_param)
|
|
# replace the parameter with torch.nn.Parameter for TorchDynamo
|
|
# compatibility
|
|
replace_parameter(
|
|
layer, name, torch.nn.Parameter(new_param.data, requires_grad=False)
|
|
)
|
|
|
|
def transform_w_q(x):
|
|
assert isinstance(x, BasevLLMParameter)
|
|
permute_param_layout_(x, input_dim=0, output_dim=1, packed_dim=0)
|
|
x.data = gptq_marlin_repack(
|
|
x.data.contiguous(),
|
|
perm=layer.g_idx_sort_indices,
|
|
size_k=c.partition_weight_shape[0],
|
|
size_n=c.partition_weight_shape[1],
|
|
num_bits=c.weight_type.size_bits,
|
|
)
|
|
return x
|
|
|
|
def transform_w_s(x):
|
|
assert isinstance(x, BasevLLMParameter)
|
|
permute_param_layout_(x, input_dim=0, output_dim=1)
|
|
x.data = marlin_permute_scales(
|
|
x.data.contiguous(),
|
|
size_k=c.partition_weight_shape[0],
|
|
size_n=c.partition_weight_shape[1],
|
|
group_size=c.group_size,
|
|
)
|
|
return x
|
|
|
|
if c.has_g_idx:
|
|
g_idx, g_idx_sort_indices = marlin_sort_g_idx(
|
|
getattr(layer, self.w_gidx_name)
|
|
)
|
|
_transform_param(layer, self.w_gidx_name, lambda _: g_idx)
|
|
layer.g_idx_sort_indices = g_idx_sort_indices
|
|
else:
|
|
setattr(layer, self.w_gidx_name, marlin_make_empty_g_idx(device))
|
|
layer.g_idx_sort_indices = marlin_make_empty_g_idx(device)
|
|
|
|
if c.zero_points:
|
|
grouped_k = (
|
|
c.partition_weight_shape[0] // c.group_size if c.group_size != -1 else 1
|
|
)
|
|
_transform_param(
|
|
layer,
|
|
self.w_zp_name,
|
|
lambda x: marlin_zero_points(
|
|
unpack_cols(
|
|
x.t(),
|
|
c.weight_type.size_bits,
|
|
grouped_k,
|
|
c.partition_weight_shape[1],
|
|
),
|
|
size_k=grouped_k,
|
|
size_n=c.partition_weight_shape[1],
|
|
num_bits=c.weight_type.size_bits,
|
|
),
|
|
)
|
|
else:
|
|
setattr(layer, self.w_zp_name, marlin_make_empty_g_idx(device))
|
|
_transform_param(layer, self.w_q_name, transform_w_q)
|
|
_transform_param(layer, self.w_s_name, transform_w_s)
|
|
|
|
def apply(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
x: torch.Tensor,
|
|
bias: Optional[torch.Tensor] = None,
|
|
) -> torch.Tensor:
|
|
c = self.kernel_config
|
|
|
|
def _get_weight_params(
|
|
layer: torch.nn.Module,
|
|
) -> tuple[
|
|
torch.Tensor, # w_q
|
|
torch.Tensor, # w_s
|
|
Optional[torch.Tensor], # w_zp,
|
|
Optional[torch.Tensor], # w_gidx
|
|
]:
|
|
return (
|
|
getattr(layer, self.w_q_name),
|
|
getattr(layer, self.w_s_name),
|
|
getattr(layer, self.w_zp_name or "", None),
|
|
getattr(layer, self.w_gidx_name or "", None),
|
|
)
|
|
|
|
w_q, w_s, w_zp, w_gidx = _get_weight_params(layer)
|
|
|
|
# `process_weights_after_loading` will ensure w_zp and w_gidx are not
|
|
# None for marlin
|
|
return apply_gptq_marlin_linear(
|
|
input=x,
|
|
weight=w_q,
|
|
weight_scale=w_s,
|
|
weight_zp=w_zp, # type: ignore
|
|
g_idx=w_gidx, # type: ignore
|
|
g_idx_sort_indices=layer.g_idx_sort_indices,
|
|
workspace=self.workspace,
|
|
wtype=c.weight_type,
|
|
input_size_per_partition=c.partition_weight_shape[0],
|
|
output_size_per_partition=c.partition_weight_shape[1],
|
|
is_k_full=self.is_k_full,
|
|
bias=bias,
|
|
)
|
|
|
|
|
|
class GPTQMarlinMoEKernel:
|
|
def __init__(self, quant_config: Optional[QuantizationConfig] = None):
|
|
self.quant_config = quant_config
|
|
|
|
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
|
|
|
# Process act_order
|
|
if self.quant_config.desc_act:
|
|
# Get sorting based on g_idx
|
|
num_experts = layer.w13_g_idx.shape[0]
|
|
w13_g_idx_sort_indices = torch.empty_like(layer.w13_g_idx)
|
|
w2_g_idx_sort_indices = torch.empty_like(layer.w2_g_idx)
|
|
w13_sorted_g_idx = torch.empty_like(layer.w13_g_idx)
|
|
w2_sorted_g_idx = torch.empty_like(layer.w2_g_idx)
|
|
for e in range(num_experts):
|
|
w13_g_idx_sort_indices[e] = torch.argsort(layer.w13_g_idx[e]).to(
|
|
torch.int32
|
|
)
|
|
w2_g_idx_sort_indices[e] = torch.argsort(layer.w2_g_idx[e]).to(
|
|
torch.int32
|
|
)
|
|
w13_sorted_g_idx[e] = layer.w13_g_idx[e][w13_g_idx_sort_indices[e]]
|
|
w2_sorted_g_idx[e] = layer.w2_g_idx[e][w2_g_idx_sort_indices[e]]
|
|
replace_parameter(layer, "w13_g_idx", w13_sorted_g_idx)
|
|
replace_parameter(layer, "w2_g_idx", w2_sorted_g_idx)
|
|
replace_parameter(layer, "w13_g_idx_sort_indices", w13_g_idx_sort_indices)
|
|
replace_parameter(layer, "w2_g_idx_sort_indices", w2_g_idx_sort_indices)
|
|
else:
|
|
# Reset g_idx related tensors
|
|
num_experts = layer.w13_g_idx.shape[0]
|
|
device = layer.w13_g_idx.device
|
|
layer.w13_g_idx = torch.nn.Parameter(
|
|
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
|
requires_grad=False,
|
|
)
|
|
layer.w2_g_idx = torch.nn.Parameter(
|
|
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
|
requires_grad=False,
|
|
)
|
|
layer.w13_g_idx_sort_indices = torch.nn.Parameter(
|
|
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
|
requires_grad=False,
|
|
)
|
|
layer.w2_g_idx_sort_indices = torch.nn.Parameter(
|
|
torch.empty((num_experts, 0), dtype=torch.int32, device=device),
|
|
requires_grad=False,
|
|
)
|
|
# Repack weights
|
|
marlin_w13_qweight = gptq_marlin_moe_repack(
|
|
layer.w13_qweight,
|
|
layer.w13_g_idx_sort_indices,
|
|
layer.w13_qweight.shape[1] * self.quant_config.pack_factor,
|
|
layer.w13_qweight.shape[2],
|
|
self.quant_config.weight_bits,
|
|
)
|
|
replace_parameter(layer, "w13_qweight", marlin_w13_qweight)
|
|
marlin_w2_qweight = gptq_marlin_moe_repack(
|
|
layer.w2_qweight,
|
|
layer.w2_g_idx_sort_indices,
|
|
layer.w2_qweight.shape[1] * self.quant_config.pack_factor,
|
|
layer.w2_qweight.shape[2],
|
|
self.quant_config.weight_bits,
|
|
)
|
|
replace_parameter(layer, "w2_qweight", marlin_w2_qweight)
|
|
# Repack scales
|
|
marlin_w13_scales = marlin_moe_permute_scales(
|
|
s=layer.w13_scales,
|
|
size_k=layer.intermediate_size_per_partition,
|
|
size_n=layer.w13_scales.shape[2],
|
|
group_size=self.quant_config.group_size,
|
|
)
|
|
replace_parameter(layer, "w13_scales", marlin_w13_scales)
|
|
marlin_w2_scales = marlin_moe_permute_scales(
|
|
s=layer.w2_scales,
|
|
size_k=layer.w2_scales.shape[1]
|
|
* (
|
|
self.quant_config.group_size
|
|
if self.quant_config.group_size != -1
|
|
else self.quant_config.pack_factor
|
|
),
|
|
size_n=layer.w2_scales.shape[2],
|
|
group_size=self.quant_config.group_size,
|
|
)
|
|
replace_parameter(layer, "w2_scales", marlin_w2_scales)
|
|
|
|
def create_moe_runner(
|
|
self, layer: torch.nn.Module, moe_runner_config: MoeRunnerConfig
|
|
):
|
|
assert get_moe_runner_backend().is_auto()
|
|
self.moe_runner_config = moe_runner_config
|
|
self.runner = MoeRunner(MoeRunnerBackend.MARLIN, moe_runner_config)
|
|
|
|
def apply(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
dispatch_output: StandardDispatchOutput,
|
|
) -> CombineInput:
|
|
quant_info = MarlinMoeQuantInfo(
|
|
w13_qweight=layer.w13_qweight,
|
|
w2_qweight=layer.w2_qweight,
|
|
w13_scales=layer.w13_scales,
|
|
w2_scales=layer.w2_scales,
|
|
w13_g_idx=layer.w13_g_idx,
|
|
w2_g_idx=layer.w2_g_idx,
|
|
w13_g_idx_sort_indices=layer.w13_g_idx_sort_indices,
|
|
w2_g_idx_sort_indices=layer.w2_g_idx_sort_indices,
|
|
weight_bits=self.quant_config.weight_bits,
|
|
is_k_full=self.is_k_full,
|
|
)
|
|
|
|
return self.runner.run(dispatch_output, quant_info)
|