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

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:16 +08:00
commit 94057c3d3e
7152 changed files with 2120455 additions and 0 deletions
+164
View File
@@ -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}")
+133
View File
@@ -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
+75
View File
@@ -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
+255
View File
@@ -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})"
+143
View File
@@ -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"
+31
View File
@@ -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.
"""