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:
parent
db440d2252
commit
6e5d11a0a7
3 changed files with 15 additions and 14 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ def _default_threads() -> int:
|
|||
path runs, oversubscribing the hyperthreads adds scheduling contention without
|
||||
extra throughput, so pin to physical cores (``cpu_count // 2``) instead. Falls
|
||||
back to 8 when the count is unknown, and clamps to at least 1."""
|
||||
return max(1, (os.cpu_count() or 8) // 2)
|
||||
cpu = os.cpu_count()
|
||||
return max(1, cpu // 2 if cpu else 8)
|
||||
|
||||
|
||||
def _server_binary_runnable(binary: str) -> bool:
|
||||
|
|
|
|||
|
|
@ -192,19 +192,19 @@ def test_effective_steps_txt2img_is_full_count():
|
|||
|
||||
|
||||
def test_effective_steps_low_strength_shrinks_below_the_bar():
|
||||
# A 28-step upscale at strength 0.35 denoises ~10 steps (diffusers get_timesteps),
|
||||
# which is below FBCACHE_MIN_STEPS -> the auto policy must NOT engage FBCache there.
|
||||
# A 28-step upscale at strength 0.35 denoises int(9.8) = 9 steps (diffusers get_timesteps
|
||||
# floors the product), which is below FBCACHE_MIN_STEPS -> the auto policy must NOT engage
|
||||
# FBCache there.
|
||||
eff = effective_denoise_steps(28, 0.35)
|
||||
assert eff == 10
|
||||
assert eff == 9
|
||||
assert eff < FBCACHE_MIN_STEPS
|
||||
|
||||
|
||||
def test_effective_steps_matches_diffusers_get_timesteps():
|
||||
# Mirror diffusers exactly: num_inference_steps - int(num_inference_steps -
|
||||
# min(num_inference_steps * strength, num_inference_steps)).
|
||||
# Mirror diffusers exactly: it denoises init_timestep = min(int(num_inference_steps *
|
||||
# strength), num_inference_steps) steps (the product is floored, not rounded).
|
||||
for steps, strength in [(28, 0.35), (28, 0.8), (50, 0.5), (20, 0.99), (30, 0.1)]:
|
||||
init = min(steps * strength, steps)
|
||||
expected = max(1, steps - int(max(steps - init, 0)))
|
||||
expected = max(1, min(int(steps * strength), steps))
|
||||
assert effective_denoise_steps(steps, strength) == expected
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue