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
559 lines
19 KiB
Python
559 lines
19 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""Cutlass W4A8 MoE kernel."""
|
|
|
|
from typing import Optional
|
|
|
|
import torch
|
|
|
|
from sglang.srt.runtime_context import get_parallel
|
|
from sglang.srt.utils import is_cuda, is_cuda_alike
|
|
|
|
_is_cuda = is_cuda()
|
|
_is_cuda_alike = is_cuda_alike()
|
|
|
|
if _is_cuda_alike:
|
|
from sgl_kernel import (
|
|
cutlass_w4a8_moe_mm,
|
|
get_cutlass_w4a8_moe_mm_data,
|
|
)
|
|
|
|
if _is_cuda:
|
|
from sglang.jit_kernel.activation import silu_and_mul
|
|
else:
|
|
from sgl_kernel import silu_and_mul
|
|
|
|
from sglang.jit_kernel.per_tensor_quant_fp8 import per_tensor_quant_fp8
|
|
from sglang.srt.layers.moe.ep_moe.kernels import (
|
|
cutlass_w4_run_moe_ep_preproess,
|
|
deepep_ll_get_cutlass_w4a8_moe_mm_data,
|
|
deepep_permute_triton_kernel,
|
|
deepep_post_reorder_triton_kernel,
|
|
deepep_run_moe_deep_preprocess,
|
|
fp8_per_token_to_per_tensor_quant_triton,
|
|
post_reorder_for_cutlass_moe,
|
|
pre_reorder_for_cutlass_moe,
|
|
silu_and_mul_masked_post_per_tensor_quant_fwd,
|
|
silu_mul_static_tensorwise_quant_for_cutlass_moe,
|
|
)
|
|
|
|
|
|
def cutlass_w4a8_moe(
|
|
a: torch.Tensor,
|
|
w1_q: torch.Tensor,
|
|
w2_q: torch.Tensor,
|
|
w1_scale: torch.Tensor,
|
|
w2_scale: torch.Tensor,
|
|
topk_weights: torch.Tensor,
|
|
topk_ids: torch.Tensor,
|
|
a_strides1: torch.Tensor,
|
|
b_strides1: torch.Tensor,
|
|
c_strides1: torch.Tensor,
|
|
a_strides2: torch.Tensor,
|
|
b_strides2: torch.Tensor,
|
|
c_strides2: torch.Tensor,
|
|
s_strides13: torch.Tensor,
|
|
s_strides2: torch.Tensor,
|
|
expert_offsets: torch.Tensor,
|
|
problem_sizes1: torch.Tensor,
|
|
problem_sizes2: torch.Tensor,
|
|
a1_scale: Optional[torch.Tensor] = None,
|
|
a2_scale: Optional[torch.Tensor] = None,
|
|
apply_router_weight_on_input: bool = False,
|
|
routed_scaling_factor: float = 1.0,
|
|
) -> torch.Tensor:
|
|
"""
|
|
This function computes a w4a8-quantized Mixture of Experts (MoE) layer
|
|
using two sets of quantized weights, w1_q and w2_q, and top-k gating
|
|
mechanism. The matrix multiplications are implemented with CUTLASS
|
|
grouped gemm.
|
|
|
|
Parameters:
|
|
- a (torch.Tensor): The input tensor to the MoE layer.
|
|
Shape: [M, K]
|
|
- w1_q (torch.Tensor): The first set of int4-quantized expert weights.
|
|
Shape: [num_experts, N * 2, K // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w2_q (torch.Tensor): The second set of int4-quantized expert weights.
|
|
Shape: [num_experts, K, N // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w1_scale (torch.Tensor): The fp32 scale to dequantize w1_q.
|
|
Shape: [num_experts, K // 512, N * 8]
|
|
- w2_scale (torch.Tensor): The fp32 scale to dequantize w2_q.
|
|
Shape: [num_experts, N // 512, K * 4]
|
|
- topk_weights (torch.Tensor): The weights of each token->expert mapping.
|
|
- topk_ids (torch.Tensor): The ids of each token->expert mapping.
|
|
- a_strides1 (torch.Tensor): The input strides of the first grouped gemm.
|
|
- b_strides1 (torch.Tensor): The weights strides of the first grouped gemm.
|
|
- c_strides1 (torch.Tensor): The output strides of the first grouped gemm.
|
|
- a_strides2 (torch.Tensor): The input strides of the second grouped gemm.
|
|
- b_strides2 (torch.Tensor): The weights strides of the second grouped gemm.
|
|
- c_strides2 (torch.Tensor): The output strides of the second grouped gemm.
|
|
- s_strides13 (torch.Tensor): The input and scale strides of the first grouped gemm.
|
|
- s_strides2 (torch.Tensor): The scale strides of the second grouped gemm.
|
|
- a1_scale (Optional[torch.Tensor]): The optional fp32 scale to quantize a.
|
|
Shape: scalar or [1, K]
|
|
- a2_scale (Optional[torch.Tensor]): The optional fp32 scale to
|
|
quantize the intermediate result between the gemms.
|
|
Shape: scalar or [1, N]
|
|
- apply_router_weight_on_input (bool): When true, the topk weights are
|
|
applied directly on the inputs. This is only applicable when topk is 1.
|
|
|
|
Returns:
|
|
- torch.Tensor: The fp8 output tensor after applying the MoE layer.
|
|
"""
|
|
assert topk_weights.shape == topk_ids.shape, "topk shape mismatch"
|
|
assert w1_q.dtype == torch.int8
|
|
assert w2_q.dtype == torch.int8
|
|
assert a.shape[1] // 2 == w1_q.shape[2], "Hidden size mismatch w1"
|
|
assert w1_q.shape[2] * 2 == w2_q.shape[1], "Hidden size mismatch w2"
|
|
assert w1_q.shape[0] == w2_q.shape[0], "Expert number mismatch"
|
|
assert w1_q.shape[0] == w1_scale.shape[0], "w1 scales expert number mismatch"
|
|
assert w1_q.shape[0] == w2_scale.shape[0], "w2 scales expert number mismatch"
|
|
|
|
assert a_strides1.shape[0] == w1_q.shape[0], "A Strides 1 expert number mismatch"
|
|
assert b_strides1.shape[0] == w1_q.shape[0], "B Strides 1 expert number mismatch"
|
|
assert a_strides2.shape[0] == w2_q.shape[0], "A Strides 2 expert number mismatch"
|
|
assert b_strides2.shape[0] == w2_q.shape[0], "B Strides 2 expert number mismatch"
|
|
num_local_experts = w1_q.size(0)
|
|
m = a.size(0)
|
|
k = w1_q.size(2) * 2 # w1_q is transposed and packed
|
|
n = w2_q.size(2) * 2 # w2_q is transposed and packed
|
|
topk = topk_ids.size(1)
|
|
|
|
if apply_router_weight_on_input:
|
|
assert topk == 1, "apply_router_weight_on_input is only implemented for topk=1"
|
|
|
|
device = a.device
|
|
if get_parallel().moe_ep_size > 1:
|
|
topk_ids = torch.where(topk_ids == -1, num_local_experts, topk_ids)
|
|
|
|
src2dst = cutlass_w4_run_moe_ep_preproess(
|
|
topk_ids,
|
|
)
|
|
|
|
gateup_input = torch.empty(
|
|
(m * topk, k),
|
|
device=device,
|
|
dtype=torch.float8_e4m3fn,
|
|
)
|
|
|
|
pre_reorder_for_cutlass_moe(
|
|
a,
|
|
gateup_input,
|
|
src2dst,
|
|
topk_ids,
|
|
a1_scale,
|
|
num_local_experts,
|
|
topk,
|
|
m,
|
|
k,
|
|
)
|
|
|
|
# NOTE: a_map and c_map are not used in the get_cutlass_w4a8_moe_mm_data kernel,
|
|
# they are kept to allow for a quick switch of the permutation logic
|
|
# from the current triton kernel implementation to the cutlass-based one if needed.
|
|
a_map = torch.empty((topk_ids.numel()), dtype=torch.int32, device=device)
|
|
c_map = torch.empty((topk_ids.numel()), dtype=torch.int32, device=device)
|
|
get_cutlass_w4a8_moe_mm_data(
|
|
topk_ids,
|
|
expert_offsets,
|
|
problem_sizes1,
|
|
problem_sizes2,
|
|
a_map,
|
|
c_map,
|
|
num_local_experts,
|
|
n,
|
|
k,
|
|
)
|
|
|
|
c1 = torch.empty((m * topk, n * 2), device=device, dtype=torch.bfloat16)
|
|
c2 = torch.empty((m * topk, k), device=device, dtype=torch.bfloat16)
|
|
|
|
cutlass_w4a8_moe_mm(
|
|
c1,
|
|
gateup_input,
|
|
w1_q,
|
|
a1_scale.float(),
|
|
w1_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes1,
|
|
a_strides1,
|
|
b_strides1,
|
|
c_strides1,
|
|
s_strides13,
|
|
128,
|
|
topk,
|
|
)
|
|
|
|
intermediate_q = torch.empty(
|
|
(m * topk, n), dtype=torch.float8_e4m3fn, device=device
|
|
)
|
|
silu_mul_static_tensorwise_quant_for_cutlass_moe(
|
|
c1, intermediate_q, a2_scale.float(), expert_offsets[-1:], m * topk, n
|
|
)
|
|
|
|
cutlass_w4a8_moe_mm(
|
|
c2,
|
|
intermediate_q,
|
|
w2_q,
|
|
a2_scale.float(),
|
|
w2_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes2,
|
|
a_strides2,
|
|
b_strides2,
|
|
c_strides2,
|
|
s_strides2,
|
|
128,
|
|
topk,
|
|
)
|
|
|
|
output = torch.empty_like(a)
|
|
|
|
post_reorder_for_cutlass_moe(
|
|
c2,
|
|
output,
|
|
src2dst,
|
|
topk_ids,
|
|
topk_weights,
|
|
num_local_experts,
|
|
topk,
|
|
m,
|
|
k,
|
|
routed_scaling_factor,
|
|
)
|
|
return output
|
|
|
|
|
|
def cutlass_w4a8_moe_deepep_normal(
|
|
a: torch.Tensor,
|
|
w1_q: torch.Tensor,
|
|
w2_q: torch.Tensor,
|
|
w1_scale: torch.Tensor,
|
|
w2_scale: torch.Tensor,
|
|
topk_weights: torch.Tensor,
|
|
topk_ids_: torch.Tensor,
|
|
a_strides1: torch.Tensor,
|
|
b_strides1: torch.Tensor,
|
|
c_strides1: torch.Tensor,
|
|
a_strides2: torch.Tensor,
|
|
b_strides2: torch.Tensor,
|
|
c_strides2: torch.Tensor,
|
|
s_strides13: torch.Tensor,
|
|
s_strides2: torch.Tensor,
|
|
expert_offsets: torch.Tensor,
|
|
problem_sizes1: torch.Tensor,
|
|
problem_sizes2: torch.Tensor,
|
|
a1_scale: Optional[torch.Tensor] = None,
|
|
a2_scale: Optional[torch.Tensor] = None,
|
|
) -> torch.Tensor:
|
|
"""
|
|
This function computes a w4a8-quantized Mixture of Experts (MoE) layer
|
|
using two sets of quantized weights, w1_q and w2_q, and top-k gating
|
|
mechanism. The matrix multiplications are implemented with CUTLASS
|
|
grouped gemm.
|
|
|
|
Parameters:
|
|
- a (torch.Tensor): The input tensor to the MoE layer.
|
|
Shape: [M, K]
|
|
- w1_q (torch.Tensor): The first set of int4-quantized expert weights.
|
|
Shape: [num_experts, N * 2, K // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w2_q (torch.Tensor): The second set of int4-quantized expert weights.
|
|
Shape: [num_experts, K, N // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w1_scale (torch.Tensor): The fp32 scale to dequantize w1_q.
|
|
Shape: [num_experts, K // 512, N * 8]
|
|
- w2_scale (torch.Tensor): The fp32 scale to dequantize w2_q.
|
|
Shape: [num_experts, N // 512, K * 4]
|
|
- topk_weights (torch.Tensor): The weights of each token->expert mapping.
|
|
- a_strides1 (torch.Tensor): The input strides of the first grouped gemm.
|
|
- b_strides1 (torch.Tensor): The weights strides of the first grouped gemm.
|
|
- c_strides1 (torch.Tensor): The output strides of the first grouped gemm.
|
|
- a_strides2 (torch.Tensor): The input strides of the second grouped gemm.
|
|
- b_strides2 (torch.Tensor): The weights strides of the second grouped gemm.
|
|
- c_strides2 (torch.Tensor): The output strides of the second grouped gemm.
|
|
- s_strides13 (torch.Tensor): The input and scale strides of the first grouped gemm.
|
|
- s_strides2 (torch.Tensor): The scale strides of the second grouped gemm.
|
|
- a1_scale (Optional[torch.Tensor]): The optional fp32 scale to quantize a.
|
|
Shape: scalar or [1, K]
|
|
- a2_scale (Optional[torch.Tensor]): The optional fp32 scale to
|
|
quantize the intermediate result between the gemms.
|
|
Shape: scalar or [1, N]
|
|
- apply_router_weight_on_input (bool): When true, the topk weights are
|
|
applied directly on the inputs. This is only applicable when topk is 1.
|
|
|
|
Returns:
|
|
- torch.Tensor: The fp8 output tensor after applying the MoE layer.
|
|
"""
|
|
assert topk_weights.shape == topk_ids_.shape, "topk shape mismatch"
|
|
assert w1_q.dtype == torch.int8
|
|
assert w2_q.dtype == torch.int8
|
|
assert a.shape[1] // 2 == w1_q.shape[2], "Hidden size mismatch w1"
|
|
assert w1_q.shape[2] * 2 == w2_q.shape[1], "Hidden size mismatch w2"
|
|
assert w1_q.shape[0] == w2_q.shape[0], "Expert number mismatch"
|
|
assert w1_q.shape[0] == w1_scale.shape[0], "w1 scales expert number mismatch"
|
|
assert w1_q.shape[0] == w2_scale.shape[0], "w2 scales expert number mismatch"
|
|
|
|
assert a_strides1.shape[0] == w1_q.shape[0], "A Strides 1 expert number mismatch"
|
|
assert b_strides1.shape[0] == w1_q.shape[0], "B Strides 1 expert number mismatch"
|
|
assert a_strides2.shape[0] == w2_q.shape[0], "A Strides 2 expert number mismatch"
|
|
assert b_strides2.shape[0] == w2_q.shape[0], "B Strides 2 expert number mismatch"
|
|
num_experts = w1_q.size(0)
|
|
m = a.size(0)
|
|
k = w1_q.size(2) * 2 # w1_q is transposed and packed
|
|
n = w2_q.size(2) * 2 # w2_q is transposed and packed
|
|
topk = topk_ids_.size(1)
|
|
|
|
num_experts = w1_q.size(0)
|
|
m = a.size(0)
|
|
k = w1_q.size(2) * 2
|
|
n = w2_q.size(2) * 2
|
|
topk = topk_ids_.size(1)
|
|
device = a.device
|
|
|
|
reorder_topk_ids, src2dst, _ = deepep_run_moe_deep_preprocess(
|
|
topk_ids_, num_experts
|
|
)
|
|
num_total_tokens = reorder_topk_ids.numel()
|
|
gateup_input_pre_reorder = torch.empty(
|
|
(int(num_total_tokens), a.shape[1]),
|
|
device=device,
|
|
dtype=a.dtype,
|
|
)
|
|
deepep_permute_triton_kernel[(a.shape[0],)](
|
|
a,
|
|
gateup_input_pre_reorder,
|
|
src2dst,
|
|
topk_ids_.to(torch.int64),
|
|
None,
|
|
topk,
|
|
a.shape[1],
|
|
BLOCK_SIZE=512,
|
|
)
|
|
gateup_input = torch.empty(
|
|
gateup_input_pre_reorder.shape, dtype=torch.float8_e4m3fn, device=device
|
|
)
|
|
per_tensor_quant_fp8(gateup_input_pre_reorder, gateup_input, a1_scale.float(), True)
|
|
del gateup_input_pre_reorder
|
|
local_topk_ids = topk_ids_
|
|
local_topk_ids = (
|
|
torch.where(local_topk_ids == -1, num_experts, topk_ids_).to(torch.int32)
|
|
).contiguous()
|
|
|
|
a_map = torch.empty((local_topk_ids.numel()), dtype=torch.int32, device=device)
|
|
c_map = torch.empty((local_topk_ids.numel()), dtype=torch.int32, device=device)
|
|
get_cutlass_w4a8_moe_mm_data(
|
|
local_topk_ids,
|
|
expert_offsets,
|
|
problem_sizes1,
|
|
problem_sizes2,
|
|
a_map,
|
|
c_map,
|
|
num_experts,
|
|
n,
|
|
k,
|
|
)
|
|
c1 = torch.empty((m * topk, n * 2), device=device, dtype=torch.bfloat16)
|
|
c2 = torch.zeros((m * topk, k), device=device, dtype=torch.bfloat16)
|
|
|
|
cutlass_w4a8_moe_mm(
|
|
c1,
|
|
gateup_input,
|
|
w1_q,
|
|
a1_scale.float(),
|
|
w1_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes1,
|
|
a_strides1,
|
|
b_strides1,
|
|
c_strides1,
|
|
s_strides13,
|
|
128,
|
|
topk,
|
|
)
|
|
intermediate = torch.empty((m * topk, n), device=device, dtype=torch.bfloat16)
|
|
silu_and_mul(c1, intermediate)
|
|
|
|
intermediate_q = torch.empty(
|
|
intermediate.shape, dtype=torch.float8_e4m3fn, device=device
|
|
)
|
|
per_tensor_quant_fp8(intermediate, intermediate_q, a2_scale.float(), True)
|
|
|
|
cutlass_w4a8_moe_mm(
|
|
c2,
|
|
intermediate_q,
|
|
w2_q,
|
|
a2_scale.float(),
|
|
w2_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes2,
|
|
a_strides2,
|
|
b_strides2,
|
|
c_strides2,
|
|
s_strides2,
|
|
128,
|
|
topk,
|
|
)
|
|
num_tokens = src2dst.shape[0] // topk
|
|
output = torch.empty(
|
|
(num_tokens, c2.shape[1]),
|
|
device=c2.device,
|
|
dtype=torch.bfloat16,
|
|
)
|
|
deepep_post_reorder_triton_kernel[(num_tokens,)](
|
|
c2,
|
|
output,
|
|
src2dst,
|
|
topk_ids_,
|
|
topk_weights,
|
|
topk,
|
|
c2.shape[1],
|
|
BLOCK_SIZE=512,
|
|
)
|
|
|
|
return output
|
|
|
|
|
|
def cutlass_w4a8_moe_deepep_ll(
|
|
a_states: torch.Tensor,
|
|
a_scales: torch.Tensor,
|
|
w1_q: torch.Tensor,
|
|
w2_q: torch.Tensor,
|
|
w1_scale: torch.Tensor,
|
|
w2_scale: torch.Tensor,
|
|
topk_ids_: torch.Tensor,
|
|
masked_m: torch.Tensor,
|
|
a_strides1: torch.Tensor,
|
|
b_strides1: torch.Tensor,
|
|
c_strides1: torch.Tensor,
|
|
a_strides2: torch.Tensor,
|
|
b_strides2: torch.Tensor,
|
|
c_strides2: torch.Tensor,
|
|
s_strides13: torch.Tensor,
|
|
s_strides2: torch.Tensor,
|
|
expert_offsets: torch.Tensor,
|
|
problem_sizes1: torch.Tensor,
|
|
problem_sizes2: torch.Tensor,
|
|
a1_scale: Optional[torch.Tensor] = None,
|
|
a2_scale: Optional[torch.Tensor] = None,
|
|
) -> torch.Tensor:
|
|
"""
|
|
This function computes a w4a8-quantized Mixture of Experts (MoE) layer
|
|
using two sets of quantized weights, w1_q and w2_q, and top-k gating
|
|
mechanism. The matrix multiplications are implemented with CUTLASS
|
|
grouped gemm.
|
|
|
|
Parameters:
|
|
- a (torch.Tensor): The input tensor to the MoE layer.
|
|
Shape: [num_local_experts, num_max_dispatch_tokens_per_rank * num_ranks, K]
|
|
- w1_q (torch.Tensor): The first set of int4-quantized expert weights.
|
|
Shape: [num_experts, N * 2, K // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w2_q (torch.Tensor): The second set of int4-quantized expert weights.
|
|
Shape: [num_experts, K, N // 2]
|
|
(the weights are passed transposed and int4-packed)
|
|
- w1_scale (torch.Tensor): The fp32 scale to dequantize w1_q.
|
|
Shape: [num_experts, K // 512, N * 8]
|
|
- w2_scale (torch.Tensor): The fp32 scale to dequantize w2_q.
|
|
Shape: [num_experts, N // 512, K * 4]
|
|
- topk_weights (torch.Tensor): The weights of each token->expert mapping.
|
|
- a_strides1 (torch.Tensor): The input strides of the first grouped gemm.
|
|
- b_strides1 (torch.Tensor): The weights strides of the first grouped gemm.
|
|
- c_strides1 (torch.Tensor): The output strides of the first grouped gemm.
|
|
- a_strides2 (torch.Tensor): The input strides of the second grouped gemm.
|
|
- b_strides2 (torch.Tensor): The weights strides of the second grouped gemm.
|
|
- c_strides2 (torch.Tensor): The output strides of the second grouped gemm.
|
|
- s_strides13 (torch.Tensor): The input and scale strides of the first grouped gemm.
|
|
- s_strides2 (torch.Tensor): The scale strides of the second grouped gemm.
|
|
- a1_scale (Optional[torch.Tensor]): The optional fp32 scale to quantize a.
|
|
Shape: scalar or [1, K]
|
|
- a2_scale (Optional[torch.Tensor]): The optional fp32 scale to
|
|
quantize the intermediate result between the gemms.
|
|
Shape: scalar or [1, N]
|
|
- apply_router_weight_on_input (bool): When true, the topk weights are
|
|
applied directly on the inputs. This is only applicable when topk is 1.
|
|
|
|
Returns:
|
|
- torch.Tensor: The fp8 output tensor after applying the MoE layer.
|
|
"""
|
|
assert w1_q.dtype == torch.int8
|
|
assert w2_q.dtype == torch.int8
|
|
assert a_states.shape[2] // 2 == w1_q.shape[2], "Hidden size mismatch w1"
|
|
assert w1_q.shape[2] * 2 == w2_q.shape[1], "Hidden size mismatch w2"
|
|
assert w1_q.shape[0] == w2_q.shape[0], "Expert number mismatch"
|
|
assert w1_q.shape[0] == w1_scale.shape[0], "w1 scales expert number mismatch"
|
|
assert w1_q.shape[0] == w2_scale.shape[0], "w2 scales expert number mismatch"
|
|
|
|
assert a_strides1.shape[0] == w1_q.shape[0], "A Strides 1 expert number mismatch"
|
|
assert b_strides1.shape[0] == w1_q.shape[0], "B Strides 1 expert number mismatch"
|
|
assert a_strides2.shape[0] == w2_q.shape[0], "A Strides 2 expert number mismatch"
|
|
assert b_strides2.shape[0] == w2_q.shape[0], "B Strides 2 expert number mismatch"
|
|
num_experts = w1_q.size(0)
|
|
m = a_states.size(1)
|
|
k = w1_q.size(2) * 2 # w1_q is transposed and packed
|
|
n = w2_q.size(2) * 2 # w2_q is transposed and packed
|
|
topk = topk_ids_.size(1)
|
|
|
|
device = a_states.device
|
|
|
|
problem_sizes1, problem_sizes2 = deepep_ll_get_cutlass_w4a8_moe_mm_data(
|
|
masked_m,
|
|
problem_sizes1,
|
|
problem_sizes2,
|
|
num_experts,
|
|
n,
|
|
k,
|
|
)
|
|
|
|
gateup_input = torch.empty(a_states.shape, dtype=torch.float8_e4m3fn, device=device)
|
|
fp8_per_token_to_per_tensor_quant_triton(
|
|
x=a_states,
|
|
x_scale=a_scales,
|
|
masked_m=masked_m,
|
|
output_scale=a1_scale,
|
|
output=gateup_input,
|
|
)
|
|
c1 = torch.empty((num_experts, m, n * 2), device=device, dtype=torch.bfloat16)
|
|
c2 = torch.empty((num_experts, m, k), device=device, dtype=torch.bfloat16)
|
|
|
|
cutlass_w4a8_moe_mm(
|
|
c1,
|
|
gateup_input,
|
|
w1_q,
|
|
a1_scale.float(),
|
|
w1_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes1,
|
|
a_strides1,
|
|
b_strides1,
|
|
c_strides1,
|
|
s_strides13,
|
|
128,
|
|
topk,
|
|
)
|
|
|
|
intermediate_q = torch.empty(
|
|
(num_experts, m, n), device=a_states.device, dtype=torch.float8_e4m3fn
|
|
)
|
|
silu_and_mul_masked_post_per_tensor_quant_fwd(
|
|
c1, intermediate_q, masked_m, a2_scale
|
|
)
|
|
cutlass_w4a8_moe_mm(
|
|
c2,
|
|
intermediate_q,
|
|
w2_q,
|
|
a2_scale.float(),
|
|
w2_scale,
|
|
expert_offsets[:-1],
|
|
problem_sizes2,
|
|
a_strides2,
|
|
b_strides2,
|
|
c_strides2,
|
|
s_strides2,
|
|
128,
|
|
topk,
|
|
)
|
|
|
|
return c2
|