Files
vllm-omni/vllm_omni/diffusion/offloader/config.py
T
2026-09-26 17:45:20 +08:00

443 lines
18 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Public diffusion CPU-offload configuration helpers."""
from __future__ import annotations
from collections.abc import Collection, Iterator, Mapping
from dataclasses import dataclass
from enum import Enum
from typing import Any, TypeVar
DIT_COMPONENT = "dit"
TEXT_ENCODER_COMPONENT = "text_encoder"
OFFLOAD_COMPONENTS = frozenset({DIT_COMPONENT, TEXT_ENCODER_COMPONENT})
DEFAULT_OFFLOAD_COMPONENTS = frozenset({DIT_COMPONENT})
_KeyT = TypeVar("_KeyT")
_ValueT = TypeVar("_ValueT")
class _FrozenMapping(Mapping[_KeyT, _ValueT]):
"""Small immutable mapping that remains safe to pickle between workers."""
def __init__(self, value: Mapping[_KeyT, _ValueT]) -> None:
self._items = tuple(value.items())
def __getitem__(self, key: _KeyT) -> _ValueT:
for candidate, value in self._items:
if candidate == key:
return value
raise KeyError(key)
def __iter__(self) -> Iterator[_KeyT]:
return (key for key, _ in self._items)
def __len__(self) -> int:
return len(self._items)
def __reduce__(self):
return type(self), (dict(self._items),)
class OffloadMode(str, Enum):
"""User-facing offload granularity."""
MODULE = "module"
LAYER = "layer"
class OffloadStrategy(str, Enum):
"""Resolved internal backend strategy."""
NONE = "none"
MODEL_LEVEL = "model_level"
LAYER_WISE = "layer_wise"
DISTRIBUTED_LAYER_WISE = "distributed_layer_wise"
class DLOTransfer(str, Enum):
"""How one component's next block reaches the device."""
ALLGATHER = "allgather"
RANK_LOCAL = "rank-local"
@dataclass(frozen=True)
class LayerOffloadOptions:
"""Layer-mode settings for one selected diffusion component."""
weight_transfer: DLOTransfer | None = None
resident_layers: int = 0
@dataclass(frozen=True)
class ParsedDiffusionOffloadConfig:
"""Validated internal form of the raw public offload mapping."""
mode: OffloadMode
components: frozenset[str]
layer_options: Mapping[str, LayerOffloadOptions]
pin_memory: bool | None = None
def __post_init__(self) -> None:
object.__setattr__(self, "layer_options", _FrozenMapping(self.layer_options))
@dataclass(frozen=True)
class ResolvedOffload:
"""Canonical offload policy derived once from public or legacy inputs."""
strategy: OffloadStrategy
public: ParsedDiffusionOffloadConfig | None
components: frozenset[str]
transfers: Mapping[str, DLOTransfer]
pin_memory: bool
resident_layers: int
def __post_init__(self) -> None:
object.__setattr__(self, "transfers", _FrozenMapping(self.transfers))
def offloads(self, component: str) -> bool:
return component in self.components
def uses_allgather(self, component: str = DIT_COMPONENT) -> bool:
try:
return self.transfers[component] is DLOTransfer.ALLGATHER
except KeyError as exc:
raise ValueError(f"Unknown offload component {component!r}") from exc
@property
def any_allgather(self) -> bool:
return self.strategy is OffloadStrategy.DISTRIBUTED_LAYER_WISE and any(
self.uses_allgather(component) for component in self.components
)
_LEGACY_STRATEGY_FIELDS = {
"enable_cpu_offload": OffloadStrategy.MODEL_LEVEL,
"enable_layerwise_offload": OffloadStrategy.LAYER_WISE,
"enable_distributed_layerwise_offload": OffloadStrategy.DISTRIBUTED_LAYER_WISE,
}
_LEGACY_STRATEGY_PRIORITY = (
OffloadStrategy.DISTRIBUTED_LAYER_WISE,
OffloadStrategy.LAYER_WISE,
OffloadStrategy.MODEL_LEVEL,
)
_EnumT = TypeVar("_EnumT", bound=Enum)
def _parse_enum(value: Any, enum_type: type[_EnumT], label: str) -> _EnumT:
if isinstance(value, enum_type):
return value
if not isinstance(value, str):
raise TypeError(f"{label} must be a string, got {type(value).__name__}")
try:
return enum_type(value)
except ValueError as exc:
choices = ", ".join(item.value for item in enum_type)
raise ValueError(f"Unknown {label} {value!r}; choose from: {choices}") from exc
def _validate_string_keys(value: Mapping[Any, Any], label: str) -> None:
if any(not isinstance(key, str) for key in value):
raise TypeError(f"{label} keys must be strings")
def _parse_layer_options(component: str, value: Any) -> LayerOffloadOptions:
if not isinstance(value, Mapping):
raise TypeError(
f"diffusion_offload_config.layer_options[{component!r}] must be a mapping, got {type(value).__name__}"
)
_validate_string_keys(value, f"diffusion_offload_config.layer_options[{component!r}]")
unknown = sorted(set(value) - {"weight_transfer", "resident_layers"})
if unknown:
raise ValueError(
f"Unknown diffusion offload setting(s) for {component}: {', '.join(unknown)}; "
"choose from: weight_transfer, resident_layers"
)
weight_transfer = (
_parse_enum(value["weight_transfer"], DLOTransfer, "offload transfer") if "weight_transfer" in value else None
)
resident_layers = value.get("resident_layers", 0)
if type(resident_layers) is not int or resident_layers < 0:
raise ValueError(f"resident_layers for {component} must be a non-negative integer")
parsed = LayerOffloadOptions(
weight_transfer=weight_transfer,
resident_layers=resident_layers,
)
if component != DIT_COMPONENT and parsed.resident_layers:
raise ValueError("resident_layers currently supports only the 'dit' component")
return parsed
def parse_diffusion_offload_config(value: Any) -> ParsedDiffusionOffloadConfig | None:
"""Validate the compact ``diffusion_offload_config`` public API."""
if value is None:
return None
if not isinstance(value, Mapping):
raise TypeError(f"diffusion_offload_config must be a mapping, got {type(value).__name__}")
_validate_string_keys(value, "diffusion_offload_config")
unknown = sorted(set(value) - {"mode", "components", "layer_options", "pin_memory"})
if unknown:
raise ValueError(
f"Unknown diffusion_offload_config field(s): {', '.join(unknown)}; "
"choose from: mode, components, layer_options, pin_memory"
)
if "mode" not in value:
raise ValueError("diffusion_offload_config requires 'mode'")
if "components" not in value:
raise ValueError("diffusion_offload_config requires 'components'")
mode = _parse_enum(value["mode"], OffloadMode, "diffusion offload mode")
components = parse_offload_components(value["components"])
raw_layer_options = value.get("layer_options", {})
if not isinstance(raw_layer_options, Mapping):
raise TypeError("diffusion_offload_config.layer_options must be a mapping")
_validate_string_keys(raw_layer_options, "diffusion_offload_config.layer_options")
unselected_options = sorted(set(raw_layer_options) - components)
if unselected_options:
raise ValueError(
"diffusion_offload_config.layer_options requires selecting the same component(s): "
+ ", ".join(unselected_options)
)
layer_options = {
component: _parse_layer_options(component, raw_layer_options.get(component, {})) for component in components
}
pin_memory = value.get("pin_memory")
if pin_memory is not None and type(pin_memory) is not bool:
raise TypeError("diffusion_offload_config.pin_memory must be a bool")
if mode is OffloadMode.MODULE and raw_layer_options:
raise ValueError(
"diffusion_offload_config.layer_options requires mode='layer'; "
f"configured for: {', '.join(sorted(raw_layer_options))}"
)
dit = layer_options.get(DIT_COMPONENT)
if dit is not None and dit.resident_layers and dit.weight_transfer is DLOTransfer.ALLGATHER:
raise ValueError("resident_layers requires dit.weight_transfer='rank-local'")
return ParsedDiffusionOffloadConfig(
mode=mode,
components=components,
layer_options=layer_options,
pin_memory=pin_memory,
)
def _legacy_strategy(config: Any) -> OffloadStrategy:
enabled = {strategy for field, strategy in _LEGACY_STRATEGY_FIELDS.items() if bool(getattr(config, field, False))}
return next((strategy for strategy in _LEGACY_STRATEGY_PRIORITY if strategy in enabled), OffloadStrategy.NONE)
def _public_strategy(public: ParsedDiffusionOffloadConfig) -> OffloadStrategy:
if public.mode is OffloadMode.MODULE:
return OffloadStrategy.MODEL_LEVEL
needs_distributed_backend = any(
settings.weight_transfer is DLOTransfer.ALLGATHER or settings.resident_layers
for settings in public.layer_options.values()
)
return OffloadStrategy.DISTRIBUTED_LAYER_WISE if needs_distributed_backend else OffloadStrategy.LAYER_WISE
def _validate_legacy_layer_options(config: Any, public: ParsedDiffusionOffloadConfig | None) -> None:
"""Reject ambiguous or invalid compatibility DLO tuning before loading."""
resident_layers = getattr(config, "dlo_resident_layers", 0)
if type(resident_layers) is not int or resident_layers < 0:
raise ValueError(f"dlo_resident_layers must be a non-negative integer, got {resident_layers!r}")
if public is not None:
if getattr(config, "host_weight_runtime_mode", "disabled") != "disabled":
raise ValueError("diffusion_offload_config cannot be combined with Host Weight Runtime")
conflicting_fields = []
if getattr(config, "dlo_use_allgather", True) is not True:
conflicting_fields.append("dlo_use_allgather")
if resident_layers:
conflicting_fields.append("dlo_resident_layers")
if float(getattr(config, "dlo_host_registration_limit_gib", 0.0)) > 0:
conflicting_fields.append("dlo_host_registration_limit_gib")
if conflicting_fields:
raise ValueError(
"diffusion_offload_config cannot be combined with legacy DLO option(s): "
+ ", ".join(conflicting_fields)
)
return
if resident_layers and bool(getattr(config, "dlo_use_allgather", True)):
raise ValueError(
"dlo_resident_layers requires the DiT DLO transfer to be rank-local; set dlo_use_allgather=False"
)
def resolve_offload(config: Any) -> ResolvedOffload:
"""Resolve and cache the complete offload policy on its owning config."""
cached = getattr(config, "_resolved_diffusion_offload", None)
if isinstance(cached, ResolvedOffload):
return cached
public = parse_diffusion_offload_config(getattr(config, "diffusion_offload_config", None))
_validate_legacy_layer_options(config, public)
legacy = _legacy_strategy(config)
if public is None:
transfers = {
DIT_COMPONENT: (
DLOTransfer.ALLGATHER if bool(getattr(config, "dlo_use_allgather", True)) else DLOTransfer.RANK_LOCAL
),
TEXT_ENCODER_COMPONENT: DLOTransfer.RANK_LOCAL,
}
resolved = ResolvedOffload(
strategy=legacy,
public=None,
components=DEFAULT_OFFLOAD_COMPONENTS,
transfers=transfers,
pin_memory=bool(getattr(config, "pin_cpu_memory", True)),
resident_layers=int(getattr(config, "dlo_resident_layers", 0)),
)
else:
strategy = _public_strategy(public)
if legacy is not OffloadStrategy.NONE and legacy is not strategy:
raise ValueError("diffusion_offload_config cannot be combined with legacy enable_*_offload flags")
transfers = {component: DLOTransfer.RANK_LOCAL for component in OFFLOAD_COMPONENTS}
for component, options in public.layer_options.items():
transfers[component] = options.weight_transfer or DLOTransfer.RANK_LOCAL
dit_options = public.layer_options.get(DIT_COMPONENT)
resolved = ResolvedOffload(
strategy=strategy,
public=public,
components=public.components,
transfers=transfers,
pin_memory=(
public.pin_memory if public.pin_memory is not None else bool(getattr(config, "pin_cpu_memory", True))
),
resident_layers=0 if dit_options is None else dit_options.resident_layers,
)
parallel_config = getattr(config, "parallel_config", None)
data_parallel_size = int(getattr(parallel_config, "data_parallel_size", 1))
cache_backend = getattr(config, "cache_backend", None)
if data_parallel_size > 1 and resolved.any_allgather and cache_backend not in (None, "none"):
raise ValueError(
"Diffusion cache acceleration cannot be combined with DLO AllGather "
"across data-parallel ranks because rank-local cache decisions can skip "
"different weight collectives. Disable cache_backend or use rank-local transfer."
)
if (
data_parallel_size > 1
and bool(getattr(config, "enable_prompt_embed_cache", False))
and resolved.offloads(TEXT_ENCODER_COMPONENT)
and resolved.uses_allgather(TEXT_ENCODER_COMPONENT)
):
raise ValueError(
"Prompt embedding cache cannot be combined with text_encoder AllGather "
"across data-parallel ranks: rank-local cache hits would skip different "
"encoder collectives. Use text_encoder weight_transfer='rank-local' or "
"disable enable_prompt_embed_cache."
)
# Actual runtime configs are immutable after construction. Keeping the
# derived object off the public dataclass fields also keeps projections and
# serialization transport-safe while making hot-path accessors constant-time.
try:
setattr(config, "_resolved_diffusion_offload", resolved)
except (AttributeError, TypeError):
pass
return resolved
def resolve_offload_strategy(config: Any) -> OffloadStrategy:
"""Resolve the compact config or compatibility boolean entry points."""
return resolve_offload(config).strategy
def offload_enabled(config: Any) -> bool:
"""Return whether any CPU-offload policy is active."""
return resolve_offload(config).strategy is not OffloadStrategy.NONE
def offload_streams_blocks(config: Any) -> bool:
"""Return whether an active policy streams weights block by block."""
return resolve_offload(config).strategy in (
OffloadStrategy.LAYER_WISE,
OffloadStrategy.DISTRIBUTED_LAYER_WISE,
)
def materialize_legacy_offload_flags(config: Any) -> OffloadStrategy:
"""Keep existing strategy readers working after resolving the compact API."""
resolved = resolve_offload(config)
for field, field_strategy in _LEGACY_STRATEGY_FIELDS.items():
setattr(config, field, resolved.strategy is field_strategy)
setattr(config, "pin_cpu_memory", resolved.pin_memory)
return resolved.strategy
def parse_offload_components(value: Collection[str]) -> frozenset[str]:
"""Validate an internal component collection."""
if isinstance(value, (str, Mapping)) or not isinstance(value, Collection):
raise TypeError("diffusion_offload_config.components must be a non-empty list of component names")
if any(not isinstance(item, str) for item in value):
raise TypeError("offload component entries must be strings")
components = list(value)
if not components:
raise ValueError("offload components must not be empty")
unknown = sorted(set(components) - OFFLOAD_COMPONENTS)
if unknown:
choices = ", ".join(sorted(OFFLOAD_COMPONENTS))
raise ValueError(f"Unknown diffusion offload component(s): {', '.join(unknown)}; choose from: {choices}")
return frozenset(components)
def parse_dlo_transfer(value: Mapping[str, str | DLOTransfer]) -> dict[str, DLOTransfer]:
"""Resolve an internal per-component transfer mapping."""
if not isinstance(value, Mapping):
raise TypeError(f"offload transfers must be a mapping, got {type(value).__name__}")
_validate_string_keys(value, "offload transfers")
missing = sorted(OFFLOAD_COMPONENTS - set(value))
if missing:
raise ValueError("offload transfers require every component; missing: " + ", ".join(missing))
resolved: dict[str, DLOTransfer] = {}
for component, raw_transfer in value.items():
if component not in OFFLOAD_COMPONENTS:
choices = ", ".join(sorted(OFFLOAD_COMPONENTS))
raise ValueError(f"Unknown offload transfer component {component!r}; choose from: {choices}")
resolved[component] = _parse_enum(raw_transfer, DLOTransfer, "offload transfer")
return resolved
def component_uses_allgather(config: Any, component: str = DIT_COMPONENT) -> bool:
"""Return whether one selected component uses AllGather transport."""
resolved = resolve_offload(config)
if not resolved.offloads(component):
raise ValueError(f"Offload component {component!r} is not selected")
return resolved.uses_allgather(component)
def selected_offload_components(config: Any) -> frozenset[str]:
"""Resolve selected components while preserving legacy topology defaults."""
return resolve_offload(config).components
def should_offload_component(config: Any, component: str) -> bool:
"""Return whether an active layer policy selects ``component``."""
if component not in OFFLOAD_COMPONENTS:
raise ValueError(f"Unknown offload component: {component}")
resolved = resolve_offload(config)
if resolved.strategy not in {
OffloadStrategy.LAYER_WISE,
OffloadStrategy.DISTRIBUTED_LAYER_WISE,
}:
return False
return resolved.offloads(component)
def any_selected_component_uses_allgather(config: Any) -> bool:
"""Return whether an enabled layer backend requires weight collectives."""
return resolve_offload(config).any_allgather