diff --git a/studio/backend/core/inference/diffusion.py b/studio/backend/core/inference/diffusion.py index 8037dde174..25244e55c9 100644 --- a/studio/backend/core/inference/diffusion.py +++ b/studio/backend/core/inference/diffusion.py @@ -1167,17 +1167,13 @@ class DiffusionBackend: # explicit "off" / "fbcache" are pinned and never toggled. cache_request = normalize_transformer_cache(transformer_cache) cache_auto = transformer_cache is None or cache_request == TC_AUTO - cache_quant_active = ( - transformer_quant_engaged is not None or bool(gguf_filename) - ) + cache_quant_active = transformer_quant_engaged is not None or bool(gguf_filename) default_steps: Optional[int] = None if cache_auto: default_steps, _ = default_generation_params( gguf_filename, repo_id, base, fam.name ) - cache_request = ( - TC_FBCACHE if default_steps >= FBCACHE_MIN_STEPS else None - ) + cache_request = TC_FBCACHE if default_steps >= FBCACHE_MIN_STEPS else None cache_engaged = apply_step_cache( pipe, mode = cache_request, diff --git a/studio/backend/core/inference/sd_cpp_backend.py b/studio/backend/core/inference/sd_cpp_backend.py index d8cb21d409..45ecee64f5 100644 --- a/studio/backend/core/inference/sd_cpp_backend.py +++ b/studio/backend/core/inference/sd_cpp_backend.py @@ -89,6 +89,17 @@ _MAX_SERVER_BATCH = 8 _SERVER_PER_IMAGE_TIMEOUT_S = 1800.0 +def _default_threads() -> int: + """Physical-core thread count for the sd.cpp CPU backend. + + ``threads = None`` lets sd.cpp pick its own default, which is the logical-core + count (all hyperthreads). For the compute-bound GGML matmuls the diffusion CPU + 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) + + def _server_binary_runnable(binary: str) -> bool: """Best-effort probe that ``binary`` can actually execute (not just exist). @@ -531,7 +542,9 @@ class SdCppDiffusionBackend: vae_format = fam.sd_cpp_vae_format, offload = list(offload), native_speed = native_speed, - threads = None, + # Pin the CPU backend to physical cores; sd.cpp's own + # default oversubscribes hyperthreads (see _default_threads). + threads = _default_threads(), ) except SdCppCancelled: # Startup was aborted by an unload / superseding load: stop the @@ -568,7 +581,9 @@ class SdCppDiffusionBackend: vae_format = fam.sd_cpp_vae_format, native_speed = native_speed, offload_flags = offload, - threads = None, + # One-shot sd-cli reads this per generation (state.threads); pin to + # physical cores for the same reason as the server (see _default_threads). + threads = _default_threads(), sampling_method = fam.sd_cpp_sampling_method, flow_shift = fam.sd_cpp_flow_shift, server = server, diff --git a/studio/backend/tests/test_diffusion_attention.py b/studio/backend/tests/test_diffusion_attention.py index 48647bb0b4..dce7dd2bfa 100644 --- a/studio/backend/tests/test_diffusion_attention.py +++ b/studio/backend/tests/test_diffusion_attention.py @@ -245,7 +245,6 @@ class _Recorder: def _stub_subprocess(monkeypatch, run): import subprocess - monkeypatch.setattr(subprocess, "run", run) diff --git a/studio/backend/tests/test_diffusion_dit_trainer.py b/studio/backend/tests/test_diffusion_dit_trainer.py index b58868c701..6d7736a106 100644 --- a/studio/backend/tests/test_diffusion_dit_trainer.py +++ b/studio/backend/tests/test_diffusion_dit_trainer.py @@ -230,10 +230,15 @@ def test_mxfp8_training_config_falls_back_to_the_torchao_0_17_api(monkeypatch): def _patch_capability(monkeypatch, capability): # Drive train_precision_modes' GPU probe: pretend CUDA is present at the given tensor - # core capability (fp8 needs sm89+, mxfp8 needs sm100+). + # core capability (fp8 needs sm89+, mxfp8 needs sm100+). The torchao probe is stubbed + # functional so these tests exercise the CAPABILITY gate on hosts without torchao + # (the CPU-only CI runner does not install it). import torch + + import core.training.diffusion_train_common as dtc monkeypatch.setattr(torch.cuda, "is_available", lambda: True) monkeypatch.setattr(torch.cuda, "get_device_capability", lambda *a, **k: capability) + monkeypatch.setattr(dtc, "has_functional_torchao", lambda: True) def test_train_precision_modes_blackwell_lists_mxfp8(monkeypatch):