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
510 lines
19 KiB
Python
510 lines
19 KiB
Python
import logging
|
|
import math
|
|
import os
|
|
import time
|
|
from contextlib import contextmanager, nullcontext
|
|
from enum import IntEnum, auto
|
|
from typing import Dict, List, Tuple
|
|
|
|
import torch
|
|
from tqdm import tqdm
|
|
|
|
from sglang.srt.distributed.device_communicators.pynccl_allocator import (
|
|
disable_symmetric_memory_context,
|
|
restore_symmetric_memory_context,
|
|
)
|
|
from sglang.srt.environ import envs
|
|
from sglang.srt.layers.deep_gemm_wrapper.configurer import ENABLE_JIT_DEEPGEMM
|
|
from sglang.srt.model_executor.forward_batch_info import ForwardMode
|
|
from sglang.srt.runtime_context import get_parallel
|
|
from sglang.srt.server_args import ServerArgs
|
|
from sglang.srt.utils import ceil_align, ceil_div, get_available_gpu_memory, is_musa
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_is_musa = is_musa()
|
|
|
|
if ENABLE_JIT_DEEPGEMM:
|
|
import deep_gemm
|
|
|
|
|
|
_BUILTIN_M_LIST = list(range(1, 1024 * 16 + 1))
|
|
_ENABLE_JIT_DEEPGEMM_PRECOMPILE = envs.SGLANG_JIT_DEEPGEMM_PRECOMPILE.get()
|
|
_DO_COMPILE_ALL = True
|
|
_IS_FIRST_RANK_ON_NODE = envs.SGLANG_IS_FIRST_RANK_ON_NODE.get()
|
|
_IN_PRECOMPILE_STAGE = envs.SGLANG_IN_DEEPGEMM_PRECOMPILE_STAGE.get()
|
|
_FAST_WARMUP = envs.SGLANG_JIT_DEEPGEMM_FAST_WARMUP.get()
|
|
|
|
# Force redirect deep_gemm cache_dir
|
|
os.environ["DG_JIT_CACHE_DIR"] = os.getenv(
|
|
"SGLANG_DG_CACHE_DIR", os.path.join(os.path.expanduser("~"), ".cache", "deep_gemm")
|
|
)
|
|
|
|
# Refer to https://github.com/deepseek-ai/DeepGEMM/commit/d75b218b7b8f4a5dd5406ac87905039ead3ae42f
|
|
# NVRTC may have performance loss with some cases.
|
|
# And NVCC JIT speed is also 9x faster in the ref commit
|
|
os.environ["DG_JIT_USE_NVRTC"] = os.getenv("SGL_DG_USE_NVRTC", "0")
|
|
|
|
|
|
def update_deep_gemm_config(gpu_id: int, server_args: ServerArgs):
|
|
global _BUILTIN_M_LIST
|
|
global _DO_COMPILE_ALL
|
|
global _IS_FIRST_RANK_ON_NODE
|
|
|
|
_BUILTIN_M_LIST = []
|
|
|
|
if _FAST_WARMUP:
|
|
# In fast warmup mode, only compile a small set of typical Ms
|
|
|
|
# First cover all the small bs to ensure decode performance
|
|
_BUILTIN_M_LIST += list(range(1, 1025))
|
|
|
|
# Then cover larger batch sizes with gradually increasing steps
|
|
# For example, when chunekd prefill size is 16384
|
|
# The sampled Ms would be:
|
|
# 1024, 1026, ... 2046 (step 2)
|
|
# 2048, 2052, ... 4092 (step 4)
|
|
# 4096, 5004, ... 8184 (step 8)
|
|
# 8192, 9008, ... 16384 (step 16)
|
|
# Totally 1024 + 1024 / 2 + 2048 / 4 + 4096 / 8 + 8192 / 16 = 3072 kernels
|
|
next_m, sample_step = 1024, 2
|
|
max_prefill_bs = (
|
|
min(server_args.chunked_prefill_size, 32 * 1024)
|
|
if server_args.chunked_prefill_size >= 1
|
|
else 16 * 1024
|
|
)
|
|
while next_m < max_prefill_bs:
|
|
_BUILTIN_M_LIST += list(range(next_m, 2 * next_m, sample_step))
|
|
next_m = next_m * 2
|
|
sample_step = sample_step * 2
|
|
_BUILTIN_M_LIST.append(max_prefill_bs)
|
|
_BUILTIN_M_LIST = sorted(list(set(_BUILTIN_M_LIST)))
|
|
else:
|
|
# When fast warmup isn't enabled, generate m_max and compile all the covered Ms.
|
|
m_max = 1024 * 16
|
|
if server_args.chunked_prefill_size < 1:
|
|
m_max = 1024 * 64
|
|
elif server_args.chunked_prefill_size > 8192:
|
|
m_max = server_args.chunked_prefill_size * 2
|
|
m_max = min(1024 * 128, m_max)
|
|
_BUILTIN_M_LIST += list(range(1, m_max + 1))
|
|
|
|
_IS_FIRST_RANK_ON_NODE = server_args.base_gpu_id == gpu_id
|
|
|
|
# Check if is the first rank on node.
|
|
# Default each rank will try compile all Ms to
|
|
# load all symbols at the launch stages.
|
|
# Avoid loading symbols at the serving stages.
|
|
_DO_COMPILE_ALL = _IS_FIRST_RANK_ON_NODE
|
|
|
|
|
|
class DeepGemmKernelType(IntEnum):
|
|
GROUPED_GEMM_NT_F8F8BF16_MASKED = auto()
|
|
GROUPED_GEMM_NT_F8F8BF16_CONTIG = auto()
|
|
GROUPED_GEMM_NT_BF16_MASKED = auto()
|
|
GROUPED_GEMM_NT_BF16_CONTIG = auto()
|
|
GEMM_NT_F8F8BF16 = auto()
|
|
GEMM_NT_BF16BF16F32 = auto()
|
|
|
|
|
|
_INITIALIZATION_DICT: Dict[Tuple[DeepGemmKernelType, int, int, int], bool] = dict()
|
|
|
|
|
|
# TODO improve code
|
|
def _maybe_compile_deep_gemm_one_type_all(
|
|
kernel_type: DeepGemmKernelType,
|
|
n: int,
|
|
k: int,
|
|
num_groups: int,
|
|
) -> None:
|
|
global _INITIALIZATION_DICT
|
|
global _BUILTIN_M_LIST
|
|
|
|
query_key = (kernel_type, n, k, num_groups)
|
|
if (
|
|
_ENABLE_JIT_DEEPGEMM_PRECOMPILE
|
|
and _DO_COMPILE_ALL
|
|
and _INITIALIZATION_DICT.get(query_key) is None
|
|
):
|
|
_INITIALIZATION_DICT[query_key] = True
|
|
|
|
# TODO maybe improve logs
|
|
if not _IN_PRECOMPILE_STAGE and _IS_FIRST_RANK_ON_NODE:
|
|
logger.warning(
|
|
"Entering DeepGEMM JIT Pre-Compile session. "
|
|
"It may take a long time (typically 10-20 mins) "
|
|
"if you have not run `sglang.compile_deep_gemm`. "
|
|
"It is recommended to run `sglang.compile_deep_gemm` with same args as `sglang.launch_server`"
|
|
" for pre-compilation to reduce the overhead if you have not run it before. "
|
|
"For example: "
|
|
"`python3 -m sglang.compile_deep_gemm --model deepseek-ai/DeepSeek-V3 --tp 8 --trust-remote-code`"
|
|
)
|
|
|
|
logger.info(
|
|
f"Try DeepGEMM JIT Compiling for "
|
|
f"<{kernel_type.name}> N={n}, K={k}, num_groups={num_groups} with all Ms."
|
|
f"{' It only takes a little time (typically 1 sec) if you have run `python3 -m sglang.compile_deep_gemm`. ' if not _IN_PRECOMPILE_STAGE else ''}"
|
|
)
|
|
|
|
_compile_deep_gemm_one_type_all(
|
|
kernel_type=kernel_type,
|
|
n=n,
|
|
k=k,
|
|
num_groups=num_groups,
|
|
m_list=_BUILTIN_M_LIST,
|
|
)
|
|
|
|
|
|
# NOTE(alcanderian): get_num_sms should be change when 2-batch-overlap is introduced
|
|
def _compile_deep_gemm_one_type_all(
|
|
kernel_type: DeepGemmKernelType,
|
|
n: int,
|
|
k: int,
|
|
num_groups: int,
|
|
m_list: List[int],
|
|
) -> None:
|
|
# Symmetric memory allocation performs a collective operation across all the GPUs.
|
|
# Temporary disable symmetric memory during compilation since it only runs on the first rank.
|
|
saved_context = disable_symmetric_memory_context()
|
|
try:
|
|
if kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_F8F8BF16_CONTIG:
|
|
m_alignment = deep_gemm.get_mk_alignment_for_contiguous_layout()
|
|
m_list = sorted(list(set(m for m in m_list if m % m_alignment == 0)))
|
|
elif kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_BF16_CONTIG:
|
|
m_alignment = deep_gemm.get_mk_alignment_for_contiguous_layout()
|
|
m_list = sorted(list(set(m for m in m_list if m % m_alignment == 0)))
|
|
|
|
# Here the precompilation is only run on the first rank, so gpu_id should be 0
|
|
memory_budget = get_available_gpu_memory(device="cuda", gpu_id=0)
|
|
|
|
# If the memory budget is less memory requirement, we need to reduce max_m to avoid out of memory, which might further cause hanging during warmup
|
|
max_m = max(m_list)
|
|
required_memory = _BaseWarmupExecutor.get_memory_requirement(
|
|
kernel_type, max_m=max_m, n=n, k=k, num_groups=num_groups
|
|
)
|
|
logger.info(
|
|
f"Required memory for warmup: {required_memory}GB, Available memory: {memory_budget}GB"
|
|
)
|
|
if memory_budget < required_memory:
|
|
# TODO: Maybe compute the max_m based on the memory budget
|
|
while (
|
|
_BaseWarmupExecutor.get_memory_requirement(
|
|
kernel_type, max_m=max_m, n=n, k=k, num_groups=num_groups
|
|
)
|
|
> memory_budget
|
|
and max_m > 4096
|
|
):
|
|
max_m = max_m // 2
|
|
logger.warning(
|
|
f"Available memory {memory_budget}GB is less than required memory {required_memory}GB for warmup, reducing max_m to {max_m} to avoid out of memory"
|
|
)
|
|
m_list = [m for m in m_list if m <= max_m]
|
|
|
|
# Need some methods to estimate needed memory for warmup
|
|
executor = _BaseWarmupExecutor.create(
|
|
kernel_type, max_m=max_m, n=n, k=k, num_groups=num_groups
|
|
)
|
|
|
|
has_compile_mode_api = hasattr(deep_gemm, "get_compile_mode") and hasattr(
|
|
deep_gemm, "set_compile_mode"
|
|
)
|
|
if has_compile_mode_api:
|
|
old_compile_mode = deep_gemm.get_compile_mode()
|
|
deep_gemm.set_compile_mode(1)
|
|
|
|
# TODO can use multi thread
|
|
for m in tqdm(m_list, desc="DeepGEMM warmup"):
|
|
executor.execute(m=m)
|
|
if has_compile_mode_api:
|
|
deep_gemm.set_compile_mode(old_compile_mode)
|
|
|
|
# clean up input buffers
|
|
torch.cuda.current_stream().synchronize()
|
|
del executor
|
|
torch.cuda.empty_cache()
|
|
finally:
|
|
# Restore symmetric memory context
|
|
restore_symmetric_memory_context(saved_context)
|
|
|
|
|
|
class _BaseWarmupExecutor:
|
|
@staticmethod
|
|
def create(kernel_type: DeepGemmKernelType, **kwargs):
|
|
return {
|
|
DeepGemmKernelType.GEMM_NT_F8F8BF16: _NormalWarmupExecutor,
|
|
DeepGemmKernelType.GROUPED_GEMM_NT_F8F8BF16_CONTIG: _GroupedContWarmupExecutor,
|
|
DeepGemmKernelType.GROUPED_GEMM_NT_F8F8BF16_MASKED: _GroupedMaskedWarmupExecutor,
|
|
DeepGemmKernelType.GEMM_NT_BF16BF16F32: _BF16F32WarmupExecutor,
|
|
DeepGemmKernelType.GROUPED_GEMM_NT_BF16_CONTIG: _BF16GroupedContWarmupExecutor,
|
|
DeepGemmKernelType.GROUPED_GEMM_NT_BF16_MASKED: _BF16GroupedMaskedWarmupExecutor,
|
|
}[kernel_type](**kwargs)
|
|
|
|
@staticmethod
|
|
def get_memory_requirement(
|
|
kernel_type: DeepGemmKernelType, max_m: int, n: int, k: int, num_groups: int
|
|
) -> int:
|
|
# Return the required memory space in GB for warmup executor
|
|
_GB = 1 << 30
|
|
if kernel_type == DeepGemmKernelType.GEMM_NT_F8F8BF16:
|
|
return (max_m * k + n * k + max_m * n * 2) / _GB
|
|
elif kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_F8F8BF16_CONTIG:
|
|
return (max_m * k + num_groups * n * k + max_m * 4 + max_m * n * 2) / _GB
|
|
elif kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_BF16_CONTIG:
|
|
return (
|
|
max_m * k * 2 + num_groups * n * k * 2 + max_m * 4 + max_m * n * 2
|
|
) / _GB
|
|
elif kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_F8F8BF16_MASKED:
|
|
return (
|
|
num_groups * max_m * k
|
|
+ num_groups * n * k
|
|
+ num_groups * 4
|
|
+ num_groups * max_m * n * 2
|
|
) / _GB
|
|
elif kernel_type == DeepGemmKernelType.GEMM_NT_BF16BF16F32:
|
|
# bf16 lhs + bf16 rhs + fp32 out
|
|
return (max_m * k * 2 + n * k * 2 + max_m * n * 4) / _GB
|
|
elif kernel_type == DeepGemmKernelType.GROUPED_GEMM_NT_BF16_MASKED:
|
|
return (
|
|
num_groups * max_m * k * 2
|
|
+ num_groups * n * k * 2
|
|
+ num_groups * 4
|
|
+ num_groups * max_m * n * 2
|
|
) / _GB
|
|
else:
|
|
raise ValueError(f"Invalid kernel type: {kernel_type}")
|
|
|
|
def execute(self, m):
|
|
raise NotImplementedError
|
|
|
|
|
|
def _empty_token_fp8(size):
|
|
*dims, k = size
|
|
return (
|
|
torch.empty(size, device="cuda", dtype=torch.float8_e4m3fn),
|
|
torch.ones(
|
|
(*dims, ceil_div(k, _BLOCK_SIZE)), device="cuda", dtype=torch.float32
|
|
),
|
|
)
|
|
|
|
|
|
def _empty_block_fp8(size):
|
|
*dims, n, k = size
|
|
return (
|
|
torch.empty(size, device="cuda", dtype=torch.float8_e4m3fn),
|
|
torch.ones(
|
|
(*dims, ceil_div(n, _BLOCK_SIZE), ceil_div(k, _BLOCK_SIZE)),
|
|
device="cuda",
|
|
dtype=torch.float32,
|
|
),
|
|
)
|
|
|
|
|
|
_BLOCK_SIZE = 128
|
|
|
|
|
|
class _NormalWarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.lhs_q, self.lhs_s = _empty_token_fp8((max_m, k))
|
|
self.rhs_q, self.rhs_s = _empty_block_fp8((n, k))
|
|
self.out = torch.empty((max_m, n), device="cuda", dtype=torch.bfloat16)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.fp8_gemm_nt(
|
|
(self.lhs_q[:m], self.lhs_s[:m]),
|
|
(self.rhs_q, self.rhs_s),
|
|
self.out[:m],
|
|
)
|
|
|
|
|
|
class _GroupedContWarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.lhs_q, self.lhs_s = _empty_token_fp8((max_m, k))
|
|
self.rhs_q, self.rhs_s = _empty_block_fp8((num_groups, n, k))
|
|
self.m_indices = torch.zeros((max_m,), device="cuda", dtype=torch.int32)
|
|
self.out = torch.empty((max_m, n), device="cuda", dtype=torch.bfloat16)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.m_grouped_fp8_gemm_nt_contiguous(
|
|
(self.lhs_q[:m], self.lhs_s[:m]),
|
|
(self.rhs_q, self.rhs_s),
|
|
self.out[:m],
|
|
self.m_indices[:m],
|
|
)
|
|
|
|
|
|
class _BF16GroupedContWarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.a = torch.empty((max_m, k), device="cuda", dtype=torch.bfloat16)
|
|
self.b = torch.empty((num_groups, n, k), device="cuda", dtype=torch.bfloat16)
|
|
self.m_indices = torch.zeros((max_m,), device="cuda", dtype=torch.int32)
|
|
self.out = torch.empty((max_m, n), device="cuda", dtype=torch.bfloat16)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.m_grouped_bf16_gemm_nt_contiguous(
|
|
self.a[:m],
|
|
self.b,
|
|
self.out[:m],
|
|
self.m_indices[:m],
|
|
)
|
|
|
|
|
|
class _GroupedMaskedWarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.lhs_q, self.lhs_s = _empty_token_fp8((num_groups, max_m, k))
|
|
self.rhs_q, self.rhs_s = _empty_block_fp8((num_groups, n, k))
|
|
self.masked_m = torch.zeros((num_groups,), device="cuda", dtype=torch.int32)
|
|
self.out = torch.empty(
|
|
(num_groups, max_m, n), device="cuda", dtype=torch.bfloat16
|
|
)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.fp8_m_grouped_gemm_nt_masked(
|
|
(self.lhs_q, self.lhs_s),
|
|
(self.rhs_q, self.rhs_s),
|
|
self.out,
|
|
masked_m=self.masked_m,
|
|
# DeepGEMM uses `expect_m` instead of input shape for `get_best_config`
|
|
expected_m=m,
|
|
)
|
|
|
|
|
|
class _BF16F32WarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.lhs = torch.empty((max_m, k), device="cuda", dtype=torch.bfloat16)
|
|
self.rhs = torch.empty((n, k), device="cuda", dtype=torch.bfloat16)
|
|
self.out = torch.empty((max_m, n), device="cuda", dtype=torch.float32)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.bf16_gemm_nt(self.lhs[:m], self.rhs, self.out[:m])
|
|
|
|
|
|
class _BF16GroupedMaskedWarmupExecutor(_BaseWarmupExecutor):
|
|
def __init__(self, max_m: int, n: int, k: int, num_groups: int):
|
|
self.a = torch.empty(
|
|
(num_groups, max_m, k), device="cuda", dtype=torch.bfloat16
|
|
)
|
|
self.b = torch.empty((num_groups, n, k), device="cuda", dtype=torch.bfloat16)
|
|
self.masked_m = torch.zeros((num_groups,), device="cuda", dtype=torch.int32)
|
|
self.out = torch.empty(
|
|
(num_groups, max_m, n), device="cuda", dtype=torch.bfloat16
|
|
)
|
|
|
|
def execute(self, m):
|
|
deep_gemm.m_grouped_bf16_gemm_nt_masked(
|
|
self.a,
|
|
self.b,
|
|
self.out,
|
|
masked_m=self.masked_m,
|
|
# DeepGEMM uses `expect_m` instead of input shape for `get_best_config`
|
|
expected_m=m,
|
|
)
|
|
|
|
|
|
def deep_gemm_execution_hook(
|
|
m: int, n: int, k: int, num_groups: int, kernel_type: DeepGemmKernelType
|
|
):
|
|
if _is_musa:
|
|
return nullcontext()
|
|
|
|
return _deep_gemm_execution_hook(m, n, k, num_groups, kernel_type)
|
|
|
|
|
|
@contextmanager
|
|
def _deep_gemm_execution_hook(
|
|
m: int, n: int, k: int, num_groups: int, kernel_type: DeepGemmKernelType
|
|
):
|
|
if m > 0:
|
|
_maybe_compile_deep_gemm_one_type_all(kernel_type, n, k, num_groups)
|
|
yield
|
|
|
|
|
|
def pp_parallel_deep_gemm_warmup(runner) -> None:
|
|
"""Run per-PP-rank dummy DECODE+EXTEND forwards so each rank's
|
|
DeepGEMM JIT compiles in parallel instead of serially via the warmup
|
|
/generate flowing through the pipeline. Opt-in via
|
|
SGLANG_PP_PARALLEL_DEEPGEMM_WARMUP.
|
|
|
|
Driven from BaseRunner.warmup(), which passes the runner; the dummy
|
|
forwards go through runner._dummy_run (the autotune/dummy-run machinery now
|
|
lives on BaseRunner). ModelRunner state is read via runner.model_runner.
|
|
"""
|
|
model_runner = runner.model_runner
|
|
# n_splits ~= n_sms / ceil(bs/block_m) with block_m=64; sweep 5 bs to
|
|
# cover the brackets real /generate hits (smallest decode shape,
|
|
# mid-low, two mid, and n_splits=1 for ~5K+ token prefill). Ceil-align
|
|
# bs to the CP padding alignment (cp_size, or 2*cp_size for DSA
|
|
# in-seq-split). _dummy_run does not pad q/hidden like the real flow, so
|
|
# an unaligned bs makes DSA's padded num_splits longer than the q tokens
|
|
# and trips FlashMLA's "num_splits must have shape (b+1)" check.
|
|
from sglang.srt.layers.utils.cp_utils import get_cp_padding_align_size
|
|
from sglang.srt.utils.common import require_mlp_sync
|
|
|
|
n_sms = torch.cuda.get_device_properties(model_runner.device).multi_processor_count
|
|
block_m = 64
|
|
cp = max(get_cp_padding_align_size(), 1)
|
|
|
|
attn_tp_size = get_parallel().attn_tp_size
|
|
mlp_sync = require_mlp_sync(model_runner.server_args)
|
|
|
|
def _align(bs: int) -> int:
|
|
# Align to lcm(cp, attn_tp_size) so the CP multiple isn't undone by a
|
|
# later attn_tp align (e.g. cp=2, attn_tp=3: 128 -> 128 -> 129).
|
|
align = cp
|
|
if mlp_sync and attn_tp_size > 1:
|
|
align = math.lcm(cp, attn_tp_size)
|
|
return ceil_align(bs, align)
|
|
|
|
batch_sizes = sorted(
|
|
{
|
|
_align(bs)
|
|
for bs in (
|
|
1,
|
|
2 * block_m,
|
|
max(n_sms // 8, 2) * block_m,
|
|
max(n_sms // 4, 4) * block_m,
|
|
n_sms * block_m,
|
|
)
|
|
}
|
|
)
|
|
|
|
# In PD, prefill-only nodes never decode (indexer would OOM at large
|
|
# bs) and decode-only nodes never extend.
|
|
disagg_mode = model_runner.server_args.disaggregation_mode
|
|
run_decode = model_runner.is_generation and disagg_mode != "prefill"
|
|
run_extend = disagg_mode != "decode"
|
|
|
|
logger.info(
|
|
"PP-parallel DeepGEMM warmup start "
|
|
"(pp_rank=%d, tp_rank=%d, batch_sizes=%s, disagg=%s).",
|
|
model_runner.pp_rank,
|
|
model_runner.tp_rank,
|
|
batch_sizes,
|
|
disagg_mode,
|
|
)
|
|
|
|
# One buffer set sized to the largest shape, reused across the sweep
|
|
# (the decode runner's max_bs is too small for n_sms*block_m).
|
|
dummy_buffers = runner._alloc_dummy_decode_buffers(max(batch_sizes))
|
|
|
|
t0 = time.perf_counter()
|
|
with torch.inference_mode():
|
|
for bs in batch_sizes:
|
|
if run_decode:
|
|
runner._dummy_run(
|
|
batch_size=bs,
|
|
forward_mode_override=ForwardMode.DECODE,
|
|
buffers=dummy_buffers,
|
|
)
|
|
if run_extend:
|
|
runner._dummy_run(
|
|
batch_size=bs,
|
|
forward_mode_override=ForwardMode.EXTEND,
|
|
buffers=dummy_buffers,
|
|
)
|
|
|
|
logger.info(
|
|
"PP-parallel DeepGEMM warmup done in %.2fs (pp_rank=%d).",
|
|
time.perf_counter() - t0,
|
|
model_runner.pp_rank,
|
|
)
|