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
367 lines
11 KiB
Python
367 lines
11 KiB
Python
import functools
|
|
import logging
|
|
import sys
|
|
from enum import IntEnum
|
|
from typing import TYPE_CHECKING, Callable
|
|
|
|
import torch
|
|
|
|
from sglang.srt.environ import envs
|
|
from sglang.srt.utils import get_npu_memory_capacity, is_npu
|
|
|
|
if TYPE_CHECKING:
|
|
from sglang.srt.server_args import ServerArgs
|
|
|
|
logger = logging.getLogger(__name__)
|
|
_is_npu = is_npu()
|
|
indexer_weight_stream = None
|
|
gva_is_inited = False
|
|
|
|
|
|
class NPUACLFormat(IntEnum):
|
|
ACL_FORMAT_UNDEFINED = -1
|
|
ACL_FORMAT_ND = 2
|
|
ACL_FORMAT_FRACTAL_NZ = 29
|
|
|
|
|
|
class FusedMoEMode(IntEnum):
|
|
FUSED_DEEP_MOE = 1
|
|
DISPATCH_FFN_COMBINE = 2
|
|
|
|
|
|
def _call_once(fn: Callable):
|
|
|
|
@functools.wraps(fn)
|
|
def wrapper(*args, **kwargs):
|
|
if getattr(fn, "_has_been_called", False):
|
|
logger.debug("Function {} has already been called.", fn.__name__)
|
|
return
|
|
|
|
fn._has_been_called = True
|
|
return fn(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
|
|
def set_default_server_args(args: "ServerArgs"):
|
|
"""
|
|
Set default server arguments for NPU backend.
|
|
"""
|
|
|
|
# NPU only works with "ascend" attention backend for now
|
|
args.attention_backend = "ascend"
|
|
args.prefill_attention_backend = "ascend"
|
|
args.decode_attention_backend = "ascend"
|
|
if args.page_size is None:
|
|
args.page_size = 128
|
|
|
|
# NPU memory settings
|
|
decode = args.cuda_graph_config.decode
|
|
npu_mem = get_npu_memory_capacity()
|
|
if npu_mem <= 32 * 1024:
|
|
# Ascend 910B4,910B4_1
|
|
# (chunked_prefill_size 4k, max_bs 16 if tp < 4 else 64)
|
|
if args.chunked_prefill_size is None:
|
|
args.chunked_prefill_size = 4 * 1024
|
|
if decode.max_bs is None:
|
|
if args.tp_size < 4:
|
|
decode.max_bs = 16
|
|
else:
|
|
decode.max_bs = 64
|
|
elif npu_mem <= 64 * 1024:
|
|
# Ascend 910B1,910B2,910B2C,910B3,910_9391,910_9392,910_9381,910_9382,910_9372,910_9362
|
|
# (chunked_prefill_size 8k, max_bs 64 if tp < 4 else 256)
|
|
if args.chunked_prefill_size is None:
|
|
args.chunked_prefill_size = 8 * 1024
|
|
if decode.max_bs is None:
|
|
if args.tp_size < 4:
|
|
decode.max_bs = 64
|
|
else:
|
|
decode.max_bs = 256
|
|
|
|
# NPU does not support CustomAllReduce
|
|
args.disable_custom_all_reduce = True
|
|
|
|
# handles hierarchical cache configs
|
|
if args.enable_hierarchical_cache:
|
|
args.hicache_io_backend = "kernel_ascend"
|
|
if args.use_mla_backend():
|
|
args.hicache_mem_layout = "page_first_kv_split"
|
|
else:
|
|
args.hicache_mem_layout = "page_first_direct"
|
|
|
|
|
|
@_call_once
|
|
def init_npu_backend():
|
|
"""
|
|
Initialize NPU backend. This function should be called only once.
|
|
"""
|
|
|
|
assert _is_npu, "NPU backend initialization called on non-NPU device."
|
|
|
|
try:
|
|
import custom_ops # noqa: F401
|
|
import sgl_kernel_npu # noqa: F401
|
|
except ImportError as e:
|
|
logger.warning("NPU custom kernel packages unavailable: %s", e)
|
|
|
|
import torch_npu
|
|
from torch_npu.contrib import transfer_to_npu # noqa: F401
|
|
|
|
# Re-mock torch.cuda.is_available cuz transfer_to_npu mocks it True
|
|
torch.cuda.is_available = lambda: False
|
|
|
|
torch_npu.npu.config.allow_internal_format = True
|
|
torch_npu.npu.set_compile_mode(jit_compile=False)
|
|
|
|
|
|
def _is_nz_aligned(tensor: torch.Tensor) -> bool:
|
|
"""Check whether the last two dims satisfy FRACTAL_NZ alignment rules.
|
|
|
|
Ascend FRACTAL_NZ requires:
|
|
BF16 / FP16 : both dims divisible by 16
|
|
INT8 : k % 16 == 0 and n % 32 == 0
|
|
INT4 : k % 16 == 0 and n % 64 == 0
|
|
FP4 : both dims divisible by 64
|
|
"""
|
|
if tensor.dim() < 2:
|
|
return False
|
|
k, n = tensor.shape[-2], tensor.shape[-1]
|
|
if tensor.dtype in (torch.bfloat16, torch.float16):
|
|
return k % 16 == 0 and n % 16 == 0
|
|
if tensor.dtype == torch.int8:
|
|
return k % 16 == 0 and n % 32 == 0
|
|
if tensor.dtype in (torch.uint8, torch.int32):
|
|
# INT4 is typically packed into uint8/int32; be conservative
|
|
return k % 16 == 0 and n % 64 == 0
|
|
return True
|
|
|
|
|
|
def npu_format_cast(
|
|
tensor: torch.Tensor,
|
|
acl_format: NPUACLFormat = NPUACLFormat.ACL_FORMAT_FRACTAL_NZ,
|
|
*,
|
|
customize_dtype=None,
|
|
input_dtype=None,
|
|
) -> torch.Tensor:
|
|
"""
|
|
Cast a tensor to a specific NPU ACL format.
|
|
|
|
Args:
|
|
tensor (torch.Tensor): The input tensor.
|
|
acl_format (NPUACLFormat): The target NPU ACL format.
|
|
customize_dtype / input_dtype: packed-FP4 unpack kwargs (e.g.
|
|
``customize_dtype=torch.float8_e4m3fn``,
|
|
``input_dtype=torch.float4_e2m1fn_x2``). When either is set the unpack
|
|
kwargs are forwarded to the op and the ``_is_nz_aligned`` ND fallback
|
|
is skipped: the FP4 matmul strictly requires FRACTAL_NZ, so a silent
|
|
ND fallback would corrupt results.
|
|
|
|
Returns:
|
|
torch.Tensor: The tensor cast to the specified NPU ACL format.
|
|
"""
|
|
|
|
if not _is_npu:
|
|
return tensor
|
|
|
|
if envs.SGLANG_NPU_DISABLE_ACL_FORMAT_WEIGHT.get():
|
|
return tensor
|
|
|
|
if tensor.device == torch.device("cpu"):
|
|
logger.warning_once(
|
|
"Warning: The conversion from 'ND' to 'NZ' does not work on the CPU. "
|
|
"Please disable offloading, otherwise the performance will be "
|
|
"significantly reduced. --dit-cpu-offload false"
|
|
)
|
|
return tensor
|
|
|
|
# Skip format cast for meta tensors (used in offloader)
|
|
if tensor.device.type == "meta":
|
|
return tensor
|
|
|
|
# Packed-FP4 → FRACTAL_NZ: forward the unpack kwargs to the op, and skip the
|
|
# _is_nz_aligned ND fallback — the FP4 matmul strictly requires NZ, so a
|
|
# silent ND fallback would corrupt results.
|
|
if customize_dtype is not None or input_dtype is not None:
|
|
return torch.ops.npu.npu_format_cast(
|
|
tensor,
|
|
int(acl_format),
|
|
customize_dtype=customize_dtype,
|
|
input_dtype=input_dtype,
|
|
)
|
|
|
|
if acl_format == NPUACLFormat.ACL_FORMAT_FRACTAL_NZ and not _is_nz_aligned(tensor):
|
|
k, n = tensor.shape[-2], tensor.shape[-1]
|
|
logger.warning_once(
|
|
"Skipping FRACTAL_NZ format cast: tensor shape (%d, %d) dtype %s "
|
|
"is not aligned to NZ requirements. Falling back to 'ND' format, "
|
|
"which may reduce NPU performance.",
|
|
k,
|
|
n,
|
|
tensor.dtype,
|
|
)
|
|
return tensor
|
|
|
|
return torch.ops.npu.npu_format_cast(tensor, acl_format.value)
|
|
|
|
|
|
def get_indexer_weight_stream():
|
|
global indexer_weight_stream
|
|
if indexer_weight_stream is None:
|
|
indexer_weight_stream = torch.npu.Stream()
|
|
return indexer_weight_stream
|
|
|
|
|
|
def init_zbal(world_size, gpu_id, world_rank, do_check=True):
|
|
"""
|
|
init zbal, if is mix alloc mode, only register for sma & comm
|
|
"""
|
|
zbal_mem_size = envs.SGLANG_ZBAL_LOCAL_MEM_SIZE.get()
|
|
if not zbal_mem_size > 0:
|
|
return 1
|
|
|
|
global gva_is_inited
|
|
from zbal import is_mix_alloc, switch_to_allocator, zbal_init
|
|
|
|
if is_mix_alloc():
|
|
switch_to_allocator()
|
|
# use lazy init for mix alloc
|
|
return 1
|
|
else:
|
|
if envs.SGLANG_ZBAL_BOOTSTRAP_URL.get():
|
|
ret = zbal_init(
|
|
world_size,
|
|
gpu_id,
|
|
world_rank,
|
|
zbal_mem_size * (1024**2),
|
|
ip_port=envs.SGLANG_ZBAL_BOOTSTRAP_URL.get(),
|
|
)
|
|
else:
|
|
ret = zbal_init(world_size, gpu_id, world_rank, zbal_mem_size * (1024**2))
|
|
|
|
gva_is_inited = True
|
|
|
|
if do_check and not ret:
|
|
logger.error("[ZBAL] zbal init failed!")
|
|
sys.exit(-1)
|
|
|
|
return ret
|
|
|
|
|
|
def lazy_init_zbal_gva_mem(
|
|
device, gpu_id, world_rank, world_size, cpu_group=None, do_check=True
|
|
):
|
|
"""
|
|
lazy init zbal gva mem, keep weights and kv remains alloc by dma vmm to avoid memory fragment
|
|
"""
|
|
from zbal import is_mix_alloc, zbal_init
|
|
|
|
if not is_mix_alloc():
|
|
logger.info(
|
|
"lazy init is supported only in mix alloc mode, this action will be passed"
|
|
)
|
|
return 1
|
|
|
|
global gva_is_inited
|
|
from sglang.srt.utils.common import get_available_gpu_memory
|
|
|
|
# TODO need to use allgather if you want use total_memory stats from mem_get_info as unbalance os
|
|
total_memory = 61.2 # 2.5GB for other (workspace & os) outside torch
|
|
free_gpu_memory = get_available_gpu_memory(
|
|
device,
|
|
gpu_id,
|
|
distributed=world_size > 1,
|
|
cpu_group=cpu_group,
|
|
empty_cache=True,
|
|
)
|
|
|
|
used_memory = total_memory - free_gpu_memory
|
|
|
|
used_memory_in_mb = int(used_memory * 1024)
|
|
gva_in_mb = envs.SGLANG_ZBAL_LOCAL_MEM_SIZE.get() - used_memory_in_mb
|
|
gva_in_mb = gva_in_mb - gva_in_mb % 128 # align to 128MB
|
|
print(f"[ZBAL] rank {world_rank} allocated {gva_in_mb} MB gva space.")
|
|
|
|
assert not gva_is_inited, "zbal gva should be inited only once"
|
|
# zbal_set_logger_level(0)
|
|
if envs.SGLANG_ZBAL_BOOTSTRAP_URL.get():
|
|
res = zbal_init(
|
|
world_size,
|
|
gpu_id,
|
|
world_rank,
|
|
gva_in_mb * (1024**2),
|
|
ip_port=envs.SGLANG_ZBAL_BOOTSTRAP_URL.get(),
|
|
)
|
|
else:
|
|
res = zbal_init(world_size, gpu_id, world_rank, gva_in_mb * (1024**2))
|
|
|
|
gva_is_inited = True
|
|
if do_check and not res:
|
|
logger.error("[ZBAL] zbal lazy init failed!")
|
|
sys.exit(-1)
|
|
return res
|
|
|
|
|
|
share_stream = None
|
|
routed_stream = None
|
|
|
|
|
|
def get_share_stream():
|
|
global share_stream
|
|
return share_stream
|
|
|
|
|
|
def set_share_stream(stream):
|
|
global share_stream
|
|
share_stream = stream
|
|
# TODO LKL: set stream limit has impact on precision
|
|
# torch.npu.set_stream_limit(share_stream, 8, 16)
|
|
|
|
|
|
def get_routed_stream():
|
|
global routed_stream
|
|
return routed_stream
|
|
|
|
|
|
def set_routed_stream(stream):
|
|
global routed_stream
|
|
routed_stream = stream
|
|
# TODO LKL: set stream limit has impact on precision
|
|
# torch.npu.set_stream_limit(routed_stream, 16, 32)
|
|
|
|
|
|
def wait_share_stream():
|
|
stream = get_share_stream()
|
|
if stream is not None:
|
|
cur_stream = torch.get_device_module().current_stream()
|
|
cur_stream.wait_stream(stream)
|
|
|
|
|
|
def wait_routed_stream():
|
|
stream = get_routed_stream()
|
|
if stream is not None:
|
|
cur_stream = torch.get_device_module().current_stream()
|
|
cur_stream.wait_stream(stream)
|
|
|
|
|
|
def process_shared_expert(hidden_states, forward_func):
|
|
stream = get_share_stream()
|
|
if stream is None:
|
|
stream = torch.get_device_module().Stream()
|
|
set_share_stream(stream)
|
|
stream.wait_stream(torch.get_device_module().current_stream())
|
|
with torch.get_device_module().stream(stream):
|
|
shared_output = forward_func(hidden_states)
|
|
return shared_output
|
|
|
|
|
|
def process_routed_expert(hidden_states, topk_output, forward_func):
|
|
stream = get_routed_stream()
|
|
if stream is None:
|
|
stream = torch.get_device_module().Stream()
|
|
set_routed_stream(stream)
|
|
stream.wait_stream(torch.get_device_module().current_stream())
|
|
with torch.get_device_module().stream(stream):
|
|
shared_output = forward_func(hidden_states, topk_output)
|
|
return shared_output
|