From a7e0723dcfa66fe36f4e2ffc1b3dc1d16e19024a Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Wed, 18 Dec 2024 13:02:22 -0500 Subject: [PATCH] profiling Signed-off-by: Vladimir Mandic --- CHANGELOG.md | 1 + modules/errors.py | 5 +++-- modules/ipadapter.py | 7 ++++++- modules/processing.py | 4 +++- modules/processing_diffusers.py | 4 ++++ modules/prompt_parser_diffusers.py | 2 ++ modules/shared.py | 5 ++++- modules/textual_inversion/textual_inversion.py | 2 ++ modules/timer.py | 6 +++++- 9 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3211a4752..1931bd2b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -124,6 +124,7 @@ ### Fixes - add `SD_NO_CACHE=true` env variable to disable file/folder caching +- add settings -> networks -> embeddings -> enable/disable - update `diffusers` - fix README links - fix sdxl controlnet single-file loader diff --git a/modules/errors.py b/modules/errors.py index 527884cf1..5f2c54cb7 100644 --- a/modules/errors.py +++ b/modules/errors.py @@ -59,14 +59,14 @@ def exception(suppress=[]): console.print_exception(show_locals=False, max_frames=16, extra_lines=2, suppress=suppress, theme="ansi_dark", word_wrap=False, width=min([console.width, 200])) -def profile(profiler, msg: str, n: int = 5): +def profile(profiler, msg: str, n: int = 16): profiler.disable() import io import pstats stream = io.StringIO() # pylint: disable=abstract-class-instantiated p = pstats.Stats(profiler, stream=stream) p.sort_stats(pstats.SortKey.CUMULATIVE) - p.print_stats(100) + p.print_stats(200) # p.print_title() # p.print_call_heading(10, 'time') # p.print_callees(10) @@ -81,6 +81,7 @@ def profile(profiler, msg: str, n: int = 5): and '_lsprof' not in x and '/profiler' not in x and 'rich' not in x + and 'profile_torch' not in x and x.strip() != '' ] txt = '\n'.join(lines[:min(n, len(lines))]) diff --git a/modules/ipadapter.py b/modules/ipadapter.py index d5bfbec8c..4e93a6eee 100644 --- a/modules/ipadapter.py +++ b/modules/ipadapter.py @@ -14,6 +14,7 @@ from modules import processing, shared, devices, sd_models clip_repo = "h94/IP-Adapter" clip_loaded = None +adapters_loaded = [] ADAPTERS_NONE = { 'None': { 'name': 'none', 'repo': 'none', 'subfolder': 'none' }, } @@ -129,9 +130,12 @@ def crop_images(images, crops): def unapply(pipe): # pylint: disable=arguments-differ + if len(adapters_loaded) == 0: + return try: if hasattr(pipe, 'set_ip_adapter_scale'): pipe.set_ip_adapter_scale(0) + pipe.unload_ip_adapter() if hasattr(pipe, 'unet') and hasattr(pipe.unet, 'config') and pipe.unet.config.encoder_hid_dim_type == 'ip_image_proj': pipe.unet.encoder_hid_proj = None pipe.config.encoder_hid_dim_type = None @@ -141,7 +145,7 @@ def unapply(pipe): # pylint: disable=arguments-differ def apply(pipe, p: processing.StableDiffusionProcessing, adapter_names=[], adapter_scales=[1.0], adapter_crops=[False], adapter_starts=[0.0], adapter_ends=[1.0], adapter_images=[]): - global clip_loaded # pylint: disable=global-statement + global clip_loaded, adapters_loaded # pylint: disable=global-statement # overrides if hasattr(p, 'ip_adapter_names'): if isinstance(p.ip_adapter_names, str): @@ -274,6 +278,7 @@ def apply(pipe, p: processing.StableDiffusionProcessing, adapter_names=[], adapt subfolders = [adapter['subfolder'] for adapter in adapters] names = [adapter['name'] for adapter in adapters] pipe.load_ip_adapter(repos, subfolder=subfolders, weight_name=names) + adapters_loaded = names if hasattr(p, 'ip_adapter_layers'): pipe.set_ip_adapter_scale(p.ip_adapter_layers) ip_str = ';'.join(adapter_names) + ':' + json.dumps(p.ip_adapter_layers) diff --git a/modules/processing.py b/modules/processing.py index b4839e402..4bc5e3c81 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -179,6 +179,8 @@ def process_images(p: StableDiffusionProcessing) -> Processed: timer.process.record('pre') if shared.cmd_opts.profile: + timer.startup.profile = True + timer.process.profile = True with context_hypertile_vae(p), context_hypertile_unet(p): import torch.profiler # pylint: disable=redefined-outer-name activities=[torch.profiler.ProfilerActivity.CPU] @@ -476,7 +478,7 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: p.scripts.postprocess(p, processed) timer.process.record('post') if not p.disable_extra_networks: - shared.log.info(f'Processed: images={len(output_images)} its={(p.steps * len(output_images)) / (t1 - t0):.2f} time={t1-t0:.2f} timers={timer.process.dct(min_time=0.02)} memory={memstats.memory_stats()}') + shared.log.info(f'Processed: images={len(output_images)} its={(p.steps * len(output_images)) / (t1 - t0):.2f} time={t1-t0:.2f} timers={timer.process.dct()} memory={memstats.memory_stats()}') devices.torch_gc(force=True) return processed diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index adb047511..581589262 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -4,6 +4,7 @@ import time import numpy as np import torch import torchvision.transforms.functional as TF +from PIL import Image from modules import shared, devices, processing, sd_models, errors, sd_hijack_hypertile, processing_vae, sd_models_compile, hidiffusion, timer, modelstats, extra_networks from modules.processing_helpers import resize_hires, calculate_base_steps, calculate_hires_steps, calculate_refiner_steps, save_intermediate, update_sampler, is_txt2img, is_refiner_enabled from modules.processing_args import set_pipeline_args @@ -447,6 +448,9 @@ def process_diffusers(p: processing.StableDiffusionProcessing): sd_models_compile.openvino_recompile_model(p, hires=False, refiner=False) # recompile if a parameter changes + if hasattr(p, 'dummy'): + images = [Image.new(mode='RGB', size=(p.width, p.height))] + return images if 'base' not in p.skip: output = process_base(p) else: diff --git a/modules/prompt_parser_diffusers.py b/modules/prompt_parser_diffusers.py index 8c140e0d6..4e31c747a 100644 --- a/modules/prompt_parser_diffusers.py +++ b/modules/prompt_parser_diffusers.py @@ -63,6 +63,8 @@ class PromptEmbedder: self.positive_schedule = None self.negative_schedule = None self.scheduled_prompt = False + if hasattr(p, 'dummy'): + return earlyout = self.checkcache(p) if earlyout: return diff --git a/modules/shared.py b/modules/shared.py index 2a1e8c19d..c3fc4e905 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -931,8 +931,11 @@ options_templates.update(options_section(('extra_networks', "Networks"), { "extra_networks_styles_sep": OptionInfo("

Styles

", "", gr.HTML), "extra_networks_styles": OptionInfo(True, "Show built-in styles"), + "extra_networks_embed_sep": OptionInfo("

Embeddings

", "", gr.HTML), - "diffusers_convert_embed": OptionInfo(False, "Auto-convert SD15 embeddings to SDXL ", gr.Checkbox, {"visible": native}), + "diffusers_enable_embed": OptionInfo(True, "Enable embeddings support", gr.Checkbox, {"visible": native}), + "diffusers_convert_embed": OptionInfo(False, "Auto-convert SD15 embeddings to SDXL", gr.Checkbox, {"visible": native}), + "extra_networks_wildcard_sep": OptionInfo("

Wildcards

", "", gr.HTML), "wildcards_enabled": OptionInfo(True, "Enable file wildcards support"), })) diff --git a/modules/textual_inversion/textual_inversion.py b/modules/textual_inversion/textual_inversion.py index dd4203a4f..27bb42116 100644 --- a/modules/textual_inversion/textual_inversion.py +++ b/modules/textual_inversion/textual_inversion.py @@ -274,6 +274,8 @@ class EmbeddingDatabase: overwrite = bool(data) if not shared.sd_loaded: return + if not shared.opts.diffusers_enable_embed: + return embeddings, skipped = open_embeddings(filename) or convert_bundled(data) for skip in skipped: self.skipped_embeddings[skip.name] = skipped diff --git a/modules/timer.py b/modules/timer.py index 7657ac8e8..43e859140 100644 --- a/modules/timer.py +++ b/modules/timer.py @@ -7,6 +7,7 @@ class Timer: self.start = time.time() self.records = {} self.total = 0 + self.profile = False def elapsed(self, reset=True): end = time.time() @@ -27,11 +28,12 @@ class Timer: category = sys._getframe(1).f_code.co_name # pylint: disable=protected-access if category not in self.records: self.records[category] = 0 - self.records[category] += e + extra_time self.total += e + extra_time def summary(self, min_time=0.05, total=True): + if self.profile: + min_time = -1 res = f"{self.total:.2f} " if total else '' additions = [x for x in self.records.items() if x[1] >= min_time] if not additions: @@ -40,6 +42,8 @@ class Timer: return res def dct(self, min_time=0.05): + if self.profile: + return {k: round(v, 4) for k, v in self.records.items()} return {k: round(v, 2) for k, v in self.records.items() if v >= min_time} def reset(self):