2525
2626logger = init_logger (__name__ )
2727
28- # Offload flags that trade device memory for host memory. Keeping the policy
29- # centralized lets components share the same worker-local device decision.
30- UNIFIED_MEMORY_OFFLOAD_FLAGS = ("text_encoder_cpu_offload" , )
28+ # Offload flags that trade device memory for host memory. All of them are a loss
29+ # on a device where the two are the same physical pool. Keeping the policy
30+ # centralized lets every loader and stage share one worker-local decision.
31+ UNIFIED_MEMORY_OFFLOAD_FLAGS = (
32+ "dit_layerwise_offload" ,
33+ "dit_cpu_offload" ,
34+ "text_encoder_cpu_offload" ,
35+ "image_encoder_cpu_offload" ,
36+ "vae_cpu_offload" ,
37+ )
3138
3239
3340class ExecutionMode (str , Enum ):
@@ -854,20 +861,6 @@ def from_kwargs(cls, **kwargs: Any) -> "FastVideoArgs":
854861
855862 def check_fastvideo_args (self ) -> None :
856863 """Validate inference arguments for consistency"""
857- from fastvideo .platforms import current_platform
858-
859- if current_platform .is_mps ():
860- self .use_fsdp_inference = False
861- self .dit_layerwise_offload = False
862-
863- if self .dit_layerwise_offload :
864- if self .use_fsdp_inference :
865- logger .warning ("dit_layerwise_offload is enabled, automatically disabling use_fsdp_inference." )
866- self .use_fsdp_inference = False
867- if self .dit_cpu_offload :
868- logger .warning ("dit_layerwise_offload is enabled, automatically disabling dit_cpu_offload." )
869- self .dit_cpu_offload = False
870-
871864 # Validate mode and inference_mode consistency
872865 assert isinstance (self .mode , ExecutionMode ), f"Mode must be an ExecutionMode enum, got { type (self .mode )} "
873866 assert self .mode in ExecutionMode .choices (), f"Invalid execution mode: { self .mode } "
@@ -884,6 +877,14 @@ def check_fastvideo_args(self) -> None:
884877 logger .warning ("Mode is '%s' but inference_mode is False. Setting inference_mode to True." , self .mode )
885878 self .inference_mode = True
886879
880+ # Inference policy must wait until a worker owns and binds its device:
881+ # a unified-memory device disables layerwise offload before conflicts
882+ # are resolved, preserving an explicit FSDP request. Training does not
883+ # pass through the inference worker boundary, so retain its historical
884+ # constructor-time normalization.
885+ if not self .inference_mode :
886+ self ._resolve_device_offload_conflicts ()
887+
887888 if not self .inference_mode :
888889 assert self .hsdp_replicate_dim != - 1 , "hsdp_replicate_dim must be set for training"
889890 assert self .hsdp_shard_dim != - 1 , "hsdp_shard_dim must be set for training"
@@ -918,6 +919,28 @@ def check_fastvideo_args(self) -> None:
918919 self .pipeline_config .vae_config .load_encoder = True
919920 self .preprocess_config .check_preprocess_config ()
920921
922+ def _resolve_device_offload_conflicts (self ) -> None :
923+ """Resolve offload modes after device-local policy has been applied."""
924+ from fastvideo .platforms import current_platform
925+
926+ if current_platform .is_mps ():
927+ self .use_fsdp_inference = False
928+ self .dit_layerwise_offload = False
929+
930+ if self .dit_layerwise_offload :
931+ if self .use_fsdp_inference :
932+ logger .warning ("dit_layerwise_offload is enabled, automatically disabling use_fsdp_inference." )
933+ self .use_fsdp_inference = False
934+ if self .dit_cpu_offload :
935+ logger .warning ("dit_layerwise_offload is enabled, automatically disabling dit_cpu_offload." )
936+ self .dit_cpu_offload = False
937+
938+ def finalize_device_offload_policy (self , device_id : int = 0 ) -> bool :
939+ """Apply device-local memory policy, then resolve incompatible modes."""
940+ has_unified_memory = self .disable_offload_on_unified_memory (device_id )
941+ self ._resolve_device_offload_conflicts ()
942+ return has_unified_memory
943+
921944 def disable_offload_on_unified_memory (self , device_id : int = 0 , * , offload_flag : str | None = None ) -> bool :
922945 """Disable host offload after a worker has selected its device.
923946
@@ -931,20 +954,29 @@ def disable_offload_on_unified_memory(self, device_id: int = 0, *, offload_flag:
931954 """
932955 from fastvideo .platforms import current_platform
933956
934- if not current_platform .has_unified_memory (device_id ):
957+ cached_device_id = getattr (self , "_unified_memory_device_id" , None )
958+ cached_result = getattr (self , "_unified_memory_result" , None )
959+ if cached_device_id != device_id or cached_result is None :
960+ cached_result = current_platform .has_unified_memory (device_id )
961+ self ._unified_memory_device_id = device_id
962+ self ._unified_memory_result = cached_result
963+
964+ if not cached_result :
935965 return False
936966
937- try :
938- device_name = current_platform .get_device_name (device_id )
939- except Exception :
940- # Device naming is diagnostic only. NVML can be unavailable on an
941- # integrated GPU (for example Jetson), and its physical-ordinal
942- # lookup cannot interpret CUDA_VISIBLE_DEVICES UUID/MIG selectors.
943- # Neither case should undo an authoritative driver classification.
944- device_name = current_platform .device_name
945-
946- for flag in UNIFIED_MEMORY_OFFLOAD_FLAGS :
947- if getattr (self , flag ):
967+ enabled_flags = [flag for flag in UNIFIED_MEMORY_OFFLOAD_FLAGS if getattr (self , flag )]
968+ if enabled_flags :
969+ try :
970+ device_name = current_platform .get_device_name (device_id )
971+ except Exception :
972+ # Device naming is diagnostic only. NVML can be unavailable on
973+ # an integrated GPU (for example Jetson), and its physical-
974+ # ordinal lookup cannot interpret CUDA_VISIBLE_DEVICES UUID/MIG
975+ # selectors. Neither case should undo an authoritative driver
976+ # classification.
977+ device_name = current_platform .device_name
978+
979+ for flag in enabled_flags :
948980 logger .info (
949981 "Disabling %s: %s has unified memory, so moving weights to the host duplicates "
950982 "them rather than freeing device memory." , flag , device_name )
0 commit comments