chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
SGLang Platform Discovery and Lazy Initialization.
|
||||
|
||||
Provides `current_platform` as a module-level lazy singleton. On first access,
|
||||
it discovers platform plugins via entry_points and instantiates the appropriate
|
||||
SRTPlatform subclass.
|
||||
|
||||
Usage:
|
||||
from sglang.srt.platforms import current_platform
|
||||
print(current_platform.device_name)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pkgutil
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
import torch
|
||||
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.platforms.cpu import CpuSRTPlatform
|
||||
from sglang.srt.platforms.cuda import CudaSRTPlatform
|
||||
from sglang.srt.platforms.interface import SRTPlatform
|
||||
from sglang.srt.platforms.rocm import RocmSRTPlatform
|
||||
from sglang.srt.plugins import PLATFORM_PLUGINS_GROUP, load_plugins_by_group
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_current_platform: SRTPlatform | None = None
|
||||
|
||||
|
||||
def _is_cuda_available() -> bool:
|
||||
return bool(torch.cuda.is_available() and torch.version.hip is None)
|
||||
|
||||
|
||||
def _is_rocm_available() -> bool:
|
||||
return bool(torch.cuda.is_available() and torch.version.hip is not None)
|
||||
|
||||
|
||||
def _is_cpu_available() -> bool:
|
||||
return os.getenv("SGLANG_USE_CPU_ENGINE", "0") == "1"
|
||||
|
||||
|
||||
def _resolve_platform() -> SRTPlatform:
|
||||
"""
|
||||
Discover and instantiate the active platform.
|
||||
|
||||
Discovery flow:
|
||||
1. Branch on SGLANG_PLATFORM:
|
||||
|
||||
SGLANG_PLATFORM set (front-loading filter):
|
||||
- Enumerate entry_points without importing any plugin modules
|
||||
- Only ep.load() + activate() the named plugin
|
||||
- Other plugins are never imported (avoids pulling their dependencies)
|
||||
- Plugin name not found → RuntimeError
|
||||
- activate() returns None → RuntimeError (hardware unavailable)
|
||||
|
||||
SGLANG_PLATFORM unset (auto-discover):
|
||||
- Import and activate all discovered plugins
|
||||
- 0 activated + SGLANG_USE_CPU_ENGINE=1 → fallback CpuSRTPlatform
|
||||
(checked first; an explicit opt-in wins over CUDA/ROCm availability,
|
||||
so developers on GPU hosts can intentionally exercise the CPU path)
|
||||
- 0 activated + CUDA available → fallback CudaSRTPlatform
|
||||
- 0 activated + ROCm available → fallback RocmSRTPlatform
|
||||
- 0 activated + none of the above → fallback base SRTPlatform
|
||||
- 1 activated → use it
|
||||
- N activated → RuntimeError (must set SGLANG_PLATFORM)
|
||||
|
||||
SGLANG_PLATFORM matches against entry_point names.
|
||||
"""
|
||||
selected = envs.SGLANG_PLATFORM.get()
|
||||
|
||||
if selected:
|
||||
# Front-loading filter: only import and activate the specified plugin.
|
||||
# Other plugins' modules are never loaded — avoids pulling their deps.
|
||||
discovered = entry_points(group=PLATFORM_PLUGINS_GROUP)
|
||||
ep_map = {ep.name: ep for ep in discovered}
|
||||
|
||||
if selected not in ep_map:
|
||||
available = ", ".join(f"'{n}'" for n in ep_map) if ep_map else "none"
|
||||
raise RuntimeError(
|
||||
f"SGLANG_PLATFORM={selected!r} not found in discovered platform plugins "
|
||||
f"(available: {available}). Install the plugin with 'pip install -e' "
|
||||
f"to register its entry_points."
|
||||
)
|
||||
|
||||
try:
|
||||
plugin_fn = ep_map[selected].load()
|
||||
result = plugin_fn()
|
||||
except Exception:
|
||||
logger.exception("Failed to activate platform plugin: %s", selected)
|
||||
raise
|
||||
|
||||
if result is None:
|
||||
raise RuntimeError(
|
||||
f"Platform plugin {selected!r} is installed but activate() returned None "
|
||||
f"(hardware not available on this machine?)."
|
||||
)
|
||||
logger.info("OOT platform plugin activated: %s -> %s", selected, result)
|
||||
return _load_platform_class(result)()
|
||||
|
||||
# Auto-discover: import and activate all plugins, expect exactly one
|
||||
all_plugins = load_plugins_by_group(PLATFORM_PLUGINS_GROUP)
|
||||
|
||||
activated: dict[str, str] = {}
|
||||
for name, (plugin_fn, _dist) in all_plugins.items():
|
||||
try:
|
||||
result = plugin_fn()
|
||||
if result is not None:
|
||||
activated[name] = result
|
||||
logger.info("OOT platform plugin activated: %s -> %s", name, result)
|
||||
except Exception:
|
||||
logger.exception("Failed to activate platform plugin: %s", name)
|
||||
|
||||
if len(activated) == 0:
|
||||
if _is_cpu_available():
|
||||
logger.debug("SGLANG_USE_CPU_ENGINE=1. Using CPU SRTPlatform defaults.")
|
||||
return CpuSRTPlatform()
|
||||
if _is_cuda_available():
|
||||
logger.debug(
|
||||
"No platform plugin detected. Using CUDA SRTPlatform defaults."
|
||||
)
|
||||
return CudaSRTPlatform()
|
||||
if _is_rocm_available():
|
||||
logger.debug(
|
||||
"No platform plugin detected. Using ROCm SRTPlatform defaults."
|
||||
)
|
||||
return RocmSRTPlatform()
|
||||
logger.debug("No platform detected. Using base SRTPlatform.")
|
||||
return SRTPlatform()
|
||||
|
||||
if len(activated) == 1:
|
||||
name, qualname = next(iter(activated.items()))
|
||||
return _load_platform_class(qualname)()
|
||||
|
||||
# Multiple activated without SGLANG_PLATFORM
|
||||
names_str = ", ".join(f"'{n}'" for n in activated)
|
||||
raise RuntimeError(
|
||||
f"Multiple platform plugins activated: {names_str}. "
|
||||
f"Set SGLANG_PLATFORM to select one."
|
||||
)
|
||||
|
||||
|
||||
def _load_platform_class(qualname: str) -> type:
|
||||
"""Load an SRTPlatform subclass from its fully-qualified class name."""
|
||||
cls = pkgutil.resolve_name(qualname)
|
||||
if not isinstance(cls, type) or not issubclass(cls, SRTPlatform):
|
||||
raise TypeError(
|
||||
f"Expected an SRTPlatform subclass, got {type(cls)}: {qualname}"
|
||||
)
|
||||
return cls
|
||||
|
||||
|
||||
current_platform: SRTPlatform
|
||||
|
||||
|
||||
def __getattr__(name: str):
|
||||
"""Lazy initialization of current_platform on first access."""
|
||||
if name == "current_platform":
|
||||
global _current_platform
|
||||
if _current_platform is None:
|
||||
_current_platform = _resolve_platform()
|
||||
return _current_platform
|
||||
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||
@@ -0,0 +1,133 @@
|
||||
"""CPU device operations for the SRT platform layer."""
|
||||
|
||||
import gc
|
||||
import platform as _platform
|
||||
from functools import cached_property
|
||||
from typing import Optional
|
||||
|
||||
import psutil
|
||||
import torch
|
||||
|
||||
from sglang.srt.platforms.device_mixin import (
|
||||
CpuArchEnum,
|
||||
DeviceCapability,
|
||||
DeviceMixin,
|
||||
PlatformEnum,
|
||||
)
|
||||
from sglang.srt.platforms.interface import SRTPlatform
|
||||
|
||||
|
||||
class CpuDeviceMixin(DeviceMixin):
|
||||
"""CPU implementation of the shared device operations."""
|
||||
|
||||
_enum: PlatformEnum = PlatformEnum.CPU
|
||||
device_name: str = "cpu"
|
||||
device_type: str = "cpu"
|
||||
|
||||
@cached_property
|
||||
def cpu_arch(self) -> CpuArchEnum:
|
||||
"""Host CPU architecture (X86 / ARM / UNSPECIFIED), resolved once.
|
||||
|
||||
First-class identity attribute parallel to ``_enum`` — callers branch
|
||||
on CPU arch through this instead of recomputing ``platform.machine()``.
|
||||
``get_cpu_architecture()`` is process-stable, so caching is safe.
|
||||
"""
|
||||
return self.get_cpu_architecture()
|
||||
|
||||
def get_device_total_memory(self, device_id: int = 0) -> int:
|
||||
return int(psutil.virtual_memory().total)
|
||||
|
||||
def get_current_memory_usage(
|
||||
self, device: Optional["torch.device"] = None
|
||||
) -> float:
|
||||
"""Whole-machine used memory (``total - available``) in bytes.
|
||||
|
||||
Chosen so the [Active] contract
|
||||
``free = get_device_total_memory() - get_current_memory_usage()``
|
||||
yields ``psutil.available`` — the real free RAM on a machine shared
|
||||
with the OS and other processes. Per-process RSS would wrongly ignore
|
||||
their usage. There is no per-device allocator peak on CPU (unlike
|
||||
``torch.cuda.max_memory_allocated``), so this is current usage, not a
|
||||
peak. Returns whole-machine bytes; per-rank NUMA division for CPU TP
|
||||
is the caller's concern (kept in ``get_available_gpu_memory``'s CPU
|
||||
branch), not here.
|
||||
"""
|
||||
vm = psutil.virtual_memory()
|
||||
return float(vm.total - vm.available)
|
||||
|
||||
def get_device(self, local_rank: int) -> "torch.device":
|
||||
# local_rank is ignored: all CPU ranks share the one CPU device, so
|
||||
# there is nothing rank-specific to return. PyTorch enforces this —
|
||||
# Device::validate() asserts a CPU index must be -1 or 0 (c10/core/
|
||||
# Device.h). Per-rank isolation is done via OpenMP/numactl binding
|
||||
# (ModelRunner.init_threads_binding), not the device object.
|
||||
# TODO(zijiexia): make per-rank placement NUMA-affinity aware
|
||||
# (rank -> NUMA node) when the platform layer takes this over.
|
||||
return torch.device("cpu")
|
||||
|
||||
def set_device(self, device: "torch.device") -> None:
|
||||
# Documented no-op on CPU — torch.cpu.set_device is "in CPU we do
|
||||
# nothing". Called (rather than left as ``pass``) for symmetry with
|
||||
# CudaDeviceMixin.set_device. Note this is deliberately NOT
|
||||
# torch.set_default_device("cpu"), which would flip the process-wide
|
||||
# default tensor device; per-rank CPU isolation is via OpenMP/numactl
|
||||
# binding (see get_device), not here.
|
||||
torch.cpu.set_device(device)
|
||||
|
||||
def get_device_name(self, device_id: int = 0) -> str:
|
||||
# Arch-only label. We deliberately avoid platform.processor(): it
|
||||
# spawns a subprocess (~ms) on some platforms (e.g. macOS) and on Linux
|
||||
# is usually empty or redundant with the arch (e.g. "x86_64: x86_64").
|
||||
if self.cpu_arch == CpuArchEnum.ARM:
|
||||
return "cpu (aarch64)"
|
||||
if self.cpu_arch == CpuArchEnum.X86:
|
||||
return "cpu (x86_64)"
|
||||
return "cpu"
|
||||
|
||||
def get_device_uuid(self, device_id: int = 0) -> str:
|
||||
# CPU has no per-device UUID; return the arch string as a stable
|
||||
# host-level identifier (matches the multimodal CpuPlatform).
|
||||
return _platform.machine()
|
||||
|
||||
def get_device_capability(self, device_id: int = 0) -> Optional[DeviceCapability]:
|
||||
return None
|
||||
|
||||
def empty_cache(self) -> None:
|
||||
# No torch.cpu.empty_cache() exists; do a GC pass at the teardown
|
||||
# points where this is called (flush_cache, idle sleep, weight reload).
|
||||
#
|
||||
# gc.collect() caveats:
|
||||
# - the pause grows with heap size (full walk of tracked objects);
|
||||
# - it only reclaims reference cycles — refcounting already frees
|
||||
# everything else, so it may do little;
|
||||
# - freed memory returns to the allocator, not the OS, so RSS may not
|
||||
# drop. glibc malloc_trim would not help: it is a no-op under the
|
||||
# tcmalloc / TBB malloc the CPU guide preloads via LD_PRELOAD. Real
|
||||
# RSS reclaim belongs in a separate allocator-aware, benchmarked
|
||||
# change.
|
||||
gc.collect()
|
||||
|
||||
def synchronize(self) -> None:
|
||||
# Documented no-op on CPU (no async streams to drain). Called for
|
||||
# symmetry with CudaDeviceMixin's torch.cuda.synchronize().
|
||||
torch.cpu.synchronize()
|
||||
|
||||
def get_available_memory(self, device_id: int = 0) -> tuple[int, int]:
|
||||
vm = psutil.virtual_memory()
|
||||
return (vm.available, vm.total)
|
||||
|
||||
def get_torch_distributed_backend_str(self) -> str:
|
||||
return "gloo"
|
||||
|
||||
|
||||
class CpuSRTPlatform(CpuDeviceMixin, SRTPlatform):
|
||||
"""Default in-tree CPU SRT platform.
|
||||
|
||||
supports_fp8 / support_cuda_graph / support_piecewise_cuda_graph keep the
|
||||
conservative SRTPlatform defaults (all False), so they are not repeated
|
||||
here. Only is_pin_memory_available is overridden: the base defaults to
|
||||
True, but CPU has no GPU to pin host memory to.
|
||||
"""
|
||||
|
||||
def is_pin_memory_available(self) -> bool:
|
||||
return False
|
||||
@@ -0,0 +1,75 @@
|
||||
"""CUDA device operations for the SRT platform layer."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import torch
|
||||
|
||||
from sglang.srt.platforms.device_mixin import (
|
||||
DeviceCapability,
|
||||
DeviceMixin,
|
||||
PlatformEnum,
|
||||
)
|
||||
from sglang.srt.platforms.interface import SRTPlatform
|
||||
|
||||
|
||||
class CudaDeviceMixin(DeviceMixin):
|
||||
"""CUDA implementation of the shared device operations."""
|
||||
|
||||
_enum: PlatformEnum = PlatformEnum.CUDA
|
||||
device_name: str = "cuda"
|
||||
device_type: str = "cuda"
|
||||
|
||||
def get_device_total_memory(self, device_id: int = 0) -> int:
|
||||
return int(torch.cuda.get_device_properties(device_id).total_memory)
|
||||
|
||||
def get_current_memory_usage(
|
||||
self, device: Optional["torch.device"] = None
|
||||
) -> float:
|
||||
return float(torch.cuda.max_memory_allocated(device))
|
||||
|
||||
def get_device(self, local_rank: int) -> "torch.device":
|
||||
return torch.device("cuda", local_rank)
|
||||
|
||||
def set_device(self, device: "torch.device") -> None:
|
||||
torch.cuda.set_device(device)
|
||||
|
||||
def get_device_name(self, device_id: int = 0) -> str:
|
||||
return str(torch.cuda.get_device_name(device_id))
|
||||
|
||||
def get_device_uuid(self, device_id: int = 0) -> str:
|
||||
return str(torch.cuda.get_device_properties(device_id).uuid)
|
||||
|
||||
def get_device_capability(self, device_id: int = 0) -> DeviceCapability:
|
||||
major, minor = torch.cuda.get_device_capability(device_id)
|
||||
return DeviceCapability(major, minor)
|
||||
|
||||
def empty_cache(self) -> None:
|
||||
torch.cuda.empty_cache()
|
||||
|
||||
def synchronize(self) -> None:
|
||||
torch.cuda.synchronize()
|
||||
|
||||
def get_available_memory(self, device_id: int = 0) -> tuple[int, int]:
|
||||
return torch.cuda.mem_get_info(device_id)
|
||||
|
||||
def get_torch_distributed_backend_str(self) -> str:
|
||||
return "nccl"
|
||||
|
||||
@classmethod
|
||||
def seed_everything(cls, seed: int | None = None) -> None:
|
||||
if seed is not None:
|
||||
super().seed_everything(seed)
|
||||
torch.cuda.manual_seed_all(seed)
|
||||
|
||||
|
||||
class CudaSRTPlatform(CudaDeviceMixin, SRTPlatform):
|
||||
"""Default in-tree CUDA SRT platform."""
|
||||
|
||||
def supports_fp8(self) -> bool:
|
||||
return True
|
||||
|
||||
def support_cuda_graph(self) -> bool:
|
||||
return True
|
||||
|
||||
def support_piecewise_cuda_graph(self) -> bool:
|
||||
return True
|
||||
@@ -0,0 +1,255 @@
|
||||
"""
|
||||
Shared device abstraction for SGLang platforms.
|
||||
|
||||
DeviceMixin provides the common device identity queries and operations
|
||||
shared between the SRT (LLM inference) and Multimodal (diffusion)
|
||||
platform hierarchies. Concrete per-device mixins (e.g. MyDeviceMixin)
|
||||
implement the abstract operations; subsystem-specific platforms
|
||||
(SRTPlatform, MMPlatform) inherit DeviceMixin and add their own methods.
|
||||
|
||||
Hierarchy example (OOT plugin)::
|
||||
|
||||
DeviceMixin
|
||||
├── MyDeviceMixin(DeviceMixin) # vendor-specific device operations
|
||||
├── SRTPlatform(DeviceMixin) # + graph runner, KV pool, …
|
||||
│ └── MySRTPlatform(SRTPlatform, MyDeviceMixin)
|
||||
└── MMPlatform(DeviceMixin) # + attention backend, VAE, …
|
||||
└── MyMMPlatform(MMPlatform, MyDeviceMixin)
|
||||
|
||||
Method status annotations:
|
||||
|
||||
- ``[Active]`` — SGLang core calls this method through ``current_platform``.
|
||||
OOT implementations take effect immediately.
|
||||
- ``[Planned]`` — Reserved interface. SGLang core still uses hardcoded calls
|
||||
(e.g. ``torch.cuda.empty_cache()``). OOT implementations will NOT take
|
||||
effect until the core is migrated in a future PR.
|
||||
"""
|
||||
|
||||
import enum
|
||||
import random
|
||||
from typing import NamedTuple, Optional
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from sglang.srt.environ import envs
|
||||
|
||||
|
||||
class PlatformEnum(enum.Enum):
|
||||
"""Enumeration of known platform types.
|
||||
|
||||
Superset of both SRT and MM enums so that a single PlatformEnum can
|
||||
be shared across subsystems.
|
||||
"""
|
||||
|
||||
CUDA = enum.auto()
|
||||
ROCM = enum.auto()
|
||||
CPU = enum.auto()
|
||||
XPU = enum.auto()
|
||||
MUSA = enum.auto()
|
||||
NPU = enum.auto()
|
||||
TPU = enum.auto()
|
||||
MPS = enum.auto()
|
||||
OOT = enum.auto() # Out-of-tree (external plugin)
|
||||
UNSPECIFIED = enum.auto()
|
||||
|
||||
|
||||
class CpuArchEnum(enum.Enum):
|
||||
"""CPU architecture enumeration."""
|
||||
|
||||
X86 = enum.auto()
|
||||
ARM = enum.auto()
|
||||
UNSPECIFIED = enum.auto()
|
||||
|
||||
|
||||
class DeviceCapability(NamedTuple):
|
||||
"""Device compute capability (major, minor).
|
||||
|
||||
Uses NamedTuple for built-in comparison support:
|
||||
``DeviceCapability(9, 0) >= DeviceCapability(8, 9)`` works naturally.
|
||||
"""
|
||||
|
||||
major: int
|
||||
minor: int
|
||||
|
||||
def as_version_str(self) -> str:
|
||||
return f"{self.major}.{self.minor}"
|
||||
|
||||
def to_int(self) -> int:
|
||||
"""Express capability as ``<major><minor>`` (minor is single digit)."""
|
||||
assert 0 <= self.minor < 10
|
||||
return self.major * 10 + self.minor
|
||||
|
||||
|
||||
_DEVICE_TO_DISTRIBUTED_BACKEND: dict[str, str] = {
|
||||
"cuda": "nccl",
|
||||
"xpu": "xccl",
|
||||
"hpu": "hccl",
|
||||
"cpu": "gloo",
|
||||
"npu": "hccl" if not envs.SGLANG_ZBAL_LOCAL_MEM_SIZE.get() > 0 else "zbal",
|
||||
"musa": "mccl",
|
||||
}
|
||||
|
||||
|
||||
class DeviceMixin:
|
||||
"""Mixin providing device identity queries and basic device operations.
|
||||
|
||||
Class-level attributes (override in subclasses):
|
||||
_enum: PlatformEnum identifying this platform.
|
||||
device_name: Human-readable short name (e.g. "cuda", "npu").
|
||||
device_type: ``torch.device`` type string (e.g. "cuda", "npu").
|
||||
"""
|
||||
|
||||
_enum: PlatformEnum = PlatformEnum.UNSPECIFIED
|
||||
device_name: str = "unknown"
|
||||
device_type: str = "cpu"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Platform identity queries
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def is_cuda(self) -> bool:
|
||||
return self._enum == PlatformEnum.CUDA
|
||||
|
||||
def is_rocm(self) -> bool:
|
||||
return self._enum == PlatformEnum.ROCM
|
||||
|
||||
def is_cpu(self) -> bool:
|
||||
return self._enum == PlatformEnum.CPU
|
||||
|
||||
def is_xpu(self) -> bool:
|
||||
return self._enum == PlatformEnum.XPU
|
||||
|
||||
def is_musa(self) -> bool:
|
||||
return self._enum == PlatformEnum.MUSA
|
||||
|
||||
def is_npu(self) -> bool:
|
||||
return self._enum == PlatformEnum.NPU
|
||||
|
||||
def is_tpu(self) -> bool:
|
||||
return self._enum == PlatformEnum.TPU
|
||||
|
||||
def is_mps(self) -> bool:
|
||||
return self._enum == PlatformEnum.MPS
|
||||
|
||||
def is_cuda_alike(self) -> bool:
|
||||
"""True for CUDA, ROCm, or MUSA (all expose CUDA-like APIs)."""
|
||||
return self._enum in (
|
||||
PlatformEnum.CUDA,
|
||||
PlatformEnum.ROCM,
|
||||
PlatformEnum.MUSA,
|
||||
)
|
||||
|
||||
def is_out_of_tree(self) -> bool:
|
||||
"""True for externally-registered OOT platforms."""
|
||||
return self._enum == PlatformEnum.OOT
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Active methods — core calls these through current_platform.
|
||||
# OOT implementations take effect immediately.
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def get_device_total_memory(self, device_id: int = 0) -> int:
|
||||
"""[Active] Get total device memory in bytes."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_current_memory_usage(
|
||||
self, device: Optional["torch.device"] = None
|
||||
) -> float:
|
||||
"""[Active] Get current peak memory usage in bytes."""
|
||||
raise NotImplementedError
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Planned methods — reserved interface. Core still uses hardcoded
|
||||
# calls (e.g. torch.cuda.*). OOT implementations will NOT take
|
||||
# effect until the core is migrated in a future PR.
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# ---- Device management ----
|
||||
|
||||
def get_device(self, device_id: int = 0) -> str:
|
||||
"""[Planned] Return ``torch.device`` for the given device id."""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_device(self, device: "torch.device") -> None:
|
||||
"""[Planned] Set the current device."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_device_name(self, device_id: int = 0) -> str:
|
||||
"""[Planned] Get human-readable device name."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_device_uuid(self, device_id: int = 0) -> str:
|
||||
"""[Planned] Get unique device identifier string."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_device_capability(self, device_id: int = 0) -> Optional["DeviceCapability"]:
|
||||
"""[Planned] Get device compute capability. None if N/A."""
|
||||
raise NotImplementedError
|
||||
|
||||
def empty_cache(self) -> None:
|
||||
"""[Planned] Release cached device memory. No-op for CPU-like platforms."""
|
||||
pass
|
||||
|
||||
def synchronize(self) -> None:
|
||||
"""[Planned] Synchronize device operations. No-op for CPU-like platforms."""
|
||||
pass
|
||||
|
||||
# ---- Memory ----
|
||||
|
||||
def get_available_memory(self, device_id: int = 0) -> tuple[int, int]:
|
||||
"""[Planned] Return ``(free_bytes, total_bytes)``."""
|
||||
raise NotImplementedError
|
||||
|
||||
# ---- Distributed ----
|
||||
|
||||
def get_torch_distributed_backend_str(self) -> str:
|
||||
"""Return the torch.distributed backend string (e.g. "nccl", "hccl").
|
||||
|
||||
Default: lookup ``self.device_type`` in ``_DEVICE_TO_DISTRIBUTED_BACKEND``,
|
||||
falling back to ``"gloo"``. Subclasses override only when they need a
|
||||
non-default backend (e.g. mooncake, or a brand-new device).
|
||||
"""
|
||||
return _DEVICE_TO_DISTRIBUTED_BACKEND.get(self.device_type, "gloo")
|
||||
|
||||
def get_communicator_class(self) -> type | None:
|
||||
"""[Planned] Return platform-specific communicator class, or None for default."""
|
||||
return None
|
||||
|
||||
# ---- Misc ----
|
||||
|
||||
@classmethod
|
||||
def inference_mode(cls):
|
||||
"""[Planned] Return inference mode context manager."""
|
||||
return torch.inference_mode(mode=True)
|
||||
|
||||
@classmethod
|
||||
def seed_everything(cls, seed: int | None = None) -> None:
|
||||
"""[Planned] Set random seeds for reproducibility across all libraries."""
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
|
||||
def verify_quantization(self, quant: str) -> None:
|
||||
"""[Planned] Validate that a quantization method is supported. No-op by default."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def get_cpu_architecture(cls) -> "CpuArchEnum":
|
||||
"""[Planned] Detect CPU architecture."""
|
||||
import platform as _platform
|
||||
|
||||
machine = _platform.machine().lower()
|
||||
if machine in ("x86_64", "amd64", "i386", "i686"):
|
||||
return CpuArchEnum.X86
|
||||
elif machine in ("arm64", "aarch64"):
|
||||
return CpuArchEnum.ARM
|
||||
return CpuArchEnum.UNSPECIFIED
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Dunder helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(device={self.device_name})"
|
||||
@@ -0,0 +1,143 @@
|
||||
"""
|
||||
SGLang SRT Hardware Platform Abstraction.
|
||||
|
||||
Defines SRTPlatform — the base class for SRT (LLM inference) platform
|
||||
backends. SRTPlatform inherits DeviceMixin for shared device operations
|
||||
and adds SRT-specific subsystem factory methods, capability flags, and
|
||||
configuration lifecycle hooks.
|
||||
|
||||
Out-of-tree platforms register via setuptools entry_points under the
|
||||
"sglang.srt.platforms" group and should subclass SRTPlatform.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Optional, Type
|
||||
|
||||
from sglang.srt.platforms.device_mixin import DeviceMixin, PlatformEnum
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sglang.srt.layers.quantization.base_config import QuantizationConfig
|
||||
|
||||
# Re-export for convenience
|
||||
__all__ = ["SRTPlatform", "PlatformEnum"]
|
||||
|
||||
|
||||
class SRTPlatform(DeviceMixin):
|
||||
"""
|
||||
Base class for SRT hardware platform backends.
|
||||
|
||||
Inherits device identity queries and operations from DeviceMixin.
|
||||
Adds SRT-specific factory methods, capability flags, and lifecycle hooks.
|
||||
|
||||
OOT platforms should subclass SRTPlatform and override the methods
|
||||
relevant to their hardware.
|
||||
"""
|
||||
|
||||
# SRT-specific class-level attribute
|
||||
supported_quantization: list[str] = []
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Configuration lifecycle
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def apply_server_args_defaults(self, server_args) -> None:
|
||||
"""Apply platform-specific default values to server arguments.
|
||||
|
||||
Called after ServerArgs is parsed.
|
||||
"""
|
||||
pass
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Subsystem factory methods
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def get_default_attention_backend(self) -> str:
|
||||
"""Return the default attention backend name for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_graph_runner_cls(self) -> type:
|
||||
"""Return the graph runner class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_mha_kv_pool_cls(self) -> type:
|
||||
"""Return the MHA KV pool class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_mla_kv_pool_cls(self) -> type:
|
||||
"""Return the MLA KV pool class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_dsa_kv_pool_cls(self) -> type:
|
||||
"""Return the DSA KV pool class for this platform (DeepSeek V3.2)."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_paged_allocator_cls(self) -> type:
|
||||
"""Return the paged allocator class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_compile_backend(self, mode: str | None = None) -> str:
|
||||
"""Return the compilation backend identifier.
|
||||
|
||||
``mode`` is an optional hint for the platform (e.g. "npugraph_ex").
|
||||
"""
|
||||
return "inductor"
|
||||
|
||||
def get_piecewise_backend_cls(self) -> type:
|
||||
"""Return the piecewise compilation backend class for this platform."""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_quantization_config(
|
||||
self, quantization: str
|
||||
) -> Optional[Type[QuantizationConfig]]:
|
||||
"""Return hardware-specific quantization config for the specific
|
||||
quantization scheme, raise an error if not supported or return None
|
||||
to use the default config."""
|
||||
return None
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Capability flags (safe conservative defaults)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def supports_fp8(self) -> bool:
|
||||
"""Whether this platform supports FP8 quantization."""
|
||||
return False
|
||||
|
||||
def is_pin_memory_available(self) -> bool:
|
||||
"""Whether pinned memory is available on this platform."""
|
||||
return True
|
||||
|
||||
def support_cuda_graph(self) -> bool:
|
||||
"""Whether this platform supports device graph capture and replay.
|
||||
Controls CUDA graph (CudaGraphRunner) for the decode path.
|
||||
OOT platforms that support graph-style capture should return True.
|
||||
"""
|
||||
return False
|
||||
|
||||
def support_piecewise_cuda_graph(self) -> bool:
|
||||
"""Whether this platform supports piecewise CUDA graph.
|
||||
|
||||
Controls PiecewiseCudaGraphRunner for the prefill/extend path
|
||||
(torch.compile backend).
|
||||
"""
|
||||
return False
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Initialization
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def init_backend(self) -> None:
|
||||
"""One-time backend initialization. Called in each worker."""
|
||||
pass
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# MultiPlatformOp integration
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def get_dispatch_key_name(self) -> str:
|
||||
"""Return the dispatch key name for MultiPlatformOp.
|
||||
|
||||
Determines which ``forward_<key>()`` method is selected.
|
||||
E.g. "cuda", "npu", "hip", "xpu", "cpu".
|
||||
"""
|
||||
return "native"
|
||||
@@ -0,0 +1,31 @@
|
||||
"""ROCm device operations for the SRT platform layer.
|
||||
|
||||
PyTorch exposes ROCm through the same ``torch.cuda.*`` API surface as CUDA
|
||||
(HIP is a binary shim, and ``torch.device("rocm")`` does not exist). So
|
||||
``RocmDeviceMixin`` inherits all device ops from ``CudaDeviceMixin`` and
|
||||
only overrides identity (``_enum``, ``device_name``).
|
||||
"""
|
||||
|
||||
from sglang.srt.platforms.cuda import CudaDeviceMixin
|
||||
from sglang.srt.platforms.device_mixin import PlatformEnum
|
||||
from sglang.srt.platforms.interface import SRTPlatform
|
||||
|
||||
|
||||
class RocmDeviceMixin(CudaDeviceMixin):
|
||||
"""ROCm device ops — identical surface to CUDA via torch.cuda's HIP shim."""
|
||||
|
||||
_enum: PlatformEnum = PlatformEnum.ROCM
|
||||
device_name: str = "rocm"
|
||||
# device_type stays "cuda" — torch.device("cuda") is the only valid
|
||||
# device-type string for HIP devices in PyTorch.
|
||||
|
||||
|
||||
class RocmSRTPlatform(RocmDeviceMixin, SRTPlatform):
|
||||
"""Default in-tree ROCm SRT platform.
|
||||
|
||||
Capability flags (supports_fp8, support_cuda_graph, support_piecewise_cuda_graph)
|
||||
keep the conservative SRTPlatform defaults rather than mirroring CudaSRTPlatform.
|
||||
They are currently only consulted in OOT branches gated on is_out_of_tree(),
|
||||
so the defaults are behaviorally inert for the in-tree ROCm path. A follow-up
|
||||
that migrates AMD-specific gating off legacy is_hip() should set these here.
|
||||
"""
|
||||
Reference in New Issue
Block a user