platforms/interface.py: the Platform base class
Code version: vLLM @ 3e44c0d · 2026-10-22
Purpose#
vllm/platforms/interface.py defines the contract that every hardware platform implements: the base class Platform with a default for almost every method.
| File | vllm/platforms/interface.py, 1495 lines of Python. Three small types, three module functions, the class Platform (lines 137-1490) and UnspecifiedPlatform. |
|---|---|
| Main class | Platform (vllm/platforms/interface.py:137): 12 class attributes and 90 methods, 84 of them without a leading underscore. Most methods are class methods. |
| Runs in | Every vLLM process, through its current_platform object. The methods run on the CPU, in the thread of the caller. They must not start a device context unless their job needs one. |
| Called by | vLLM code all over the tree, through current_platform: the config code in the front end, the engine core, the workers, the layers and the compiler (see the hook table on The Platform interface). |
| Calls | torch (dtypes, torch.<device_type>), os.environ for the device-control variable, and lazy imports of attention backends, KV cache specs and config classes inside some methods. |
| Not in this file | The detection of the platform (__init__.py, page platforms/__init__.py: platform detection and current_platform) and the vendor classes (cuda.py, rocm.py, cpu.py, xpu.py, tpu.py, zen_cpu.py). |
The page The Platform interface explains the hooks in start-up order. This page maps the file and reads its most important methods.
Where it sits#
Who calls it#
- The config code (front end).
EngineArgscallspre_register_and_updateandvalidate_environ(vllm/engine/arg_utils.py:2246-2250).VllmConfig.__post_init__callsapply_config_platform_defaultsandcheck_and_update_config(vllm/config/vllm.py:1804,vllm/config/vllm.py:2046). - The executors. They call
update_block_size_for_backendafter the model is loaded (vllm/v1/executor/multiproc_executor.py:702,vllm/v1/executor/uniproc_executor.py:75). - Component builders. The attention selector (
vllm/v1/attention/selector.py:218), the process groups (vllm/distributed/parallel_state.py:551), the LoRA wrapper selector (vllm/lora/punica_wrapper/punica_selector.py:14), the compiler (vllm/compilation/backends.py:691,vllm/compilation/backends.py:876) and the KV cache planner (vllm/v1/core/kv_cache_config_builder.py:101). - Subclasses. Vendor classes call
super()for some methods, for example ROCm forimport_kernels(vllm/platforms/rocm.py:654) and XPU forupdate_block_size_for_backend(vllm/platforms/xpu.py:477). - The worker process. It calls
set_assigned_physical_gpu_idswhen the config has assigned GPU ids (vllm/v1/executor/multiproc_executor.py:889-895).
What it calls#
torch.<device_type>in the fallback__getattr__(vllm/platforms/interface.py:1306-1312).- The device-control variable, for example
CUDA_VISIBLE_DEVICES, in the device-id helpers (vllm/platforms/interface.py:307-313). - Attention backend classes inside the block size methods (
vllm/platforms/interface.py:678-697) and the MLA prefill registry (vllm/platforms/interface.py:453-456). vllm.envs.environment_variablesinvalidate_environ(vllm/platforms/interface.py:1457-1460).- Kernel libraries:
vllm._C(vllm/platforms/interface.py:369) andvllm.kernels(vllm/platforms/interface.py:264).
Key classes and functions#
Map of the file#
| Lines | Section |
|---|---|
vllm/platforms/interface.py:1-35 | Imports; type-only imports of config, attention and request types. |
vllm/platforms/interface.py:37-62 | _assigned_physical_gpu_ids with set_assigned_physical_gpu_ids (raises on a conflicting second call) and get_assigned_physical_gpu_ids. |
vllm/platforms/interface.py:65-68 | in_wsl(), cached: "microsoft" in platform.uname(). |
vllm/platforms/interface.py:71-90 | PlatformEnum (CUDA, ROCM, TPU, XPU, CPU, OOT, UNSPECIFIED) and CpuArchEnum. |
vllm/platforms/interface.py:93-134 | DeviceCapability(major, minor) with ordering and to_int() (for example 9.0 → 90). |
vllm/platforms/interface.py:137-181 | Platform: identity class attributes. |
vllm/platforms/interface.py:183-244 | Properties and the is_* checks. |
vllm/platforms/interface.py:246-264 | Compile pickers and import_ir_kernels. |
vllm/platforms/interface.py:266-363 | Device ids: logical, visible and physical. |
vllm/platforms/interface.py:365-386 | Kernel hooks. |
vllm/platforms/interface.py:388-514 | Attention pickers: decoder, vision encoder, MLA prefill. |
vllm/platforms/interface.py:516-636 | Device queries: capability, name, memory, device setting, seeds. |
vllm/platforms/interface.py:638-676 | Config hooks. |
vllm/platforms/interface.py:678-1134 | Block size: the common size for all backends, hybrid (Mamba) and mixed-dtype alignment. |
vllm/platforms/interface.py:1136-1298 | Checks and feature flags: KV specs, model and quantization checks, CPU architecture, memory, LoRA wrapper, communicator, FP8, collectives, request check. |
vllm/platforms/interface.py:1300-1326 | The __getattr__ fallback and the global graph pool. |
vllm/platforms/interface.py:1328-1490 | Graph wrapper, process groups, more feature flags, NIXL, IR priority, validate_environ, KV cache config builder. |
vllm/platforms/interface.py:1493-1495 | UnspecifiedPlatform: _enum = UNSPECIFIED, device_type = "". |
Every public method of Platform#
One row per method, in file order. "Picker" means a component picker; "config hook" and "kernel hook" are the groups of The Platform interface. The private helpers (_find_non_ssm_backends, _preferred_block_size_for_backends, _check_aligned_block_size and two alignment methods) are in the walk-through and the deep dive.
| Name | Kind | Lines | What it does |
|---|---|---|---|
log_warnings | classmethod | vllm/platforms/interface.py:153-154 | Logs platform diagnostics after logging is configured. Default: nothing. |
pass_key | property | vllm/platforms/interface.py:184-186 | Property: the Inductor config key for the custom pass manager (post_grad_custom_post_pass). |
supported_dtypes | property | vllm/platforms/interface.py:189-194 | Property: model dtypes the device supports; the first one is the fallback for auto. |
is_cuda | method | vllm/platforms/interface.py:196-197 | True for PlatformEnum.CUDA. |
is_rocm | method | vllm/platforms/interface.py:199-200 | True for PlatformEnum.ROCM. |
is_tpu | method | vllm/platforms/interface.py:202-203 | True for PlatformEnum.TPU. |
is_xpu | method | vllm/platforms/interface.py:205-206 | True for PlatformEnum.XPU. |
is_cpu | method | vllm/platforms/interface.py:208-209 | True for PlatformEnum.CPU. |
uses_host_device_handling | method | vllm/platforms/interface.py:211-213 | Whether vLLM leaves DeviceConfig.device unset (TPU only by default). |
is_zen_cpu | method | vllm/platforms/interface.py:215-216 | True only on ZenCpuPlatform; default False. |
is_out_of_tree | method | vllm/platforms/interface.py:218-219 | True for PlatformEnum.OOT; selects forward_oot in an enabled CustomOp. |
is_unspecified | method | vllm/platforms/interface.py:221-222 | True for UnspecifiedPlatform. |
get_max_output_tokens | method | vllm/platforms/interface.py:224-225 | Upper limit of output tokens for a prompt length; default sys.maxsize. |
is_cuda_alike | method | vllm/platforms/interface.py:227-229 | True for CUDA and ROCm, without a CUDA call. |
is_sleep_mode_available | method | vllm/platforms/interface.py:231-236 | True for CUDA, ROCm and XPU. |
is_cumem_allocator_available | method | vllm/platforms/interface.py:238-244 | True when vllm.device_allocator.cumem imports and reports it is available. |
get_pass_manager_cls | classmethod | vllm/platforms/interface.py:247-251 | Picker: dotted name of the post-grad pass manager. |
get_compile_backend | classmethod | vllm/platforms/interface.py:254-256 | Picker: the compile backend for simple functions; default simple_compile_backend. |
import_ir_kernels | classmethod | vllm/platforms/interface.py:259-264 | Kernel hook: imports vllm.kernels (IR op implementations). |
device_control_id_to_physical_device_id | classmethod | vllm/platforms/interface.py:267-275 | Turns one entry of the device-control variable into an int; non-integers raise ValueError. |
device_id_to_physical_device_id | classmethod | vllm/platforms/interface.py:288-315 | Logical (local rank) id to physical id, through assigned ids or the device-control variable. |
logical_device_id_to_visible_device_id | classmethod | vllm/platforms/interface.py:318-341 | Logical id to the ordinal that torch.device uses in this process. |
visible_device_id_to_physical_device_id | classmethod | vllm/platforms/interface.py:344-363 | Visible ordinal back to a physical id. |
import_kernels | classmethod | vllm/platforms/interface.py:366-373 | Kernel hook: imports vllm._C and the optional MoE library. |
register_triton_kernel_overrides | classmethod | vllm/platforms/interface.py:376-386 | Kernel hook: replaces core Triton kernels; default does nothing. |
get_attn_backend_cls | classmethod | vllm/platforms/interface.py:389-396 | Picker: dotted name of the attention backend; default "". |
get_supported_vit_attn_backends | classmethod | vllm/platforms/interface.py:399-402 | Vision-encoder attention backends that the platform allows; default [TORCH_SDPA]. |
get_vit_attn_backend | classmethod | vllm/platforms/interface.py:405-432 | Picks the vision-encoder attention backend; default TORCH_SDPA. |
get_mla_prefill_backend_cls | classmethod | vllm/platforms/interface.py:435-514 | Picks the MLA prefill backend class from a capability-based priority list. |
get_device_capability | classmethod | vllm/platforms/interface.py:517-528 | (major, minor) without a CUDA context; default None. |
has_device_capability | classmethod | vllm/platforms/interface.py:531-551 | True when the capability is at least the given one; False when unknown. |
is_device_capability | classmethod | vllm/platforms/interface.py:554-574 | True when the capability is exactly the given one. |
is_device_capability_family | classmethod | vllm/platforms/interface.py:577-588 | True when the major version matches (for example any 10.x). |
get_device_name | classmethod | vllm/platforms/interface.py:591-593 | Device name; base raises NotImplementedError. |
get_device_uuid | classmethod | vllm/platforms/interface.py:596-598 | Device UUID; base raises NotImplementedError. |
get_device_total_memory | classmethod | vllm/platforms/interface.py:601-603 | Total device memory in bytes; base raises NotImplementedError. |
get_all_gpu_pci_bus_ids | classmethod | vllm/platforms/interface.py:606-616 | Device index to PCI bus id, for VLLM_GPU_NIC_PCIE_MAPPING; base raises. |
inference_mode | classmethod | vllm/platforms/interface.py:619-626 | Context manager; default torch.inference_mode(True). |
set_device | classmethod | vllm/platforms/interface.py:629-631 | Sets the current device; base raises NotImplementedError. |
manual_seed_all | classmethod | vllm/platforms/interface.py:634-636 | Seeds all devices; base raises NotImplementedError. |
pre_register_and_update | classmethod | vllm/platforms/interface.py:639-651 | Config hook: before the flags are parsed (and again in create_engine_config). |
apply_config_platform_defaults | classmethod | vllm/platforms/interface.py:654-664 | Config hook: platform defaults in VllmConfig.__post_init__. |
check_and_update_config | classmethod | vllm/platforms/interface.py:667-676 | Config hook: check or change VllmConfig; sets worker_cls. |
update_block_size_for_backend | classmethod | vllm/platforms/interface.py:750-794 | Config hook after model load: a block size that all attention backends accept. |
register_custom_kv_cache_specs | classmethod | vllm/platforms/interface.py:1137-1139 | Registers platform KV cache spec classes; default nothing. |
verify_model_arch | classmethod | vllm/platforms/interface.py:1142-1150 | Raises or warns for an unsupported architecture; default accepts all. |
verify_quantization | classmethod | vllm/platforms/interface.py:1153-1158 | Raises ValueError if supported_quantization is set and lacks the method. |
get_cpu_architecture | classmethod | vllm/platforms/interface.py:1161-1178 | CpuArchEnum from platform.machine(). |
is_pin_memory_available | classmethod | vllm/platforms/interface.py:1181-1194 | True, except under WSL (warning, False). |
get_current_memory_usage | classmethod | vllm/platforms/interface.py:1197-1201 | Memory in use in bytes; base raises NotImplementedError. |
get_punica_wrapper | classmethod | vllm/platforms/interface.py:1204-1206 | Picker: LoRA kernel wrapper class; base raises NotImplementedError. |
get_infinity_values | classmethod | vllm/platforms/interface.py:1209-1211 | The values that stand for minus and plus infinity for a dtype. |
can_update_inplace | classmethod | vllm/platforms/interface.py:1214-1216 | Whether in-place memory updates are allowed; default True. |
get_device_communicator_cls | classmethod | vllm/platforms/interface.py:1219-1221 | Picker: device communicator class; default DeviceCommunicatorBase. |
is_integrated_gpu | classmethod | vllm/platforms/interface.py:1224-1232 | Whether the GPU shares system memory (UMA); default False. |
enable_multi_stream_overlap | classmethod | vllm/platforms/interface.py:1235-1242 | Whether to overlap auxiliary streams; default False. |
supports_mx | classmethod | vllm/platforms/interface.py:1245-1247 | MX formats supported; default False. |
supports_fp8 | classmethod | vllm/platforms/interface.py:1250-1252 | FP8 supported; default False. |
is_fp8_fnuz | classmethod | vllm/platforms/interface.py:1255-1265 | Whether the preferred FP8 type is FNUZ; default False. |
fp8_dtype | classmethod | vllm/platforms/interface.py:1268-1273 | Preferred FP8 dtype; default torch.float8_e4m3fn. |
use_all_gather | classmethod | vllm/platforms/interface.py:1276-1278 | Whether the logits processor gathers logits with all-gather; default True. |
use_custom_allreduce | classmethod | vllm/platforms/interface.py:1281-1283 | Whether vLLM's custom all-reduce is supported; default False. |
opaque_attention_op | classmethod | vllm/platforms/interface.py:1286-1290 | Whether attention is one opaque custom op for torch.compile; default False. |
validate_request | classmethod | vllm/platforms/interface.py:1293-1298 | Raises for a request that the platform cannot serve; default accepts all. |
__getattr__ | method | vllm/platforms/interface.py:1300-1319 | Fallback: an unknown name is looked up in torch.<device_type>; else warning and None. |
get_global_graph_pool | method | vllm/platforms/interface.py:1321-1326 | One graph memory pool per platform class, made on first use. |
get_static_graph_wrapper_cls | classmethod | vllm/platforms/interface.py:1329-1331 | Picker: the static graph (CUDA graph) wrapper class. |
stateless_init_device_torch_dist_pg | classmethod | vllm/platforms/interface.py:1334-1343 | Makes a device process group without global state; base raises. |
check_if_supports_dtype | classmethod | vllm/platforms/interface.py:1346-1348 | Raises for an unsupported dtype; base raises NotImplementedError. |
support_hybrid_kv_cache | classmethod | vllm/platforms/interface.py:1351-1353 | Hybrid KV cache supported; default False. |
support_static_graph_mode | classmethod | vllm/platforms/interface.py:1356-1358 | Graph mode supported; default False. |
check_runner_kv_caches_multi_layer | classmethod | vllm/platforms/interface.py:1361-1374 | Raises unless the runner handles two attention layers with one layer index. |
support_deep_gemm | classmethod | vllm/platforms/interface.py:1377-1379 | DeepGEMM supported; default False. |
use_custom_op_collectives | classmethod | vllm/platforms/interface.py:1382-1387 | Use torch.ops.vllm.* collectives; default False. |
use_sync_weight_loader | classmethod | vllm/platforms/interface.py:1390-1392 | Whether the weight loader must synchronize; default False. |
make_synced_weight_loader | classmethod | vllm/platforms/interface.py:1395-1406 | Wraps a weight loader with torch._sync when the line above is true. |
get_nixl_supported_devices | classmethod | vllm/platforms/interface.py:1409-1413 | NIXL buffer devices per device type; default {}. |
get_nixl_memory_type | classmethod | vllm/platforms/interface.py:1416-1418 | NIXL memory type; default None. |
check_max_model_len | classmethod | vllm/platforms/interface.py:1421-1423 | May lower max_model_len; default returns it unchanged. |
set_additional_forward_context | classmethod | vllm/platforms/interface.py:1426-1428 | Extra forward-context fields; default {}. |
num_compute_units | classmethod | vllm/platforms/interface.py:1431-1437 | Number of SMs, CUs or EUs; base raises NotImplementedError. |
get_default_ir_op_priority | classmethod | vllm/platforms/interface.py:1440-1447 | Default IR op provider priority; default ["native"]. |
is_arch_support_pdl | classmethod | vllm/platforms/interface.py:1450-1452 | Programmatic Dependent Launch supported; default False. |
validate_environ | classmethod | vllm/platforms/interface.py:1455-1468 | Warns (or raises) for unknown VLLM_* variables. |
get_kv_cache_config_builder_cls | classmethod | vllm/platforms/interface.py:1471-1490 | Picker: KV cache config builder class (experimental). |
Class attributes (not in the table): _enum, device_name, device_type, dispatch_key, ray_device_key, device_control_env_var, ray_noset_device_env_vars, simple_compile_backend, dist_backend, supported_quantization, additional_env_vars and _global_graph_pool (vllm/platforms/interface.py:138-181).
Walk through the main methods#
The three config hooks#
@classmethod
def pre_register_and_update(
cls, parser: FlexibleArgumentParser | None = None
) -> None:
"""Do some pre-registration or update action for the current platform.
This function is called before global VllmConfig is initialized or cli
arguments are parsed. It's used for out-of-tree platforms to register or
update the configuration.
For example, the out-of-tree quantization config can be imported and
registered here dynamically.
"""
pass
@classmethod
def apply_config_platform_defaults(cls, vllm_config: "VllmConfig") -> None:
"""Apply the platform-specific default values to the config.
This function is called during the initialization of global VllmConfig, after
parsing cli arguments.
It can modify the defaults of the config according to the platform. For example,
it can enable custom_ops based on the enabled features.
The config is passed by reference, so it can be modified in place.
"""
pass
@classmethod
def check_and_update_config(cls, vllm_config: "VllmConfig") -> None:
"""Check and update the configuration for the current platform.
It can raise an exception if the configuration is not compatible with
the current platform, or it can update the configuration to make it
compatible with the current platform.
The config is passed by reference, so it can be modified in place.
"""
pass- Lines 639-651.
pre_register_and_updategets the CLI parser, orNone. The docstring names the use case: an out-of-tree quantization config. It is called twice byvllm serve(vllm/engine/arg_utils.py:3257,vllm/engine/arg_utils.py:2246). - Lines 654-664.
apply_config_platform_defaultsruns "after parsing cli arguments" and can change defaults, for examplecustom_ops. - Lines 667-676.
check_and_update_configcan raise or change the config "in place". All three returnNone; their effect is the change to the object.
update_block_size_for_backend: one block size for all backends#
@classmethod
def update_block_size_for_backend(cls, vllm_config: "VllmConfig") -> None:
"""Ensure block_size is compatible with the attention backend.
For hybrid models, also aligns block_size with mamba page sizes.
"""
from vllm.config.cache import CacheConfig
cache_config = vllm_config.cache_config
model_config = vllm_config.model_config
# model_config may be None during testing.
if not model_config:
return
backend_classes = cls._find_non_ssm_backends(vllm_config)
if not backend_classes:
return
# Phase 1: Pick a block size every attention backend supports (skip if
# user set --block-size). Models can mix backends with disjoint
# preferences, and a size from the first alone later fails
# select_common_block_size().
if not cache_config.user_specified_block_size:
preferred = cls._preferred_block_size_for_backends(
backend_classes, CacheConfig.DEFAULT_BLOCK_SIZE, vllm_config
)
if preferred != CacheConfig.DEFAULT_BLOCK_SIZE:
logger.info(
"Setting kv cache block size to %d for %s backend(s).",
preferred,
"/".join(b.get_name() for b in backend_classes),
)
cache_config.block_size = preferred
# Phase 2: Align block/mamba sizes for hybrid models
# (may override user settings).
pre_block_size = cache_config.block_size
if model_config.is_hybrid:
cls._align_hybrid_block_size(vllm_config, backend_classes[0])
# Phase 3: Align block/page sizes when multiple KV dtypes share the
# block pool (e.g. nvfp4 primary + unquantized skip layers).
# May override the user's --block-size.
if cache_config.kv_cache_dtype_skip_layers:
cls._align_heterogeneous_kv_block_size(vllm_config, backend_classes[0])
cls._check_aligned_block_size(vllm_config, backend_classes, pre_block_size)- Lines 760-765. Return early with no model config, or with no attention backend except SSM ones.
_find_non_ssm_backendsreads the backend of each attention layer, so the model must be built first (vllm/platforms/interface.py:678-697). - Lines 771-781. Phase 1, only when the user did not set
--block-size: pick a size that every backend accepts, with the default 16 (CacheConfig.DEFAULT_BLOCK_SIZE) as the start value (next section). Log at INFO when the result is not the default. - Lines 785-787. Phase 2, hybrid models (attention + Mamba): align the block and Mamba page sizes. The comment says that this may override the user's setting.
- Lines 792-794. Phase 3, KV cache dtype skip layers: align pages of mixed dtypes. Then check that the aligned size is still valid for every backend (
vllm/platforms/interface.py:796-823).
_preferred_block_size_for_backends: the common size#
from vllm.config.vllm import set_current_vllm_config
from vllm.v1.attention.backend import MultipleOf
# Backends may read the current config to decide a preference.
with set_current_vllm_config(vllm_config):
if len(backend_classes) == 1:
return backend_classes[0].get_preferred_block_size(default_block_size)
if all(b.supports_block_size(default_block_size) for b in backend_classes):
return default_block_size
# A backend declaring no sizes accepts any, so it contributes 1.
per_backend_sizes = [
[
s.base if isinstance(s, MultipleOf) else s
for s in b.get_supported_kernel_block_sizes()
]
or [1]
for b in backend_classes
]
candidates = sorted(
{math.lcm(*sizes) for sizes in itertools.product(*per_backend_sizes)}
)
for candidate in candidates:
if all(b.supports_block_size(candidate) for b in backend_classes):
return candidate
raise ValueError(
"The attention backends share no supported KV cache block size ("
+ "; ".join(
f"{b.get_name()}: {b.get_supported_kernel_block_sizes()}"
for b in backend_classes
)
+ ")."
)- Lines 721-722. One backend: ask it for its preferred size.
- Lines 723-724. All backends accept the default: use it.
- Lines 726-736. Take one supported size per backend (the base of a
MultipleOf, or a fixed size; no sizes means 1). Every combination gives one candidate, the least common multiple. Sort the candidates. - Lines 737-739. Return the first candidate that every backend accepts.
- Line 740. None fits:
ValueErrorwith each backend's sizes.
Device ids: logical to physical#
@classmethod
def device_id_to_physical_device_id(cls, device_id: int):
"""Map a vLLM-local logical device ID to a physical device ID.
The input is a logical local ID (e.g. a local rank), NOT a visible
device ordinal; for the latter use
visible_device_id_to_physical_device_id(). The two coincide only
when no logical-to-physical mapping is in effect.
"""
if _assigned_physical_gpu_ids is not None:
if device_id >= len(_assigned_physical_gpu_ids):
raise IndexError(
f"device_id {device_id} is out of range for "
f"assigned_physical_gpu_ids {_assigned_physical_gpu_ids} "
f"({len(_assigned_physical_gpu_ids)} devices assigned)"
)
return _assigned_physical_gpu_ids[device_id]
# Treat empty device control env var as unset. This is a valid
# configuration in Ray setups where the engine is launched in
# a CPU-only placement group located on a GPU node.
if (
cls.device_control_env_var in os.environ
and os.environ[cls.device_control_env_var] != ""
):
device_ids = os.environ[cls.device_control_env_var].split(",")
physical_device_id = device_ids[device_id]
return cls.device_control_id_to_physical_device_id(physical_device_id)
else:
return device_id- Lines 296-303. If the worker set assigned GPU ids, the logical id (a local rank) indexes that list. An id past its end raises
IndexError. - Lines 307-313. Else, if the device-control variable is set and not empty, the logical id indexes its comma list. Example:
CUDA_VISIBLE_DEVICES=4,5and local rank 1 give physical device 5. - Lines 314-315. Else the ids are the same.
The comment above the method names three id spaces: logical, visible and physical (vllm/platforms/interface.py:277-285). torch.device("cuda:N") needs a visible id; NVML needs a physical id.
Kernel hooks#
@classmethod
def import_kernels(cls) -> None:
"""Import any platform-specific C kernels."""
try:
import vllm._C # noqa: F401
except ImportError as e:
logger.warning_once("Failed to import from vllm._C: %s", repr(e))
with contextlib.suppress(ImportError):
import vllm._moe_C_stable_libtorch # noqa: F401
@classmethod
def register_triton_kernel_overrides(cls) -> None:
"""Override core Triton kernels with platform implementations.
Platforms whose Triton kernels need replacement implementations
(e.g. CPU fallbacks when the Triton-CPU backend is unavailable)
override this method and call
:func:`vllm.triton_utils.dispatcher.register_kernels`
with their overrides. It runs during model runner init, before any
kernel launch. The default implementation does nothing.
"""
return- Lines 368-371. A failed import of
vllm._Clogs a warning once and does not raise. - Lines 372-373. The MoE library is optional: its import error is ignored.
- Line 386.
register_triton_kernel_overridesdoes nothing by default. The docstring says that it runs "during model runner init, before any kernel launch"; the caller isinit_worker(vllm/v1/worker/worker_base.py:286-290).
get_mla_prefill_backend_cls: a default with a priority list#
device_capability = cls.get_device_capability()
if device_capability is None:
return MLAPrefillBackendEnum.FLASH_ATTN.get_class()
if device_capability.major == 10: # Blackwell
if mla_selector_config.mla_dimensions == MLADimensions(
qk_nope_head_dim=192,
qk_rope_head_dim=64,
v_head_dim=256,
):
priorities = [
MLAPrefillBackendEnum.TRTLLM_RAGGED,
MLAPrefillBackendEnum.FLASH_ATTN,
MLAPrefillBackendEnum.FLASHINFER,
MLAPrefillBackendEnum.TOKENSPEED_MLA,
]
else:
priorities = [
MLAPrefillBackendEnum.FLASH_ATTN,
MLAPrefillBackendEnum.TRTLLM_RAGGED,
MLAPrefillBackendEnum.FLASHINFER,
MLAPrefillBackendEnum.TOKENSPEED_MLA,
]
else: # Hopper (SM90) and older
priorities = [MLAPrefillBackendEnum.FLASH_ATTN]
all_invalid_reasons: dict[str, list[str]] = {}
for backend_enum in priorities:
try:
backend_cls = backend_enum.get_class()
invalid_reasons = backend_cls.validate_configuration(
device_capability, mla_selector_config
)
except ImportError:
invalid_reasons = ["ImportError"]
if not invalid_reasons:
return backend_cls
all_invalid_reasons[backend_enum.name] = invalid_reasons- Lines 459-460. No device capability (the base class): FlashAttention.
- Lines 462-480. Blackwell (major 10): TRT-LLM ragged first for one head layout (192 + 64 / 256), else FlashAttention first.
- Lines 481-482. Hopper and older: FlashAttention only.
- Lines 486-495. Return the first backend that imports and accepts the config. If none does, the method raises
ValueErrorwith all reasons (vllm/platforms/interface.py:498-514).
This is one of few pickers with real logic in the base class. A platform with one MLA prefill backend can override it and return that class.
__getattr__: the fallback to torch#
def __getattr__(self, key: str):
# Pickle checks dunder methods like __getstate__. If we return None
# for them, pickle treats it like a real value and tries to call it.
if key.startswith("__") and key.endswith("__"):
raise AttributeError(key)
device = getattr(torch, self.device_type, None)
if device is not None and hasattr(device, key):
attr = getattr(device, key)
# NOTE: `hasattr(device, key)=True` can only avoid AttributeError,
# but the value of this attr could be `None`.
if attr is not None:
return attr
logger.warning_once(
"Current platform %s does not have '%s' attribute.",
self.device_type,
key,
)
return None
def get_global_graph_pool(self) -> Any:
"""Return the global graph pool for this platform."""
cls = self.__class__
if cls._global_graph_pool is None:
cls._global_graph_pool = self.graph_pool_handle()
return cls._global_graph_pool- Lines 1303-1304. Dunder names raise at once, so pickle does not get
Nonefor__getstate__. - Lines 1306-1312. Look up the name in
torch.<device_type>; return it when it is notNone. - Lines 1314-1319. Else warn once and return
None. - Lines 1324-1325.
get_global_graph_pooluses the fallback:graph_pool_handleis not aPlatformmethod, so on CUDA it istorch.cuda.graph_pool_handle. The pool is stored on the class, so all objects of one platform class share it.
Deep dive#
The sections below give the full detail. Open them one at a time, or use Open all deep dives at the top of the page.
Deep diveData flow: types in and out
| Value | Type | Where |
|---|---|---|
| picker results | str, a dotted class path; the caller imports it with resolve_obj_by_qualname | for example vllm/v1/attention/selector.py:218-227 |
DeviceCapability | NamedTuple(major: int, minor: int); to_int() asserts minor < 10 | vllm/platforms/interface.py:93-134 |
has_device_capability(90) | bool; an int argument is compared with to_int(), a tuple with tuple order | vllm/platforms/interface.py:544-551 |
supported_dtypes | list[torch.dtype], default [bfloat16, float16, float32] | vllm/platforms/interface.py:188-194 |
get_infinity_values(dtype) | tuple[float, float], default (-inf, inf) | vllm/platforms/interface.py:1208-1211 |
fp8_dtype() | torch.dtype, default torch.float8_e4m3fn | vllm/platforms/interface.py:1267-1273 |
get_default_ir_op_priority | IrOpPriorityConfig, default with_default(["native"]) | vllm/platforms/interface.py:1439-1447 |
Deep diveA worked example: block size with two backends
Two made-up attention backends are in one model. Backend A declares MultipleOf(16). Backend B declares the fixed sizes [64, 128]. The user did not set --block-size, so the default is 16.
- Two backends, so the single-backend shortcut (line 721) does not apply.
- B does not accept 16, so line 723 fails.
- Per-backend sizes: A gives
[16](the base ofMultipleOf), B gives[64, 128](lines 726-733). - Candidates: lcm(16, 64) = 64 and lcm(16, 128) = 128, sorted
[64, 128](lines 734-736). - 64 is a multiple of 16 and is in B's list, so both accept it. The result is 64 (lines 737-739), and line 776 logs "Setting kv cache block size to 64".
(The acceptance test is each backend's supports_block_size; this example assumes that it accepts the multiples and the listed sizes. The real rules of each backend are on How vLLM chooses an attention backend.)
Deep diveProcesses and threads
The class has no lock and almost no state. The state that exists:
_assigned_physical_gpu_ids: a module global, set once per worker process; a second call with other ids raisesRuntimeError(vllm/platforms/interface.py:50-58). The docstring expects a single thread (vllm/platforms/interface.py:49)._global_graph_pool: a class attribute, so one pool per platform class and process (vllm/platforms/interface.py:1321-1326).supported_quantization: a class-level list.register_quantization_configappends to it when it is not empty (vllm/model_executor/layers/quantization/__init__.py:98-100). The base list is one object shared by every subclass that does not set its own. (Derived.)
Which process calls which method is in the hook table of The Platform interface: config hooks in the front end, pickers in the workers and the engine core.
Deep diveError paths: what raises, what only logs
| Situation | Result | Where |
|---|---|---|
A required device method is not overridden (get_device_name, set_device, get_punica_wrapper, ...) | NotImplementedError | for example vllm/platforms/interface.py:590-593 |
| An unknown attribute | WARNING once, returns None | vllm/platforms/interface.py:1314-1319 |
| A non-integer device id in the device-control variable | ValueError | vllm/platforms/interface.py:269-275 |
| A logical id that is not visible | RuntimeError | vllm/platforms/interface.py:335-340 |
| No common block size | ValueError | vllm/platforms/interface.py:740-747 |
| An aligned block size that a backend rejects | ValueError | vllm/platforms/interface.py:819-823 |
| No valid MLA prefill backend | ValueError | vllm/platforms/interface.py:511-514 |
Quantization not in a non-empty supported_quantization | ValueError | vllm/platforms/interface.py:1155-1158 |
An unknown VLLM_* variable | WARNING; ValueError with hard_fail | vllm/platforms/interface.py:1459-1468 |
| Two layers share a layer index | NotImplementedError unless the platform overrides the check | vllm/platforms/interface.py:1371-1374 |
| WSL detected | WARNING; pinned memory off | vllm/platforms/interface.py:1183-1193 |
Deep diveEnvironment variables
| Variable | Read at | Effect |
|---|---|---|
the platform's device_control_env_var (for CUDA CUDA_VISIBLE_DEVICES, vllm/platforms/cuda.py:233) | vllm/platforms/interface.py:307-311, vllm/platforms/interface.py:327, vllm/platforms/interface.py:352 | Maps logical and visible ids to physical ids. An empty value counts as not set. |
every VLLM_* name | vllm/platforms/interface.py:1459-1460 | Checked against envs.environment_variables by validate_environ. |
The file reads no VLLM_* setting itself. The variables that choose the platform are read in __init__.py (platforms/__init__.py: platform detection and current_platform).
Related concepts#
- The Platform interface: the hooks in start-up order, and
CudaPlatformas the reference. - Platform plugins and current_platform: how a process chooses its platform class.
- How vLLM chooses an attention backend: what happens with the name that
get_attn_backend_clsreturns. - CustomOp, out-of-tree ops and IR op providers: how
is_out_of_tree()selectsforward_oot. - platforms/__init__.py: platform detection and current_platform: the detection and
current_platform.
Check yourself#
- 1Which three class attributes have no default, and why must a subclass set them?easy
Show answer
_enum,device_nameanddevice_typeare only annotated (vllm/platforms/interface.py:138-140). Theis_*checks read_enum(vllm/platforms/interface.py:196-222), and the fallback__getattr__readsdevice_type(vllm/platforms/interface.py:1306). - 2medium
CUDA_VISIBLE_DEVICES=2,3is set and no assigned ids exist. What doesdevice_id_to_physical_device_id(1)return?Show answer
3. The method splits the variable at commas and takes index 1, then converts
"3"to an int (vllm/platforms/interface.py:307-313,vllm/platforms/interface.py:267-270). - 3A platform does not definemedium
Stream, and itsdevice_typeis"privateuseone". What doescurrent_platform.Streamgive, iftorch.privateuseonedoes not exist?Show answer
None, with a one-time warning "Current platform privateuseone does not have 'Stream' attribute".getattr(torch, "privateuseone", None)isNone, so the method skips the lookup and returnsNone(vllm/platforms/interface.py:1306-1319). - 4A model has two attention backends: one declareshard
MultipleOf(16), the other the fixed sizes[64, 128]. The user does not set--block-size. Which block size does vLLM pick, and in which process is it computed with TP = 2?Show answer
64: the smallest least common multiple that both accept (the worked example in the deep dive;
vllm/platforms/interface.py:726-739). Each of the 2 worker processes computes it afterload_model(vllm/v1/executor/multiproc_executor.py:701-702).