Files
hao-ai-lab-FastVideo/fastvideo/api/device_policy.py
T
Davids048andClaude Opus 5.5 6c776a219b [refactor]: run the inference path on the resolved config
ResolvedGeneratorConfig becomes the runtime config of every mode. Resolution
builds the model's PipelineConfig once, freezes it, and attaches it as
resolved_config.pipeline_config; it also exposes mode, training_mode, and
inference_mode. VideoGenerator, fastvideo serve, the OpenAI server state,
the executors, the worker, and ComposedPipelineBase receive the resolved
config instead of a FastVideoArgs, and generator_config_to_fastvideo_args
is no longer on the inference path.

- Schema: ExecutionMode and WorkloadType move to fastvideo/api/schema.py;
  typed homes for mode, output_type, boundary_ratio, master_port,
  moba_config, and the LTX-2 refine settings; Enum parsing.
- Resolution: steps for the refine preset overrides, flat experimental
  keys, the V-MoBA config, the validation that check_fastvideo_args did,
  the num_gpus bump, the model's vae_tiling default, and a final
  fill_runtime_defaults step for typed fields no earlier step decides.
- fastvideo/api/training_schema.py: TrainingRunConfig and
  PreprocessRunConfig roots, their resolvers, and a --config plus dotted
  override loader.
- fastvideo/api/device_policy.py: the device offload policy as functions
  that return an overridden config; the worker keeps the result. The LTX-2
  and MiniMax-H3 checkpoint defaults rebind the pipeline's config.
- fastvideo/api/flat_name_fallback.py answers the flat FastVideoArgs names
  on the resolved config until the readers move to typed paths.

Golden changes: the decisions of the steps above are recorded, and
FastVideoArgs.ltx2_vae_tiling holds the model's vae_tiling (it held None;
no runtime code reads it). Against the 3f6893a0 goldens every
FastVideoArgs field, PipelineConfig, and SamplingParam matches, except
disable_autocast and boundary_ratio, which FastVideoArgs held at their
defaults because flattening routed them only to PipelineConfig.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012M91wnVFmPEvJ9h39r5BH7
2026-10-03 05:35:14 +00:00

147 lines
6.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""Offload decisions that depend on the device a worker binds, as overrides of a resolved config.
Resolution runs before any device is chosen, so it cannot tell whether host and device memory are one physical pool.
A worker calls :func:`finalize_device_offload_policy` once after it binds its device and keeps the returned config.
Each decision is a recorded override (``device_policy:<name>``) on a new config object; the input is unchanged.
:func:`offload_disabled_on_unified_memory` answers the same question without overriding, for a loader that chooses a
target device for one component.
"""
from __future__ import annotations
from typing import Any
from fastvideo.api.resolution import ResolvedGeneratorConfig
from fastvideo.logger import init_logger
logger = init_logger(__name__)
# Offload settings that trade device memory for host memory, by flat name. All of them are a loss on a device whose
# host and device memory are the same physical pool.
UNIFIED_MEMORY_OFFLOAD_PATHS = {
"dit_layerwise_offload": "engine.offload.dit_layerwise",
"dit_cpu_offload": "engine.offload.dit",
"text_encoder_cpu_offload": "engine.offload.text_encoder",
"image_encoder_cpu_offload": "engine.offload.image_encoder",
"vae_cpu_offload": "engine.offload.vae",
}
def _device_name(device_id: int) -> str:
"""Device name for log messages; the platform name when the device cannot be named."""
from fastvideo.platforms import current_platform
try:
return current_platform.get_device_name(device_id)
except Exception:
# Device naming is diagnostic only. NVML can be unavailable on an integrated GPU (for example Jetson), and
# its physical-ordinal lookup cannot interpret CUDA_VISIBLE_DEVICES UUID/MIG selectors.
return current_platform.device_name
def has_unified_memory(device_id: int) -> bool:
"""Whether the bound device shares one memory pool with the host.
CUDA's probe reads runtime device properties and may initialize a CUDA context, so call it only in a process
that owns ``device_id``.
"""
from fastvideo.platforms import current_platform
return bool(current_platform.has_unified_memory(device_id))
def offload_disabled_on_unified_memory(device_id: int, offload_flag: str | None = None) -> bool:
"""Whether the unified-memory policy turns off ``offload_flag`` (any offload when ``None``) on ``device_id``."""
if not has_unified_memory(device_id):
return False
return offload_flag is None or offload_flag in UNIFIED_MEMORY_OFFLOAD_PATHS
def disable_offload_on_unified_memory(resolved_config: ResolvedGeneratorConfig,
device_id: int,
*,
unified: bool | None = None) -> ResolvedGeneratorConfig:
"""Turn off every enabled host offload when ``device_id`` has unified memory.
``unified`` is the probe result when the caller already has it. The decision is the override
``device_policy:unified_memory``.
"""
if unified is None:
unified = has_unified_memory(device_id)
if not unified:
return resolved_config
enabled = [flag for flag, path in UNIFIED_MEMORY_OFFLOAD_PATHS.items() if _typed_value(resolved_config, path)]
if not enabled:
return resolved_config
device_name = _device_name(device_id)
for flag in enabled:
logger.info(
"Disabling %s: %s has unified memory, so moving weights to the host duplicates them rather than "
"freeing device memory.", flag, device_name)
return resolved_config.with_override("device_policy:unified_memory",
{UNIFIED_MEMORY_OFFLOAD_PATHS[flag]: False
for flag in enabled})
def resolve_device_offload_conflicts(resolved_config: ResolvedGeneratorConfig) -> ResolvedGeneratorConfig:
"""Turn off offload modes that cannot run together on this platform.
On MPS, FSDP inference and layerwise offload turn off. With layerwise offload on, FSDP inference and DiT CPU
offload turn off.
"""
from fastvideo.platforms import current_platform
if current_platform.is_mps():
resolved_config = resolved_config.with_override("device_policy:mps", {
"engine.use_fsdp_inference": False,
"engine.offload.dit_layerwise": False
})
if _typed_value(resolved_config, "engine.offload.dit_layerwise"):
if _typed_value(resolved_config, "engine.use_fsdp_inference"):
logger.warning("dit_layerwise_offload is enabled, automatically disabling use_fsdp_inference.")
resolved_config = resolved_config.with_override("device_policy:layerwise_offload",
{"engine.use_fsdp_inference": False})
if _typed_value(resolved_config, "engine.offload.dit"):
logger.warning("dit_layerwise_offload is enabled, automatically disabling dit_cpu_offload.")
resolved_config = resolved_config.with_override("device_policy:layerwise_offload",
{"engine.offload.dit": False})
return resolved_config
def finalize_device_offload_policy(resolved_config: ResolvedGeneratorConfig,
device_id: int = 0) -> ResolvedGeneratorConfig:
"""Apply the device-local memory policy of ``device_id``, then resolve incompatible offload modes.
On unified memory every host offload turns off. An unset ``engine.offload.lazy_module_load`` becomes ``True`` on
unified memory for inference and ``False`` otherwise. Applying the policy to its own result changes nothing.
"""
unified = has_unified_memory(device_id)
resolved_config = disable_offload_on_unified_memory(resolved_config, device_id, unified=unified)
if _typed_value(resolved_config, "engine.offload.lazy_module_load") is None:
lazy_module_load = unified and not resolved_config.training_mode
resolved_config = resolved_config.with_override("device_policy:lazy_module_load",
{"engine.offload.lazy_module_load": lazy_module_load})
if lazy_module_load:
logger.info(
"Enabling lazy_module_load: %s has unified memory, so encoder, DiT, and VAEs cannot stay "
"resident together. Pass --no-lazy-module-load to keep every component loaded.",
_device_name(device_id),
)
return resolve_device_offload_conflicts(resolved_config)
def _typed_value(resolved_config: ResolvedGeneratorConfig, path: str) -> Any:
"""The resolved value of a dotted path."""
return resolved_config.provenance(path).value
__all__ = [
"UNIFIED_MEMORY_OFFLOAD_PATHS",
"disable_offload_on_unified_memory",
"finalize_device_offload_policy",
"has_unified_memory",
"offload_disabled_on_unified_memory",
"resolve_device_offload_conflicts",
]