From 5bb3f8ae7c9f718529263a354fb87ec3403f0ad1 Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Fri, 22 Aug 2025 00:39:46 -0700 Subject: [PATCH] Renamed SuperEasyCache to LazyCache, hardcoded subsample_factor to 8 on nodes --- comfy_extras/nodes_easycache.py | 56 ++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/comfy_extras/nodes_easycache.py b/comfy_extras/nodes_easycache.py index 45e2e6838..5e588d7f5 100644 --- a/comfy_extras/nodes_easycache.py +++ b/comfy_extras/nodes_easycache.py @@ -77,12 +77,12 @@ def easycache_forward_wrapper(executor, *args, **kwargs): logging.info(f"EasyCache [verbose] - x_prev_subsampled: {easycache.x_prev_subsampled.shape}") return output -def super_easycache_predict_noise_wrapper(executor, *args, **kwargs): +def lazycache_predict_noise_wrapper(executor, *args, **kwargs): # get values from args x: torch.Tensor = args[0] timestep: float = args[1] model_options: dict[str] = args[2] - easycache: SuperEasyCacheHolder = model_options["transformer_options"]["easycache"] + easycache: LazyCacheHolder = model_options["transformer_options"]["easycache"] if easycache.is_past_end_timestep(timestep): return executor(*args, **kwargs) # prepare next x_prev @@ -98,13 +98,13 @@ def super_easycache_predict_noise_wrapper(executor, *args, **kwargs): easycache.cumulative_change_rate += approx_output_change_rate if easycache.cumulative_change_rate < easycache.reuse_threshold: if easycache.verbose: - logging.info(f"EasyCache [verbose] - skipping step; cumulative_change_rate: {easycache.cumulative_change_rate}, reuse_threshold: {easycache.reuse_threshold}") + logging.info(f"LazyCache [verbose] - skipping step; cumulative_change_rate: {easycache.cumulative_change_rate}, reuse_threshold: {easycache.reuse_threshold}") # other conds should also skip this step, and instead use their cached values easycache.skip_current_step = True return easycache.apply_cache_diff(x) else: if easycache.verbose: - logging.info(f"EasyCache [verbose] - NOT skipping step; cumulative_change_rate: {easycache.cumulative_change_rate}, reuse_threshold: {easycache.reuse_threshold}") + logging.info(f"LazyCache [verbose] - NOT skipping step; cumulative_change_rate: {easycache.cumulative_change_rate}, reuse_threshold: {easycache.reuse_threshold}") easycache.cumulative_change_rate = 0.0 output: torch.Tensor = executor(*args, **kwargs) if easycache.has_output_prev_norm(): @@ -116,18 +116,18 @@ def super_easycache_predict_noise_wrapper(executor, *args, **kwargs): approx_output_change_rate = (easycache.relative_transformation_rate * input_change) / easycache.output_prev_norm easycache.approx_output_change_rates.append(approx_output_change_rate.item()) if easycache.verbose: - logging.info(f"EasyCache [verbose] - approx_output_change_rate: {approx_output_change_rate}") + logging.info(f"LazyCache [verbose] - approx_output_change_rate: {approx_output_change_rate}") if input_change is not None: easycache.relative_transformation_rate = output_change / input_change if easycache.verbose: - logging.info(f"EasyCache [verbose] - output_change_rate: {output_change_rate}") + logging.info(f"LazyCache [verbose] - output_change_rate: {output_change_rate}") # TODO: allow cache_diff to be offloaded easycache.update_cache_diff(output, next_x_prev) easycache.x_prev_subsampled = easycache.subsample(next_x_prev) easycache.output_prev_subsampled = easycache.subsample(output) easycache.output_prev_norm = output.flatten().abs().mean() if easycache.verbose: - logging.info(f"EasyCache [verbose] - x_prev_subsampled: {easycache.x_prev_subsampled.shape}") + logging.info(f"LazyCache [verbose] - x_prev_subsampled: {easycache.x_prev_subsampled.shape}") return output def easycache_calc_cond_batch_wrapper(executor, *args, **kwargs): @@ -147,9 +147,11 @@ def easycache_sample_wrapper(executor, *args, **kwargs): guider.model_options = comfy.model_patcher.create_model_options_clone(orig_model_options) # clone and prepare timesteps guider.model_options["transformer_options"]["easycache"] = guider.model_options["transformer_options"]["easycache"].clone().prepare_timesteps(guider.model_patcher.model.model_sampling) + easycache: Union[EasyCacheHolder, LazyCacheHolder] = guider.model_options['transformer_options']['easycache'] + logging.info(f"{easycache.name} enabled - threshold: {easycache.reuse_threshold}, start_percent: {easycache.start_percent}, end_percent: {easycache.end_percent}") return executor(*args, **kwargs) finally: - easycache: Union[EasyCacheHolder, SuperEasyCacheHolder] = guider.model_options['transformer_options']['easycache'] + easycache = guider.model_options['transformer_options']['easycache'] output_change_rates = easycache.output_change_rates approx_output_change_rates = easycache.approx_output_change_rates if easycache.verbose: @@ -315,7 +317,6 @@ class EasyCacheNode(io.ComfyNode): io.Float.Input("reuse_threshold", min=0.0, default=0.2, max=3.0, step=0.01, tooltip="The threshold for reusing cached steps."), io.Float.Input("start_percent", min=0.0, default=0.15, max=1.0, step=0.01, tooltip="The relative sampling step to begin use of EasyCache."), io.Float.Input("end_percent", min=0.0, default=0.95, max=1.0, step=0.01, tooltip="The relative sampling step to end use of EasyCache."), - io.Int.Input("subsample_factor", min=1, default=8, max=128, step=1, tooltip="The factor to subsample latents to cache by."), io.Boolean.Input("verbose", default=False, tooltip="Whether to log verbose information."), ], outputs=[ @@ -324,18 +325,18 @@ class EasyCacheNode(io.ComfyNode): ) @classmethod - def execute(cls, model: io.Model.Type, reuse_threshold: float, start_percent: float, end_percent: float, subsample_factor: int, verbose: bool) -> io.NodeOutput: + def execute(cls, model: io.Model.Type, reuse_threshold: float, start_percent: float, end_percent: float, verbose: bool) -> io.NodeOutput: model = model.clone() - model.model_options["transformer_options"]["easycache"] = EasyCacheHolder(reuse_threshold, start_percent, end_percent, subsample_factor, offload_cache_diff=False, verbose=verbose) + model.model_options["transformer_options"]["easycache"] = EasyCacheHolder(reuse_threshold, start_percent, end_percent, subsample_factor=8, offload_cache_diff=False, verbose=verbose) model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.OUTER_SAMPLE, "easycache", easycache_sample_wrapper) model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.CALC_COND_BATCH, "easycache", easycache_calc_cond_batch_wrapper) model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, "easycache", easycache_forward_wrapper) return io.NodeOutput(model) -class SuperEasyCacheHolder: +class LazyCacheHolder: def __init__(self, reuse_threshold: float, start_percent: float, end_percent: float, subsample_factor: int, offload_cache_diff: bool, verbose: bool=False): - self.name = "SuperEasyCache" + self.name = "LazyCache" self.reuse_threshold = reuse_threshold self.start_percent = start_percent self.end_percent = end_percent @@ -413,36 +414,35 @@ class SuperEasyCacheHolder: return self def clone(self): - return SuperEasyCacheHolder(self.reuse_threshold, self.start_percent, self.end_percent, self.subsample_factor, self.offload_cache_diff, self.verbose) + return LazyCacheHolder(self.reuse_threshold, self.start_percent, self.end_percent, self.subsample_factor, self.offload_cache_diff, self.verbose) -class SuperEasyCacheNode(io.ComfyNode): +class LazyCacheNode(io.ComfyNode): @classmethod def define_schema(cls) -> io.Schema: return io.Schema( - node_id="SuperEasyCache", - display_name="Super EasyCache", - description="Native SuperEasyCache implementation.", + node_id="LazyCache", + display_name="LazyCache", + description="A homebrew version of EasyCache - even 'easier' version of EasyCache to implement. Overall works worse than EasyCache, but better in some rare cases AND universal compatibility with everything in ComfyUI.", category="advanced/debug/model", is_experimental=True, inputs=[ - io.Model.Input("model", tooltip="The model to add SuperEasyCache to."), + io.Model.Input("model", tooltip="The model to add LazyCache to."), io.Float.Input("reuse_threshold", min=0.0, default=0.2, max=3.0, step=0.01, tooltip="The threshold for reusing cached steps."), - io.Float.Input("start_percent", min=0.0, default=0.15, max=1.0, step=0.01, tooltip="The relative sampling step to begin use of EasyCache."), - io.Float.Input("end_percent", min=0.0, default=0.95, max=1.0, step=0.01, tooltip="The relative sampling step to end use of EasyCache."), - io.Int.Input("subsample_factor", min=1, default=8, max=128, step=1, tooltip="The factor to subsample latents to cache by."), + io.Float.Input("start_percent", min=0.0, default=0.15, max=1.0, step=0.01, tooltip="The relative sampling step to begin use of LazyCache."), + io.Float.Input("end_percent", min=0.0, default=0.95, max=1.0, step=0.01, tooltip="The relative sampling step to end use of LazyCache."), io.Boolean.Input("verbose", default=False, tooltip="Whether to log verbose information."), ], outputs=[ - io.Model.Output(tooltip="The model with SuperEasyCache."), + io.Model.Output(tooltip="The model with LazyCache."), ], ) @classmethod - def execute(cls, model: io.Model.Type, reuse_threshold: float, start_percent: float, end_percent: float, subsample_factor: int, verbose: bool) -> io.NodeOutput: + def execute(cls, model: io.Model.Type, reuse_threshold: float, start_percent: float, end_percent: float, verbose: bool) -> io.NodeOutput: model = model.clone() - model.model_options["transformer_options"]["easycache"] = SuperEasyCacheHolder(reuse_threshold, start_percent, end_percent, subsample_factor, offload_cache_diff=False, verbose=verbose) - model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.OUTER_SAMPLE, "easycache", easycache_sample_wrapper) - model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.PREDICT_NOISE, "easycache", super_easycache_predict_noise_wrapper) + model.model_options["transformer_options"]["easycache"] = LazyCacheHolder(reuse_threshold, start_percent, end_percent, subsample_factor=8, offload_cache_diff=False, verbose=verbose) + model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.OUTER_SAMPLE, "lazycache", easycache_sample_wrapper) + model.add_wrapper_with_key(comfy.patcher_extension.WrappersMP.PREDICT_NOISE, "lazycache", lazycache_predict_noise_wrapper) return io.NodeOutput(model) @@ -450,7 +450,7 @@ class EasyCacheExtension(ComfyExtension): async def get_node_list(self) -> list[type[io.ComfyNode]]: return [ EasyCacheNode, - SuperEasyCacheNode, + LazyCacheNode, ] def comfy_entrypoint():