vLLM Explained

platforms/interface.py: the Platform base class

AdvancedCode mapabout 15 min read + 3 min deep dives

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.

Filevllm/platforms/interface.py, 1495 lines of Python. Three small types, three module functions, the class Platform (lines 137-1490) and UnspecifiedPlatform.
Main classPlatform (vllm/platforms/interface.py:137): 12 class attributes and 90 methods, 84 of them without a leading underscore. Most methods are class methods.
Runs inEvery 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 byvLLM 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).
Callstorch (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 fileThe 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#

Figure: where the methods of this file are called during a vllm serve start-up with TP = 2.
Open full size ↗

Who calls it#

What it calls#

Key classes and functions#

Map of the file#

LinesSection
vllm/platforms/interface.py:1-35Imports; 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-68in_wsl(), cached: "microsoft" in platform.uname().
vllm/platforms/interface.py:71-90PlatformEnum (CUDA, ROCM, TPU, XPU, CPU, OOT, UNSPECIFIED) and CpuArchEnum.
vllm/platforms/interface.py:93-134DeviceCapability(major, minor) with ordering and to_int() (for example 9.0 → 90).
vllm/platforms/interface.py:137-181Platform: identity class attributes.
vllm/platforms/interface.py:183-244Properties and the is_* checks.
vllm/platforms/interface.py:246-264Compile pickers and import_ir_kernels.
vllm/platforms/interface.py:266-363Device ids: logical, visible and physical.
vllm/platforms/interface.py:365-386Kernel hooks.
vllm/platforms/interface.py:388-514Attention pickers: decoder, vision encoder, MLA prefill.
vllm/platforms/interface.py:516-636Device queries: capability, name, memory, device setting, seeds.
vllm/platforms/interface.py:638-676Config hooks.
vllm/platforms/interface.py:678-1134Block size: the common size for all backends, hybrid (Mamba) and mixed-dtype alignment.
vllm/platforms/interface.py:1136-1298Checks and feature flags: KV specs, model and quantization checks, CPU architecture, memory, LoRA wrapper, communicator, FP8, collectives, request check.
vllm/platforms/interface.py:1300-1326The __getattr__ fallback and the global graph pool.
vllm/platforms/interface.py:1328-1490Graph wrapper, process groups, more feature flags, NIXL, IR priority, validate_environ, KV cache config builder.
vllm/platforms/interface.py:1493-1495UnspecifiedPlatform: _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.

NameKindLinesWhat it does
log_warningsclassmethodvllm/platforms/interface.py:153-154Logs platform diagnostics after logging is configured. Default: nothing.
pass_keypropertyvllm/platforms/interface.py:184-186Property: the Inductor config key for the custom pass manager (post_grad_custom_post_pass).
supported_dtypespropertyvllm/platforms/interface.py:189-194Property: model dtypes the device supports; the first one is the fallback for auto.
is_cudamethodvllm/platforms/interface.py:196-197True for PlatformEnum.CUDA.
is_rocmmethodvllm/platforms/interface.py:199-200True for PlatformEnum.ROCM.
is_tpumethodvllm/platforms/interface.py:202-203True for PlatformEnum.TPU.
is_xpumethodvllm/platforms/interface.py:205-206True for PlatformEnum.XPU.
is_cpumethodvllm/platforms/interface.py:208-209True for PlatformEnum.CPU.
uses_host_device_handlingmethodvllm/platforms/interface.py:211-213Whether vLLM leaves DeviceConfig.device unset (TPU only by default).
is_zen_cpumethodvllm/platforms/interface.py:215-216True only on ZenCpuPlatform; default False.
is_out_of_treemethodvllm/platforms/interface.py:218-219True for PlatformEnum.OOT; selects forward_oot in an enabled CustomOp.
is_unspecifiedmethodvllm/platforms/interface.py:221-222True for UnspecifiedPlatform.
get_max_output_tokensmethodvllm/platforms/interface.py:224-225Upper limit of output tokens for a prompt length; default sys.maxsize.
is_cuda_alikemethodvllm/platforms/interface.py:227-229True for CUDA and ROCm, without a CUDA call.
is_sleep_mode_availablemethodvllm/platforms/interface.py:231-236True for CUDA, ROCm and XPU.
is_cumem_allocator_availablemethodvllm/platforms/interface.py:238-244True when vllm.device_allocator.cumem imports and reports it is available.
get_pass_manager_clsclassmethodvllm/platforms/interface.py:247-251Picker: dotted name of the post-grad pass manager.
get_compile_backendclassmethodvllm/platforms/interface.py:254-256Picker: the compile backend for simple functions; default simple_compile_backend.
import_ir_kernelsclassmethodvllm/platforms/interface.py:259-264Kernel hook: imports vllm.kernels (IR op implementations).
device_control_id_to_physical_device_idclassmethodvllm/platforms/interface.py:267-275Turns one entry of the device-control variable into an int; non-integers raise ValueError.
device_id_to_physical_device_idclassmethodvllm/platforms/interface.py:288-315Logical (local rank) id to physical id, through assigned ids or the device-control variable.
logical_device_id_to_visible_device_idclassmethodvllm/platforms/interface.py:318-341Logical id to the ordinal that torch.device uses in this process.
visible_device_id_to_physical_device_idclassmethodvllm/platforms/interface.py:344-363Visible ordinal back to a physical id.
import_kernelsclassmethodvllm/platforms/interface.py:366-373Kernel hook: imports vllm._C and the optional MoE library.
register_triton_kernel_overridesclassmethodvllm/platforms/interface.py:376-386Kernel hook: replaces core Triton kernels; default does nothing.
get_attn_backend_clsclassmethodvllm/platforms/interface.py:389-396Picker: dotted name of the attention backend; default "".
get_supported_vit_attn_backendsclassmethodvllm/platforms/interface.py:399-402Vision-encoder attention backends that the platform allows; default [TORCH_SDPA].
get_vit_attn_backendclassmethodvllm/platforms/interface.py:405-432Picks the vision-encoder attention backend; default TORCH_SDPA.
get_mla_prefill_backend_clsclassmethodvllm/platforms/interface.py:435-514Picks the MLA prefill backend class from a capability-based priority list.
get_device_capabilityclassmethodvllm/platforms/interface.py:517-528(major, minor) without a CUDA context; default None.
has_device_capabilityclassmethodvllm/platforms/interface.py:531-551True when the capability is at least the given one; False when unknown.
is_device_capabilityclassmethodvllm/platforms/interface.py:554-574True when the capability is exactly the given one.
is_device_capability_familyclassmethodvllm/platforms/interface.py:577-588True when the major version matches (for example any 10.x).
get_device_nameclassmethodvllm/platforms/interface.py:591-593Device name; base raises NotImplementedError.
get_device_uuidclassmethodvllm/platforms/interface.py:596-598Device UUID; base raises NotImplementedError.
get_device_total_memoryclassmethodvllm/platforms/interface.py:601-603Total device memory in bytes; base raises NotImplementedError.
get_all_gpu_pci_bus_idsclassmethodvllm/platforms/interface.py:606-616Device index to PCI bus id, for VLLM_GPU_NIC_PCIE_MAPPING; base raises.
inference_modeclassmethodvllm/platforms/interface.py:619-626Context manager; default torch.inference_mode(True).
set_deviceclassmethodvllm/platforms/interface.py:629-631Sets the current device; base raises NotImplementedError.
manual_seed_allclassmethodvllm/platforms/interface.py:634-636Seeds all devices; base raises NotImplementedError.
pre_register_and_updateclassmethodvllm/platforms/interface.py:639-651Config hook: before the flags are parsed (and again in create_engine_config).
apply_config_platform_defaultsclassmethodvllm/platforms/interface.py:654-664Config hook: platform defaults in VllmConfig.__post_init__.
check_and_update_configclassmethodvllm/platforms/interface.py:667-676Config hook: check or change VllmConfig; sets worker_cls.
update_block_size_for_backendclassmethodvllm/platforms/interface.py:750-794Config hook after model load: a block size that all attention backends accept.
register_custom_kv_cache_specsclassmethodvllm/platforms/interface.py:1137-1139Registers platform KV cache spec classes; default nothing.
verify_model_archclassmethodvllm/platforms/interface.py:1142-1150Raises or warns for an unsupported architecture; default accepts all.
verify_quantizationclassmethodvllm/platforms/interface.py:1153-1158Raises ValueError if supported_quantization is set and lacks the method.
get_cpu_architectureclassmethodvllm/platforms/interface.py:1161-1178CpuArchEnum from platform.machine().
is_pin_memory_availableclassmethodvllm/platforms/interface.py:1181-1194True, except under WSL (warning, False).
get_current_memory_usageclassmethodvllm/platforms/interface.py:1197-1201Memory in use in bytes; base raises NotImplementedError.
get_punica_wrapperclassmethodvllm/platforms/interface.py:1204-1206Picker: LoRA kernel wrapper class; base raises NotImplementedError.
get_infinity_valuesclassmethodvllm/platforms/interface.py:1209-1211The values that stand for minus and plus infinity for a dtype.
can_update_inplaceclassmethodvllm/platforms/interface.py:1214-1216Whether in-place memory updates are allowed; default True.
get_device_communicator_clsclassmethodvllm/platforms/interface.py:1219-1221Picker: device communicator class; default DeviceCommunicatorBase.
is_integrated_gpuclassmethodvllm/platforms/interface.py:1224-1232Whether the GPU shares system memory (UMA); default False.
enable_multi_stream_overlapclassmethodvllm/platforms/interface.py:1235-1242Whether to overlap auxiliary streams; default False.
supports_mxclassmethodvllm/platforms/interface.py:1245-1247MX formats supported; default False.
supports_fp8classmethodvllm/platforms/interface.py:1250-1252FP8 supported; default False.
is_fp8_fnuzclassmethodvllm/platforms/interface.py:1255-1265Whether the preferred FP8 type is FNUZ; default False.
fp8_dtypeclassmethodvllm/platforms/interface.py:1268-1273Preferred FP8 dtype; default torch.float8_e4m3fn.
use_all_gatherclassmethodvllm/platforms/interface.py:1276-1278Whether the logits processor gathers logits with all-gather; default True.
use_custom_allreduceclassmethodvllm/platforms/interface.py:1281-1283Whether vLLM's custom all-reduce is supported; default False.
opaque_attention_opclassmethodvllm/platforms/interface.py:1286-1290Whether attention is one opaque custom op for torch.compile; default False.
validate_requestclassmethodvllm/platforms/interface.py:1293-1298Raises for a request that the platform cannot serve; default accepts all.
__getattr__methodvllm/platforms/interface.py:1300-1319Fallback: an unknown name is looked up in torch.<device_type>; else warning and None.
get_global_graph_poolmethodvllm/platforms/interface.py:1321-1326One graph memory pool per platform class, made on first use.
get_static_graph_wrapper_clsclassmethodvllm/platforms/interface.py:1329-1331Picker: the static graph (CUDA graph) wrapper class.
stateless_init_device_torch_dist_pgclassmethodvllm/platforms/interface.py:1334-1343Makes a device process group without global state; base raises.
check_if_supports_dtypeclassmethodvllm/platforms/interface.py:1346-1348Raises for an unsupported dtype; base raises NotImplementedError.
support_hybrid_kv_cacheclassmethodvllm/platforms/interface.py:1351-1353Hybrid KV cache supported; default False.
support_static_graph_modeclassmethodvllm/platforms/interface.py:1356-1358Graph mode supported; default False.
check_runner_kv_caches_multi_layerclassmethodvllm/platforms/interface.py:1361-1374Raises unless the runner handles two attention layers with one layer index.
support_deep_gemmclassmethodvllm/platforms/interface.py:1377-1379DeepGEMM supported; default False.
use_custom_op_collectivesclassmethodvllm/platforms/interface.py:1382-1387Use torch.ops.vllm.* collectives; default False.
use_sync_weight_loaderclassmethodvllm/platforms/interface.py:1390-1392Whether the weight loader must synchronize; default False.
make_synced_weight_loaderclassmethodvllm/platforms/interface.py:1395-1406Wraps a weight loader with torch._sync when the line above is true.
get_nixl_supported_devicesclassmethodvllm/platforms/interface.py:1409-1413NIXL buffer devices per device type; default {}.
get_nixl_memory_typeclassmethodvllm/platforms/interface.py:1416-1418NIXL memory type; default None.
check_max_model_lenclassmethodvllm/platforms/interface.py:1421-1423May lower max_model_len; default returns it unchanged.
set_additional_forward_contextclassmethodvllm/platforms/interface.py:1426-1428Extra forward-context fields; default {}.
num_compute_unitsclassmethodvllm/platforms/interface.py:1431-1437Number of SMs, CUs or EUs; base raises NotImplementedError.
get_default_ir_op_priorityclassmethodvllm/platforms/interface.py:1440-1447Default IR op provider priority; default ["native"].
is_arch_support_pdlclassmethodvllm/platforms/interface.py:1450-1452Programmatic Dependent Launch supported; default False.
validate_environclassmethodvllm/platforms/interface.py:1455-1468Warns (or raises) for unknown VLLM_* variables.
get_kv_cache_config_builder_clsclassmethodvllm/platforms/interface.py:1471-1490Picker: 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
  1. Lines 639-651. pre_register_and_update gets the CLI parser, or None. The docstring names the use case: an out-of-tree quantization config. It is called twice by vllm serve (vllm/engine/arg_utils.py:3257, vllm/engine/arg_utils.py:2246).
  2. Lines 654-664. apply_config_platform_defaults runs "after parsing cli arguments" and can change defaults, for example custom_ops.
  3. Lines 667-676. check_and_update_config can raise or change the config "in place". All three return None; 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)
  1. Lines 760-765. Return early with no model config, or with no attention backend except SSM ones. _find_non_ssm_backends reads the backend of each attention layer, so the model must be built first (vllm/platforms/interface.py:678-697).
  2. 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.
  3. 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.
  4. 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
                )
                + ")."
            )
  1. Lines 721-722. One backend: ask it for its preferred size.
  2. Lines 723-724. All backends accept the default: use it.
  3. 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.
  4. Lines 737-739. Return the first candidate that every backend accepts.
  5. Line 740. None fits: ValueError with 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
  1. 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.
  2. 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,5 and local rank 1 give physical device 5.
  3. 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
  1. Lines 368-371. A failed import of vllm._C logs a warning once and does not raise.
  2. Lines 372-373. The MoE library is optional: its import error is ignored.
  3. Line 386. register_triton_kernel_overrides does nothing by default. The docstring says that it runs "during model runner init, before any kernel launch"; the caller is init_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
  1. Lines 459-460. No device capability (the base class): FlashAttention.
  2. Lines 462-480. Blackwell (major 10): TRT-LLM ragged first for one head layout (192 + 64 / 256), else FlashAttention first.
  3. Lines 481-482. Hopper and older: FlashAttention only.
  4. Lines 486-495. Return the first backend that imports and accepts the config. If none does, the method raises ValueError with 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
  1. Lines 1303-1304. Dunder names raise at once, so pickle does not get None for __getstate__.
  2. Lines 1306-1312. Look up the name in torch.<device_type>; return it when it is not None.
  3. Lines 1314-1319. Else warn once and return None.
  4. Lines 1324-1325. get_global_graph_pool uses the fallback: graph_pool_handle is not a Platform method, so on CUDA it is torch.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 dive

Data flow: types in and out

ValueTypeWhere
picker resultsstr, a dotted class path; the caller imports it with resolve_obj_by_qualnamefor example vllm/v1/attention/selector.py:218-227
DeviceCapabilityNamedTuple(major: int, minor: int); to_int() asserts minor < 10vllm/platforms/interface.py:93-134
has_device_capability(90)bool; an int argument is compared with to_int(), a tuple with tuple ordervllm/platforms/interface.py:544-551
supported_dtypeslist[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_e4m3fnvllm/platforms/interface.py:1267-1273
get_default_ir_op_priorityIrOpPriorityConfig, default with_default(["native"])vllm/platforms/interface.py:1439-1447
Deep dive

A 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.

  1. Two backends, so the single-backend shortcut (line 721) does not apply.
  2. B does not accept 16, so line 723 fails.
  3. Per-backend sizes: A gives [16] (the base of MultipleOf), B gives [64, 128] (lines 726-733).
  4. Candidates: lcm(16, 64) = 64 and lcm(16, 128) = 128, sorted [64, 128] (lines 734-736).
  5. 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 dive

Processes and threads

The class has no lock and almost no state. The state that exists:

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 dive

Error paths: what raises, what only logs

SituationResultWhere
A required device method is not overridden (get_device_name, set_device, get_punica_wrapper, ...)NotImplementedErrorfor example vllm/platforms/interface.py:590-593
An unknown attributeWARNING once, returns Nonevllm/platforms/interface.py:1314-1319
A non-integer device id in the device-control variableValueErrorvllm/platforms/interface.py:269-275
A logical id that is not visibleRuntimeErrorvllm/platforms/interface.py:335-340
No common block sizeValueErrorvllm/platforms/interface.py:740-747
An aligned block size that a backend rejectsValueErrorvllm/platforms/interface.py:819-823
No valid MLA prefill backendValueErrorvllm/platforms/interface.py:511-514
Quantization not in a non-empty supported_quantizationValueErrorvllm/platforms/interface.py:1155-1158
An unknown VLLM_* variableWARNING; ValueError with hard_failvllm/platforms/interface.py:1459-1468
Two layers share a layer indexNotImplementedError unless the platform overrides the checkvllm/platforms/interface.py:1371-1374
WSL detectedWARNING; pinned memory offvllm/platforms/interface.py:1183-1193
Deep dive

Environment variables

VariableRead atEffect
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:352Maps logical and visible ids to physical ids. An empty value counts as not set.
every VLLM_* namevllm/platforms/interface.py:1459-1460Checked 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).

Check yourself#

  1. 1
    Which three class attributes have no default, and why must a subclass set them?
    easy
    Show answer

    _enum, device_name and device_type are only annotated (vllm/platforms/interface.py:138-140). The is_* checks read _enum (vllm/platforms/interface.py:196-222), and the fallback __getattr__ reads device_type (vllm/platforms/interface.py:1306).

  2. 2
    CUDA_VISIBLE_DEVICES=2,3 is set and no assigned ids exist. What does device_id_to_physical_device_id(1) return?
    medium
    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).

  3. 3
    A platform does not define Stream, and its device_type is "privateuseone". What does current_platform.Stream give, if torch.privateuseone does not exist?
    medium
    Show answer

    None, with a one-time warning "Current platform privateuseone does not have 'Stream' attribute". getattr(torch, "privateuseone", None) is None, so the method skips the lookup and returns None (vllm/platforms/interface.py:1306-1319).

  4. 4
    A model has two attention backends: one declares 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?
    hard
    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 after load_model (vllm/v1/executor/multiproc_executor.py:701-702).