55import json
66import math
77import time
8+ from concurrent .futures import ThreadPoolExecutor
89from dataclasses import dataclass , field
910from typing import Any , Callable , Dict , List , Optional , Union
1011
4950_FP8_DTYPES = (torch .float8_e4m3fn , torch .float8_e5m2 )
5051# Baseline BF16 peak memory ~75 GiB, saving BF16 weights snapshot total ~108 GiB.
5152_BF16_WEIGHTS_SNAPSHOT_FREE_MEMORY_THRESHOLD_GIB = 115.0
53+ _QKV_SUFFIXES = (".to_q" , ".to_k" , ".to_v" )
5254
5355
5456# ---------------------------------------------------------------------------
@@ -99,6 +101,36 @@ def _should_save_bf16_weights(
99101 return save_state
100102
101103
104+ def _map_lora_param_name (base_name : str , strip_prefixes : List [str ]) -> str :
105+ param_name = base_name
106+ for prefix in strip_prefixes :
107+ if param_name .startswith (prefix ):
108+ param_name = param_name [len (prefix ) :]
109+ break
110+
111+ # Apply the same key remapping as LTXModel.load_weights() so LoRA delta
112+ # keys match TRT-LLM parameter names.
113+ for ff_prefix in (".ff." , ".audio_ff." ):
114+ if ff_prefix + "net.0.proj" in param_name :
115+ param_name = param_name .replace (ff_prefix + "net.0.proj" , ff_prefix + "up_proj" )
116+ elif ff_prefix + "net.2" in param_name :
117+ param_name = param_name .replace (ff_prefix + "net.2" , ff_prefix + "down_proj" )
118+ param_name = param_name .replace (".q_norm." , ".norm_q." )
119+ param_name = param_name .replace (".k_norm." , ".norm_k." )
120+ return param_name
121+
122+
123+ def _has_lora_target (param_name : str , model_params : set [str ]) -> bool :
124+ if param_name in model_params or f"{ param_name } .weight" in model_params :
125+ return True
126+
127+ for suffix in _QKV_SUFFIXES :
128+ if param_name .endswith (suffix ):
129+ attn_prefix = param_name [: - len (suffix )]
130+ return f"{ attn_prefix } .qkv_proj.weight" in model_params
131+ return False
132+
133+
102134def _load_lora_deltas (
103135 lora_path : str ,
104136 transformer : torch .nn .Module ,
@@ -117,7 +149,9 @@ def _load_lora_deltas(
117149 sft_paths = _find_safetensors_files (lora_path )
118150 if not sft_paths :
119151 raise ValueError (f"No safetensors files found at { lora_path } " )
120- _prefetch_ltx2_safetensors_files (sft_paths )
152+ # This helper can run in a background thread while base components load.
153+ # Avoid distributed prefetch collectives here; every rank must enter those
154+ # from the same foreground load sequence to avoid hangs.
121155
122156 raw : Dict [str , torch .Tensor ] = {}
123157 alpha_dict : Dict [str , float ] = {}
@@ -163,39 +197,28 @@ def _strip(key, suffixes):
163197 if suffix :
164198 strip_prefixes .append (suffix )
165199
200+ model_params = {n for n , _ in transformer .named_parameters ()}
166201 deltas : Dict [str , torch .Tensor ] = {}
202+ skipped_non_targets = 0
167203 for base_name in down_keys :
168204 if base_name not in up_keys :
169205 continue
206+
207+ param_name = _map_lora_param_name (base_name , strip_prefixes )
208+ if not _has_lora_target (param_name , model_params ):
209+ skipped_non_targets += 1
210+ continue
211+
170212 A = down_keys [base_name ] # (rank, in_features)
171213 B = up_keys [base_name ] # (out_features, rank)
172214 rank = A .shape [0 ]
173215 alpha = alpha_dict .get (base_name , float (rank ))
174216 scale = strength * alpha / rank
175-
176- param_name = base_name
177- for prefix in strip_prefixes :
178- if param_name .startswith (prefix ):
179- param_name = param_name [len (prefix ) :]
180- break
181-
182- # Apply the same key remapping as LTXModel.load_weights() so
183- # that LoRA delta keys match TRT-LLM parameter names.
184- for ff_prefix in (".ff." , ".audio_ff." ):
185- if ff_prefix + "net.0.proj" in param_name :
186- param_name = param_name .replace (ff_prefix + "net.0.proj" , ff_prefix + "up_proj" )
187- elif ff_prefix + "net.2" in param_name :
188- param_name = param_name .replace (ff_prefix + "net.2" , ff_prefix + "down_proj" )
189- param_name = param_name .replace (".q_norm." , ".norm_q." )
190- param_name = param_name .replace (".k_norm." , ".norm_k." )
191-
192217 delta = (B .float () @ A .float ()) * scale
193218 deltas [param_name ] = delta
194219
195220 # Fuse separate to_q / to_k / to_v deltas into a single qkv_proj
196221 # delta when the transformer uses QKV fusion (FUSE_QKV mode).
197- model_params = {n for n , _ in transformer .named_parameters ()}
198- _QKV_SUFFIXES = (".to_q" , ".to_k" , ".to_v" )
199222 fused_keys : set = set ()
200223 qkv_groups : Dict [str , List [str ]] = {}
201224 for key in list (deltas .keys ()):
@@ -223,7 +246,10 @@ def _strip(key, suffixes):
223246 for key in fused_keys :
224247 del deltas [key ]
225248
226- logger .info (f"Loaded { len (deltas )} LoRA deltas from { lora_path } (strength={ strength } )" )
249+ logger .info (
250+ f"Loaded { len (deltas )} LoRA deltas from { lora_path } "
251+ f"(strength={ strength } , skipped_non_targets={ skipped_non_targets } )"
252+ )
227253 return deltas
228254
229255
@@ -967,17 +993,49 @@ def load_standard_components(
967993 # The BF16 snapshot threshold is a whole-pipeline capacity gate, so
968994 # record it before loading model/runtime components that consume HBM.
969995 self ._bf16_snapshot_preload_free_gib = _get_free_gpu_memory_gib (device = device )
970- super ().load_standard_components (
971- checkpoint_dir ,
972- device ,
973- skip_components = skip_components ,
974- ** kwargs ,
975- )
976-
977996 dtype = self .pipeline_config .torch_dtype
978997 spatial_upsampler_path = self .pipeline_config .extra_attrs .get ("spatial_upsampler_path" , "" )
979998 distilled_lora_path = self .pipeline_config .extra_attrs .get ("distilled_lora_path" , "" )
980999
1000+ lora_executor = None
1001+ lora_future = None
1002+ if distilled_lora_path :
1003+ logger .info (f"Starting distilled LoRA pre-compute from { distilled_lora_path } ..." )
1004+ lora_executor = ThreadPoolExecutor (max_workers = 1 )
1005+ lora_future = lora_executor .submit (
1006+ _load_lora_deltas ,
1007+ distilled_lora_path ,
1008+ self .transformer ,
1009+ self ._TRANSFORMER_PREFIX ,
1010+ )
1011+
1012+ try :
1013+ super ().load_standard_components (
1014+ checkpoint_dir ,
1015+ device ,
1016+ skip_components = skip_components ,
1017+ ** kwargs ,
1018+ )
1019+
1020+ self ._load_two_stage_components (
1021+ device ,
1022+ dtype ,
1023+ spatial_upsampler_path ,
1024+ distilled_lora_path ,
1025+ lora_future ,
1026+ )
1027+ finally :
1028+ if lora_executor is not None :
1029+ lora_executor .shutdown (wait = True )
1030+
1031+ def _load_two_stage_components (
1032+ self ,
1033+ device : torch .device ,
1034+ dtype : torch .dtype ,
1035+ spatial_upsampler_path : str ,
1036+ distilled_lora_path : str ,
1037+ lora_future ,
1038+ ) -> None :
9811039 # --- Spatial upsampler ---
9821040 if spatial_upsampler_path :
9831041 logger .info (f"Loading spatial upsampler from { spatial_upsampler_path } ..." )
@@ -1018,12 +1076,10 @@ def load_standard_components(
10181076 self ._distilled_lora_deltas : Dict [str , torch .Tensor ] = {}
10191077 self ._distilled_lora_weight_cache : Optional [_PersistentLoRAWeightCache ] = None
10201078 if distilled_lora_path :
1021- logger .info (f"Loading distilled LoRA from { distilled_lora_path } ..." )
1022- self ._distilled_lora_deltas = _load_lora_deltas (
1023- distilled_lora_path ,
1024- self .transformer ,
1025- transformer_prefix = self ._TRANSFORMER_PREFIX ,
1026- )
1079+ logger .info ("Waiting for distilled LoRA pre-compute..." )
1080+ if lora_future is None :
1081+ raise RuntimeError ("Distilled LoRA pre-compute was not started." )
1082+ self ._distilled_lora_deltas = lora_future .result ()
10271083 logger .info (
10281084 f"Distilled LoRA ready: { len (self ._distilled_lora_deltas )} parameter deltas"
10291085 )
0 commit comments