diffusion cache: floor the strength-scaled step count like diffusers

effective_denoise_steps computed ceil(steps * strength) (steps - int(steps - steps*strength)),
but diffusers get_timesteps denoises init_timestep = min(int(num_inference_steps * strength),
num_inference_steps), i.e. the floored product. The two differ by one whenever the product is
fractional, and that flips the auto FBCache decision in the (19, 20) band: a strength-0.7
28-step img2img denoises int(19.6) = 19 real steps (below FBCACHE_MIN_STEPS = 20) but the old
formula returned 20 and engaged FBCache on that short trajectory, exactly the quality hit the
auto policy exists to avoid. Return min(int(steps * strength), steps) to match diffusers, and
fix the two tests that replayed the old formula.

Also honor _default_threads' documented fallback: (os.cpu_count() or 8) // 2 yields 4 when the
count is unknown, contradicting the docstring's 'falls back to 8'. Return 8 in that case.
This commit is contained in:
Daniel Han 2026-07-06 09:59:54 +00:00
commit 6e5d11a0a7
3 changed files with 15 additions and 14 deletions

View file

@ -131,17 +131,17 @@ def effective_denoise_steps(steps: int, strength: Optional[float]) -> int:
An image-conditioned workflow with ``strength`` < 1 (img2img / upscale / inpaint) runs
only a fraction of ``num_inference_steps``: diffusers' ``get_timesteps`` computes
``init_timestep = min(num_inference_steps * strength, num_inference_steps)`` and denoises
``num_inference_steps - int(num_inference_steps - init_timestep)`` steps. The auto
``init_timestep = min(int(num_inference_steps * strength), num_inference_steps)`` and
denoises exactly ``init_timestep`` steps -- the product is FLOORED, not rounded. The auto
step-cache policy must key on THIS count -- e.g. a 28-step upscale at strength 0.35 runs
~10 real steps, exactly the short trajectory FBCache should stay off (each skipped step
is a large quality hit). ``strength`` None (txt2img / reference) or >= 1 -> the full count.
``int(9.8) = 9`` real steps, exactly the short trajectory FBCache should stay off (each
skipped step is a large quality hit). ``strength`` None (txt2img / reference) or >= 1 -> the
full count.
"""
s = int(steps)
if strength is None or float(strength) >= 1.0:
return s
init = min(s * float(strength), s)
return max(1, s - int(max(s - init, 0)))
return max(1, min(int(s * float(strength)), s))
def maybe_toggle_step_cache(