Both jobs processed one file at a time on a single core. Pillow decodes the whole image to reach trailing PNG text chunks and releases the GIL while it does, so a bounded thread pool scales almost linearly: on a 1 GB sample of 400 PNGs the metadata pass dropped from 18 s to 2.5 s at 8 threads, and 120 thumbnails from 4.6 s to 0.6 s. - utils/parallel.py: map_parallel(func, items, workers) keeps order, runs on the caller's thread when workers is 1, and caps the pool at the item count - PromptManagerConfig.WORKER_THREADS defaults to half the detected cores, is clamped to max_worker_threads() when loaded from config.json, and is persisted under "performance" - settings API returns worker_threads and cpu_count and validates saves; the settings modal gets a "Worker threads" slider capped at the core count - the scan reads each metadata batch on the configured threads; thumbnail generation processes batches of files per executor job, still emitting one progress event per file Threads rather than processes: this runs inside ComfyUI's server, where forking a CUDA-initialised process is unsafe and the spawn start method would re-import ComfyUI's main module in every child.
27 lines
1.1 KiB
Python
27 lines
1.1 KiB
Python
"""Bounded thread-pool map for the long jobs (output scan, thumbnails).
|
|
|
|
Threads, not processes: this code runs inside ComfyUI's server, where forking
|
|
a CUDA-initialised, multi-threaded process is unsafe and the spawn start
|
|
method would re-import ComfyUI's main module in every child. Pillow releases
|
|
the GIL while decoding, resizing and encoding, so threads still scale close
|
|
to linearly for both jobs.
|
|
"""
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
|
|
|
|
def map_parallel(func, items, workers):
|
|
"""Apply ``func`` to every item on up to ``workers`` threads, keeping order.
|
|
|
|
``workers`` of 1 (or fewer) runs on the calling thread with no pool. An
|
|
exception in ``func`` propagates to the caller, so callers that must
|
|
survive bad input wrap ``func`` themselves.
|
|
"""
|
|
items = list(items)
|
|
workers = max(1, int(workers or 1))
|
|
if workers == 1 or len(items) <= 1:
|
|
return [func(item) for item in items]
|
|
max_workers = min(workers, len(items))
|
|
with ThreadPoolExecutor(max_workers, thread_name_prefix="pm-worker") as pool:
|
|
return list(pool.map(func, items))
|