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
394 lines
14 KiB
Python
394 lines
14 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""
|
|
KT Expert Parallelism Wrapper for MoE layers.
|
|
|
|
This module provides a generic wrapper that enables CPU-GPU expert parallelism
|
|
for any MoE quantization method. It coordinates parallel execution of GPU experts
|
|
(using any quantization method) and CPU experts (using AMX/AVX instructions).
|
|
"""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
import torch
|
|
|
|
from sglang.srt.layers.quantization.base_config import FusedMoEMethodBase
|
|
from sglang.srt.runtime_context import get_parallel
|
|
from sglang.srt.utils import get_compiler_backend
|
|
|
|
if TYPE_CHECKING:
|
|
from sglang.srt.layers.moe import MoeRunnerConfig
|
|
from sglang.srt.layers.moe.token_dispatcher import (
|
|
CombineInput,
|
|
StandardDispatchOutput,
|
|
)
|
|
from sglang.srt.server_args import ServerArgs
|
|
|
|
try:
|
|
from kt_kernel import KTMoEWrapper
|
|
|
|
KTRANSFORMERS_AVAILABLE = True
|
|
except ImportError:
|
|
KTRANSFORMERS_AVAILABLE = False
|
|
|
|
|
|
@dataclass
|
|
class KTConfig:
|
|
"""Configuration for KTransformers heterogeneous computing CPU part.
|
|
|
|
Args:
|
|
layer_idx: Layer index in the model
|
|
num_gpu_experts: Number of experts to run on GPU
|
|
cpuinfer_threads: Number of CPU inference threads
|
|
threadpool_count: Number of thread pools for CPU computation
|
|
weight_path: Path to CPU quantized weights
|
|
chunked_prefill_size: Chunk size for prefill computation
|
|
method: CPU computation method (e.g., "int4")
|
|
num_layers: Total number of layers in the model (optional)
|
|
"""
|
|
|
|
layer_idx: int
|
|
num_gpu_experts: int
|
|
cpuinfer_threads: int
|
|
threadpool_count: int
|
|
weight_path: str
|
|
chunked_prefill_size: int
|
|
max_deferred_experts_per_token: int
|
|
method: str
|
|
num_layers: Optional[int] = None
|
|
|
|
|
|
def create_kt_config_from_server_args(
|
|
server_args: "ServerArgs", layer_idx: int
|
|
) -> Optional[KTConfig]:
|
|
"""Create KTConfig from ServerArgs if KT is configured.
|
|
|
|
Args:
|
|
server_args: Global server arguments
|
|
layer_idx: Layer index in the model
|
|
|
|
Returns:
|
|
KTConfig if KT is configured, None otherwise
|
|
"""
|
|
if server_args.kt_weight_path is None:
|
|
return None
|
|
|
|
# Try to get num_layers from model config
|
|
num_layers = None
|
|
try:
|
|
hf_config = server_args.get_hf_config()
|
|
num_layers = getattr(hf_config, "num_hidden_layers", None)
|
|
except Exception:
|
|
# If we can't get the config, num_layers will be None
|
|
pass
|
|
|
|
return KTConfig(
|
|
layer_idx=layer_idx,
|
|
num_gpu_experts=server_args.kt_num_gpu_experts,
|
|
cpuinfer_threads=server_args.kt_cpuinfer,
|
|
threadpool_count=server_args.kt_threadpool_count,
|
|
weight_path=server_args.kt_weight_path,
|
|
chunked_prefill_size=server_args.chunked_prefill_size,
|
|
method=server_args.kt_method,
|
|
max_deferred_experts_per_token=server_args.kt_max_deferred_experts_per_token,
|
|
num_layers=num_layers,
|
|
)
|
|
|
|
|
|
@torch.compile(dynamic=True, backend=get_compiler_backend())
|
|
def mask_cpu_expert_ids(topk_ids: torch.Tensor, num_gpu_experts: int) -> torch.Tensor:
|
|
"""Mask CPU expert IDs by setting them to -1.
|
|
|
|
This function masks expert IDs that should be computed on CPU (IDs >= num_gpu_experts)
|
|
so they won't be computed on GPU. The masked IDs are set to -1, which causes the
|
|
GPU MoE kernel to skip those experts.
|
|
|
|
Args:
|
|
topk_ids: Tensor of shape [num_tokens, top_k] containing expert IDs
|
|
num_gpu_experts: Number of experts that should run on GPU (experts 0 to num_gpu_experts-1)
|
|
|
|
Returns:
|
|
Modified topk_ids tensor with CPU expert IDs masked as -1
|
|
"""
|
|
topk_ids[topk_ids >= num_gpu_experts] = -1
|
|
return topk_ids
|
|
|
|
|
|
class KTEPWrapperMethod(FusedMoEMethodBase):
|
|
"""Wrapper for any MoE quantization method to enable CPU-GPU expert parallelism.
|
|
|
|
This wrapper coordinates parallel execution of:
|
|
- GPU experts (0 to num_gpu_experts-1) using any quantization method
|
|
- CPU experts (num_gpu_experts to total_experts-1) using AMX/AVX instructions
|
|
|
|
The wrapper implements the submit-compute-sync pattern:
|
|
1. Submit CPU expert computation (non-blocking)
|
|
2. Execute GPU expert computation in parallel
|
|
3. Synchronize and merge CPU+GPU results
|
|
|
|
Example:
|
|
# Wrap any GPU method with AMX/AVX CPU expert support
|
|
gpu_method = CompressedTensorsWNA16MoE(quant_config, prefix)
|
|
kt_config = KTConfig(layer_idx=0, num_gpu_experts=4, ...)
|
|
method = KTEPWrapperMethod(gpu_method, kt_config)
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
gpu_method: FusedMoEMethodBase,
|
|
kt_config: KTConfig,
|
|
):
|
|
"""Initialize the KT EP wrapper.
|
|
|
|
Args:
|
|
gpu_method: The quantization method to use for GPU experts
|
|
kt_config: Configuration for KT CPU expert computation
|
|
"""
|
|
if not KTRANSFORMERS_AVAILABLE:
|
|
raise ImportError(
|
|
"kt_kernel is not installed. To use KTransformers EP wrapper, please install kt_kernel."
|
|
)
|
|
|
|
self.gpu_method = gpu_method
|
|
self.kt_config = kt_config
|
|
self.num_gpu_experts = kt_config.num_gpu_experts
|
|
self.override_num_local_experts = True
|
|
self.gpu_method.num_gpu_experts = self.num_gpu_experts
|
|
self.tp_rank = get_parallel().tp_rank
|
|
|
|
# KT wrapper will be initialized in create_weights
|
|
self.wrapper: Optional[KTMoEWrapper] = None
|
|
|
|
# Store parameters needed for KT initialization
|
|
self._layer_params = None
|
|
|
|
def create_weights(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
num_experts: int,
|
|
hidden_size: int,
|
|
intermediate_size_per_partition: int,
|
|
params_dtype: torch.dtype,
|
|
**extra_weight_attrs,
|
|
):
|
|
"""Create weights for both GPU and CPU experts.
|
|
|
|
Args:
|
|
layer: The MoE layer module
|
|
num_experts: Total number of experts (GPU + CPU)
|
|
hidden_size: Hidden dimension size
|
|
intermediate_size_per_partition: Intermediate size per TP partition
|
|
params_dtype: Data type for parameters
|
|
**extra_weight_attrs: Additional weight attributes
|
|
"""
|
|
self.global_num_experts = num_experts
|
|
self.hidden_size = hidden_size
|
|
self.intermediate_size_per_partition = intermediate_size_per_partition
|
|
|
|
# Get required parameters from layer object
|
|
# top_k: number of experts selected per token
|
|
num_experts_per_tok = layer.top_k
|
|
|
|
# intermediate_size_full: full intermediate size before TP partitioning
|
|
intermediate_size_full = (
|
|
layer.intermediate_size_per_partition * layer.moe_tp_size
|
|
)
|
|
|
|
layer_max_deferred = self.kt_config.max_deferred_experts_per_token or 0
|
|
if (
|
|
self.kt_config.max_deferred_experts_per_token is not None
|
|
and self.kt_config.num_layers is not None
|
|
and self.kt_config.layer_idx == self.kt_config.num_layers - 1
|
|
):
|
|
layer_max_deferred = 0
|
|
|
|
# 1. Create weights for GPU experts using the wrapped method
|
|
# GPU experts: 0 to num_gpu_experts-1
|
|
self.gpu_method.create_weights(
|
|
layer=layer,
|
|
num_experts=self.num_gpu_experts,
|
|
hidden_size=hidden_size,
|
|
intermediate_size_per_partition=intermediate_size_per_partition,
|
|
params_dtype=params_dtype,
|
|
**extra_weight_attrs,
|
|
)
|
|
|
|
# 2. Initialize KT wrapper for CPU experts
|
|
# CPU experts: num_gpu_experts to num_experts-1
|
|
if self.tp_rank == 0:
|
|
self.wrapper = KTMoEWrapper(
|
|
layer_idx=self.kt_config.layer_idx,
|
|
num_experts=num_experts,
|
|
num_experts_per_tok=num_experts_per_tok,
|
|
hidden_size=hidden_size,
|
|
moe_intermediate_size=intermediate_size_full,
|
|
num_gpu_experts=self.num_gpu_experts,
|
|
cpuinfer_threads=self.kt_config.cpuinfer_threads,
|
|
threadpool_count=self.kt_config.threadpool_count,
|
|
weight_path=self.kt_config.weight_path,
|
|
chunked_prefill_size=self.kt_config.chunked_prefill_size,
|
|
method=self.kt_config.method,
|
|
max_deferred_experts_per_token=layer_max_deferred,
|
|
)
|
|
|
|
def process_weights_after_loading(self, layer: torch.nn.Module) -> None:
|
|
"""Process weights after loading from checkpoint.
|
|
|
|
Args:
|
|
layer: The MoE layer module
|
|
"""
|
|
# 1. Process GPU weights
|
|
if hasattr(self.gpu_method, "process_weights_after_loading"):
|
|
self.gpu_method.process_weights_after_loading(layer)
|
|
|
|
# 2. Load CPU weights using KT wrapper
|
|
if self.tp_rank == 0 and self.wrapper is not None:
|
|
torch.cuda.synchronize()
|
|
|
|
# Get expert location metadata for CPU expert mapping
|
|
from sglang.srt.eplb.expert_location_dispatch import (
|
|
get_global_expert_location_metadata,
|
|
)
|
|
|
|
physical_to_logical_map_cpu = (
|
|
get_global_expert_location_metadata()
|
|
.physical_to_logical_map_cpu[self.kt_config.layer_idx]
|
|
.contiguous()
|
|
)
|
|
self.wrapper.load_weights(physical_to_logical_map_cpu)
|
|
|
|
def create_moe_runner(
|
|
self, layer: torch.nn.Module, moe_runner_config: "MoeRunnerConfig"
|
|
):
|
|
"""Create MoE runner for computation.
|
|
|
|
Args:
|
|
layer: The MoE layer module
|
|
moe_runner_config: Configuration for MoE runner
|
|
"""
|
|
self.moe_runner_config = moe_runner_config
|
|
if self.override_num_local_experts:
|
|
moe_runner_config.num_local_experts = self.num_gpu_experts
|
|
# Delegate to GPU method to create its runner
|
|
self.gpu_method.create_moe_runner(layer, moe_runner_config)
|
|
|
|
def submit(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
dispatch_output: "StandardDispatchOutput",
|
|
) -> None:
|
|
"""Submit CPU expert computation asynchronously (non-blocking).
|
|
|
|
This method submits the CPU expert computation to AMX/AVX without waiting
|
|
for completion, allowing GPU computation to proceed in parallel.
|
|
|
|
Args:
|
|
layer: The MoE layer module
|
|
dispatch_output: Dispatched tokens and routing information
|
|
"""
|
|
assert (
|
|
self.moe_runner_config.activation == "silu"
|
|
), "Only SiLU activation is supported."
|
|
|
|
if self.tp_rank != 0 or self.wrapper is None:
|
|
return
|
|
|
|
x = dispatch_output.hidden_states
|
|
topk_output = dispatch_output.topk_output
|
|
topk_weights, topk_ids, _ = topk_output
|
|
|
|
# Submit forward task to CPU (non-blocking)
|
|
self.wrapper.submit_forward(
|
|
x, topk_ids, topk_weights, torch.cuda.current_stream(x.device).cuda_stream
|
|
)
|
|
|
|
def sync(self, x: torch.Tensor) -> torch.Tensor:
|
|
"""Synchronize and retrieve CPU expert computation results.
|
|
|
|
This method waits for the CPU computation to complete and returns the results.
|
|
|
|
Args:
|
|
x: Reference tensor for shape and device information
|
|
|
|
Returns:
|
|
CPU expert computation results
|
|
"""
|
|
if self.tp_rank != 0 or self.wrapper is None:
|
|
return torch.zeros_like(x)
|
|
|
|
# Wait for CPU computation and retrieve results
|
|
return self.wrapper.sync_forward(
|
|
x, torch.cuda.current_stream(x.device).cuda_stream
|
|
)
|
|
|
|
def apply(
|
|
self,
|
|
layer: torch.nn.Module,
|
|
dispatch_output: "StandardDispatchOutput",
|
|
) -> "CombineInput":
|
|
"""Execute hybrid CPU+GPU MoE forward pass with parallelism.
|
|
|
|
This is the main computation method that coordinates:
|
|
1. Submit CPU expert computation (non-blocking)
|
|
2. Execute GPU expert computation in parallel
|
|
3. Synchronize CPU results and merge with GPU results
|
|
|
|
Args:
|
|
layer: The MoE layer module
|
|
dispatch_output: Dispatched tokens and routing information
|
|
|
|
Returns:
|
|
Combined computation results from CPU and GPU experts
|
|
"""
|
|
from sglang.srt.layers.moe.token_dispatcher import StandardCombineInput
|
|
|
|
x = dispatch_output.hidden_states
|
|
topk_output = dispatch_output.topk_output
|
|
|
|
# Step 1: Submit CPU expert computation (non-blocking)
|
|
if self.tp_rank == 0:
|
|
self.submit(layer, dispatch_output)
|
|
|
|
# Step 2: Prepare GPU computation by masking CPU expert IDs
|
|
# CPU expert IDs (>= num_gpu_experts) are set to -1 so GPU kernel skips them
|
|
topk_ids = topk_output.topk_ids
|
|
masked_topk_ids = mask_cpu_expert_ids(topk_ids, self.num_gpu_experts)
|
|
|
|
# Create modified dispatch output for GPU computation
|
|
masked_topk_output = topk_output._replace(topk_ids=masked_topk_ids)
|
|
masked_dispatch_output = dispatch_output._replace(
|
|
topk_output=masked_topk_output
|
|
)
|
|
|
|
# Step 3: Execute GPU expert computation (any quantization method)
|
|
# This runs in parallel with CPU computation
|
|
gpu_combine_input = self.gpu_method.apply(layer, masked_dispatch_output)
|
|
|
|
# Step 4: Synchronize CPU results and merge with GPU results
|
|
output = gpu_combine_input.hidden_states
|
|
if self.tp_rank == 0:
|
|
cpu_output = self.sync(x)
|
|
output = output + cpu_output
|
|
|
|
return StandardCombineInput(hidden_states=output)
|
|
|
|
def __getattr__(self, name: str):
|
|
"""Delegate attribute access to the wrapped GPU method.
|
|
|
|
This allows the wrapper to transparently expose attributes and methods
|
|
from the wrapped GPU quantization method.
|
|
|
|
Args:
|
|
name: Attribute name
|
|
|
|
Returns:
|
|
Attribute value from gpu_method
|
|
"""
|
|
# Avoid infinite recursion for internal attributes
|
|
if name in ("gpu_method", "wrapper", "kt_config"):
|
|
raise AttributeError(
|
|
f"'{type(self).__name__}' object has no attribute '{name}'"
|
|
)
|
|
|
|
return getattr(self.gpu_method, name)
|