Skip to content

Commit 6d19597

Browse files
committed
fix(mag_cache): stop mutating MagCacheConfig; broadcast per-transformer step count via plain attribute
MagCacheConfig is no longer mutated at runtime. Instead, pipelines broadcast each transformer's actual step count via a plain _mag_cache_expected_steps attribute on the root module, which MagCacheHeadHook/MagCacheBlockHook read live through _get_effective_num_steps/_get_effective_mag_ratios (falling back to config.num_inference_steps when unset, so single-transformer pipelines are unaffected). Addresses both review comments on #14027: - Sharing one MagCacheConfig across transformer/transformer_2 under an asymmetric step split can no longer corrupt it, since it's never mutated. - Pipelines no longer import hook internals (update_mag_cache_num_steps is removed); they only set a plain attribute, keeping pipeline code decoupled from hook implementation details.
1 parent 8bd6e6c commit 6d19597

5 files changed

Lines changed: 51 additions & 35 deletions

File tree

src/diffusers/hooks/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from .hooks import HookRegistry, ModelHook
2424
from .layer_skip import LayerSkipConfig, apply_layer_skip
2525
from .layerwise_casting import apply_layerwise_casting, apply_layerwise_casting_hook
26-
from .mag_cache import MagCacheConfig, apply_mag_cache, update_mag_cache_num_steps
26+
from .mag_cache import MagCacheConfig, apply_mag_cache
2727
from .pyramid_attention_broadcast import PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
2828
from .smoothed_energy_guidance_utils import SmoothedEnergyGuidanceConfig
2929
from .taylorseer_cache import TaylorSeerCacheConfig, apply_taylorseer_cache

src/diffusers/hooks/mag_cache.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,25 @@ def reset(self):
169169
self.calibration_ratios = []
170170

171171

172+
def _get_effective_num_steps(config: MagCacheConfig, root_module: torch.nn.Module) -> int:
173+
expected = getattr(root_module, "_mag_cache_expected_steps", None)
174+
return expected if expected is not None else config.num_inference_steps
175+
176+
177+
def _get_effective_mag_ratios(config: MagCacheConfig, effective_num_steps: int) -> torch.Tensor:
178+
if effective_num_steps == config.num_inference_steps:
179+
return config.mag_ratios
180+
original = getattr(config, "_original_mag_ratios", config.mag_ratios)
181+
return nearest_interp(original, effective_num_steps)
182+
183+
172184
class MagCacheHeadHook(ModelHook):
173185
_is_stateful = True
174186

175-
def __init__(self, state_manager: StateManager, config: MagCacheConfig):
187+
def __init__(self, state_manager: StateManager, config: MagCacheConfig, root_module: torch.nn.Module):
176188
self.state_manager = state_manager
177189
self.config = config
190+
self.root_module = root_module
178191
self._metadata = None
179192

180193
def initialize_hook(self, module):
@@ -200,13 +213,16 @@ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
200213
should_compute = True
201214
else:
202215
# MagCache Logic
216+
effective_num_steps = _get_effective_num_steps(self.config, self.root_module)
217+
effective_mag_ratios = _get_effective_mag_ratios(self.config, effective_num_steps)
218+
203219
current_step = state.step_index
204-
if current_step >= len(self.config.mag_ratios):
220+
if current_step >= len(effective_mag_ratios):
205221
current_scale = 1.0
206222
else:
207-
current_scale = self.config.mag_ratios[current_step]
223+
current_scale = effective_mag_ratios[current_step]
208224

209-
retention_step = int(self.config.retention_ratio * self.config.num_inference_steps + 0.5)
225+
retention_step = int(self.config.retention_ratio * effective_num_steps + 0.5)
210226

211227
if current_step >= retention_step:
212228
state.accumulated_ratio *= current_scale
@@ -286,11 +302,18 @@ def reset_state(self, module):
286302

287303

288304
class MagCacheBlockHook(ModelHook):
289-
def __init__(self, state_manager: StateManager, is_tail: bool = False, config: MagCacheConfig = None):
305+
def __init__(
306+
self,
307+
state_manager: StateManager,
308+
is_tail: bool = False,
309+
config: MagCacheConfig = None,
310+
root_module: torch.nn.Module = None,
311+
):
290312
super().__init__()
291313
self.state_manager = state_manager
292314
self.is_tail = is_tail
293315
self.config = config
316+
self.root_module = root_module
294317
self._metadata = None
295318

296319
def initialize_hook(self, module):
@@ -379,7 +402,8 @@ def _perform_calibration_step(self, state: MagCacheState, current_residual: torc
379402

380403
def _advance_step(self, state: MagCacheState):
381404
state.step_index += 1
382-
if state.step_index >= self.config.num_inference_steps:
405+
effective_num_steps = _get_effective_num_steps(self.config, self.root_module)
406+
if state.step_index >= effective_num_steps:
383407
# End of inference loop
384408
if self.config.calibrate:
385409
print("\n[MagCache] Calibration Complete. Copy these values to MagCacheConfig(mag_ratios=...):")
@@ -426,38 +450,44 @@ def apply_mag_cache(module: torch.nn.Module, config: MagCacheConfig) -> None:
426450
if len(remaining_blocks) == 1:
427451
name, block = remaining_blocks[0]
428452
logger.info(f"MagCache: Applying Head+Tail Hooks to single block '{name}'")
429-
_apply_mag_cache_block_hook(block, state_manager, config, is_tail=True)
430-
_apply_mag_cache_head_hook(block, state_manager, config)
453+
_apply_mag_cache_block_hook(block, state_manager, config, module, is_tail=True)
454+
_apply_mag_cache_head_hook(block, state_manager, config, module)
431455
return
432456

433457
head_block_name, head_block = remaining_blocks.pop(0)
434458
tail_block_name, tail_block = remaining_blocks.pop(-1)
435459

436460
logger.info(f"MagCache: Applying Head Hook to {head_block_name}")
437-
_apply_mag_cache_head_hook(head_block, state_manager, config)
461+
_apply_mag_cache_head_hook(head_block, state_manager, config, module)
438462

439463
for name, block in remaining_blocks:
440-
_apply_mag_cache_block_hook(block, state_manager, config)
464+
_apply_mag_cache_block_hook(block, state_manager, config, module)
441465

442466
logger.info(f"MagCache: Applying Tail Hook to {tail_block_name}")
443-
_apply_mag_cache_block_hook(tail_block, state_manager, config, is_tail=True)
467+
_apply_mag_cache_block_hook(tail_block, state_manager, config, module, is_tail=True)
444468

445469

446-
def _apply_mag_cache_head_hook(block: torch.nn.Module, state_manager: StateManager, config: MagCacheConfig) -> None:
470+
def _apply_mag_cache_head_hook(
471+
block: torch.nn.Module,
472+
state_manager: StateManager,
473+
config: MagCacheConfig,
474+
root_module: torch.nn.Module,
475+
) -> None:
447476
registry = HookRegistry.check_if_exists_or_initialize(block)
448477

449478
# Automatically remove existing hook to allow re-application (e.g. switching modes)
450479
if registry.get_hook(_MAG_CACHE_LEADER_BLOCK_HOOK) is not None:
451480
registry.remove_hook(_MAG_CACHE_LEADER_BLOCK_HOOK)
452481

453-
hook = MagCacheHeadHook(state_manager, config)
482+
hook = MagCacheHeadHook(state_manager, config, root_module)
454483
registry.register_hook(hook, _MAG_CACHE_LEADER_BLOCK_HOOK)
455484

456485

457486
def _apply_mag_cache_block_hook(
458487
block: torch.nn.Module,
459488
state_manager: StateManager,
460489
config: MagCacheConfig,
490+
root_module: torch.nn.Module,
461491
is_tail: bool = False,
462492
) -> None:
463493
registry = HookRegistry.check_if_exists_or_initialize(block)
@@ -466,17 +496,6 @@ def _apply_mag_cache_block_hook(
466496
if registry.get_hook(_MAG_CACHE_BLOCK_HOOK) is not None:
467497
registry.remove_hook(_MAG_CACHE_BLOCK_HOOK)
468498

469-
hook = MagCacheBlockHook(state_manager, is_tail, config)
499+
hook = MagCacheBlockHook(state_manager, is_tail, config, root_module)
470500
registry.register_hook(hook, _MAG_CACHE_BLOCK_HOOK)
471501

472-
473-
def update_mag_cache_num_steps(module: torch.nn.Module, num_steps: int) -> None:
474-
config: MagCacheConfig = getattr(module, "_mag_cache_config", None)
475-
if config is None:
476-
return
477-
original_ratios = getattr(config, "_original_mag_ratios", config.mag_ratios)
478-
config.num_inference_steps = num_steps
479-
if original_ratios is not None:
480-
config.mag_ratios = nearest_interp(original_ratios, num_steps)
481-
482-

src/diffusers/pipelines/wan/pipeline_wan.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -587,13 +587,12 @@ def __call__(
587587
boundary_timestep = None
588588

589589
if boundary_timestep is not None:
590-
from ...hooks.mag_cache import update_mag_cache_num_steps
591590
n_steps_t1 = sum(1 for t in timesteps if t >= boundary_timestep)
592591
n_steps_t2 = len(timesteps) - n_steps_t1
593592
if self.transformer is not None:
594-
update_mag_cache_num_steps(self.transformer, n_steps_t1)
593+
self.transformer._mag_cache_expected_steps = n_steps_t1
595594
if self.transformer_2 is not None:
596-
update_mag_cache_num_steps(self.transformer_2, n_steps_t2)
595+
self.transformer_2._mag_cache_expected_steps = n_steps_t2
597596

598597
with self.progress_bar(total=num_inference_steps) as progress_bar:
599598
for i, t in enumerate(timesteps):

src/diffusers/pipelines/wan/pipeline_wan_i2v.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -742,13 +742,12 @@ def __call__(
742742
boundary_timestep = None
743743

744744
if boundary_timestep is not None:
745-
from ...hooks.mag_cache import update_mag_cache_num_steps
746745
n_steps_t1 = sum(1 for t in timesteps if t >= boundary_timestep)
747746
n_steps_t2 = len(timesteps) - n_steps_t1
748747
if self.transformer is not None:
749-
update_mag_cache_num_steps(self.transformer, n_steps_t1)
748+
self.transformer._mag_cache_expected_steps = n_steps_t1
750749
if self.transformer_2 is not None:
751-
update_mag_cache_num_steps(self.transformer_2, n_steps_t2)
750+
self.transformer_2._mag_cache_expected_steps = n_steps_t2
752751

753752
with self.progress_bar(total=num_inference_steps) as progress_bar:
754753
for i, t in enumerate(timesteps):

src/diffusers/pipelines/wan/pipeline_wan_vace.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -956,13 +956,12 @@ def __call__(
956956
boundary_timestep = None
957957

958958
if boundary_timestep is not None:
959-
from ...hooks.mag_cache import update_mag_cache_num_steps
960959
n_steps_t1 = sum(1 for t in timesteps if t >= boundary_timestep)
961960
n_steps_t2 = len(timesteps) - n_steps_t1
962961
if self.transformer is not None:
963-
update_mag_cache_num_steps(self.transformer, n_steps_t1)
962+
self.transformer._mag_cache_expected_steps = n_steps_t1
964963
if self.transformer_2 is not None:
965-
update_mag_cache_num_steps(self.transformer_2, n_steps_t2)
964+
self.transformer_2._mag_cache_expected_steps = n_steps_t2
966965

967966
with self.progress_bar(total=num_inference_steps) as progress_bar:
968967
for i, t in enumerate(timesteps):

0 commit comments

Comments
 (0)