# Conflicts: # studio/backend/core/inference/diffusion.py # studio/frontend/src/features/images/images-page.tsx
3.1 KiB
Summary
Adds opt-in step caching (First-Block-Cache) for the diffusion transformer. Across denoise steps a DiT's output settles, so once the first block's residual barely changes the remaining blocks are skipped and their cached output reused. diffusers ships this natively (FirstBlockCacheConfig + transformer.enable_cache, with the standalone apply_first_block_cache hook as a fallback).
This is the next lever in the Studio diffusion efficiency stack, targeting denoise-time speed on many-step models.
Measured (Flux.1-dev, 28 steps, 1024px, one B200)
Reference is the no-cache compiled output (scripts/fbcache_flux_probe.py):
| mode | latency | speedup | LPIPS vs no-cache |
|---|---|---|---|
| compile baseline | 2.83s | 1.00x | ref |
| fbcache (threshold 0.08) | 2.03s | 1.40x | ~0.08 |
~1.4x on top of torch.compile at a small, well-bounded quality cost.
Design
- OFF by default and a per-load opt-in. The win scales with step count, so it is for many-step models (Flux / Qwen-Image) and pointless for few-step distilled models (e.g. Z-Image-Turbo at ~8 steps), where a single skipped step is a large fraction of the trajectory.
- Composes with regional compile only with
fullgraph=False(the cache's per-step decision is atorch.compiler.disablegraph break), which the speed layer now switches to automatically when a cache is engaged. - Best-effort: a model whose block signature the hook does not recognise is caught and the load proceeds uncached.
- The residual threshold auto-raises for a quantised transformer (0.08 -> 0.12), which shifts the residual distribution, per ParaAttention's fp8 guidance. An explicit
transformer_cache_thresholdoverrides.
Changes
- new
core/inference/diffusion_cache.py:normalize_transformer_cache+apply_step_cache(enable_cache / apply_first_block_cache fallback; lazy diffusers import). diffusion_speed.py:apply_speed_optimstakescache_active; compile dropsfullgraphwhen a cache is engaged.diffusion.py:apply_step_cacheruns before compile;transformer_cache/transformer_cache_thresholdthread throughbegin_load->load_pipeline, and the engaged mode is reported instatus().models/inference.py+routes/inference.py:transformer_cache(off | fbcache) andtransformer_cache_thresholdrequest fields; engaged mode in the status response.- hermetic tests for normalisation, the enable_cache / hook-fallback paths, threshold selection, and best-effort failure handling, plus route threading + validation (422 on a bad enum / out-of-range threshold).
scripts/fbcache_flux_probe.py: the Flux validation probe (latency / speedup / VRAM / LPIPS vs the compiled no-cache baseline).
Compatibility
Default behaviour is unchanged: the request field defaults to null so nothing engages unless a caller opts in. No change to the GGUF build, the dense fast path, the placement/offload order, or any other family.
Testing
python -m pytest tests/ -q -k diffusion -> 230 passed. The Flux speedup/quality numbers above are from scripts/fbcache_flux_probe.py.
Stacked on #6702 (Phase 11). Base branch diffusion-phase11-consumer-int8.