Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ sd-cli --diffusion-model flux1-dev.safetensors ... \

Ordered from fastest to smallest-VRAM: no flags → `--offload-to-cpu` → `--offload-to-cpu --max-vram <N>` → `--offload-to-cpu --max-vram <N> --stream-layers`. Each step down costs a few percent of throughput to buy more room; combined they can run models roughly 3-4x larger than the raw VRAM would allow.

## Use SPEED to accelerate flow-model sampling.

`--sampling-method speed_flow` runs the early denoising steps at a reduced spatial resolution and progressively expands to full resolution as the trajectory approaches the data manifold. Compatible with `--offload-to-cpu`, `--max-vram`, `--stream-layers`, and the cache-mode flags. Typical sampling-time speedups on flow models: ~1.6× on Flux, ~1.7× on Qwen Image, up to ~3.5× on Z-Image with a tuned transition point. Composition drifts slightly from the euler baseline (different trajectory), quality preserved for txt2img; edit / ref-guided models see reduced source fidelity — see [speed_sampler.md](./speed_sampler.md) for tuning knobs and compatibility notes.

## Use quantization to reduce memory usage.

[quantization](./quantization_and_gguf.md)
84 changes: 84 additions & 0 deletions docs/speed_sampler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# SPEED — Spectral Progressive Diffusion sampler

SPEED (`--sampling-method speed_flow`) accelerates flow-model sampling by running the early denoising steps at a **reduced spatial resolution**, then progressively expanding to full resolution as the trajectory approaches the data manifold. Reference: [Xiao et al., arXiv:2605.18736](https://arxiv.org/abs/2605.18736); reference C++/Python: [howardhx/speed](https://github.com/howardhx/speed), [ruwwww/ComfyUI-SPEED](https://github.com/ruwwww/ComfyUI-SPEED).

Between segments, the low-resolution latent is expanded via a spectral basis (DCT-II by default) — the low-frequency coefficients are preserved and the newly-exposed high-frequency bands are filled with Gaussian noise scaled by the current sigma. The flow-matching time at each transition is rescaled by `kappa` (paper Eq. 5-6) so the base sampler continues on a consistent schedule after resize.

## Quick usage

```sh
# Flux dev / schnell — default configuration
./bin/sd-cli --sampling-method speed_flow ...

# Explicit scales; use ':' as the intra-list separator
./bin/sd-cli --sampling-method speed_flow \
--extra-sample-args "speed_scales=0.5:1.0"

# Manual transition point (overrides delta-optimal)
./bin/sd-cli --sampling-method speed_flow \
--extra-sample-args "speed_scales=0.5:1.0,speed_manual_sigmas=0.7"
```

## Extra sampler args

All parsed out of `--extra-sample-args`. The list is comma-separated at the top level; within a single value use `:` (or `/`, `|`) as the intra-value separator.

| Arg | Default | Description |
|---|---|---|
| `speed_scales` | `0.5:1.0` | Colon-separated resolution fractions, strictly increasing, ending at `1.0`. E.g. `0.25:0.5:1.0` for a 3-level schedule. |
| `speed_levels` | *(unset)* | Shortcut: `speed_levels=2` → `{0.5, 1.0}`; `=3` → `{0.25, 0.5, 1.0}`. Ignored if `speed_scales` is set. |
| `speed_delta` | `0.01` | Noise-dominated tolerance (paper Eq. 9). Smaller values push transitions later in the trajectory. |
| `speed_manual_sigmas` | *(unset)* | Colon-separated sigma thresholds, one per transition (length `= scales.size() - 1`). Bypasses `speed_delta` and the power-spectrum formula. Values must be strictly decreasing and each in `(0, 1)`. |
| `speed_preset` | *auto* | Power-spectrum preset. Auto-selected from the loaded model: `wan21` (`A=219.485, β=2.423`) for WAN, `flux` (`A=203.615, β=1.915`) for every other flow model. Values fitted from the respective VAEs in the reference impl's `configs.yaml`. Set explicitly to override. |
| `speed_A`, `speed_beta` | *(preset)* | Power-spectrum override for custom models. Overrides `speed_preset`. |
| `speed_transform` | `dct` | Spectral basis for the resolution expansion. `dct` handles any ratio; `fft` also handles any ratio, provided both spatial dims are powers of 2. |
| `speed_seed` | `0` | Seed for the Gaussian noise placed into the new high-frequency bands at each transition. |

## Typical speedups

Measured locally on an RTX 3060 12 GiB. Numbers are sampling-time speedups over `--sampling-method euler` at the same seed and step count.

| Model | Resolution | Steps | Config | Speedup |
|---|---|---|---|---|
| Flux schnell Q4_K | 1024×1024 | 8 | `speed_scales=0.5:1.0` (delta-optimal) | ~1.6× |
| Qwen Image Q2_K | 1024×1024 | 20 | `speed_scales=0.5:1.0` (delta-optimal) | ~1.7× |
| Z-Image turbo Q8 | 1024×688 | 8 | `speed_scales=0.5:1.0,speed_manual_sigmas=0.7` | ~3.5× |

Compute savings scale with the number of low-resolution steps in the schedule. Models with front-loaded (shift-heavy) sigma schedules — Flux schnell, Z-Image turbo — often benefit from `speed_manual_sigmas` to push the transition into the middle of the trajectory rather than the very end.

## Compatibility

- `--offload-to-cpu`, `--max-vram`, `--stream-layers`: fully compatible. The graph-cut planner builds a second cached plan for the full-resolution shape at the transition (~5 ms overhead).
- `--cache-mode easycache` (and other caches): compatible. The cache runtime tolerates the resolution transition; the cache heuristic skips only steps where the shape context is stable.
- Non-flow denoisers (SD 1.5, SDXL): SPEED targets flow models specifically. If dispatched on an ε-prediction model it will run but the kappa/sigma rescale math is not valid — do not use.

## Reference-latent models (Qwen Image Edit, Flux Kontext, etc.)

**SPEED runs on ref-latent models but reduces fidelity to the source image.** A warning is logged when this combination is detected:

```
[WARN ] SPEED sampler with reference latents (edit / ref-guided model): the low-resolution
segments see a downscaled reference so fine detail from the source image (backgrounds,
textures, layout beyond broad structure) is likely to drift.
```

Why: the reference latents are the low-frequency structure the edit is supposed to preserve. During SPEED's low-resolution segments the reference is downscaled to match the working latent, so the model conditions on a coarser version of the reference — it retains the semantic subject and the requested edit direction, but loses fine detail (backgrounds, textures, exact framing). Composition remains plausible but is not faithful to the source.

Practical guidance:

- For **creative / style edits** where subject and edit intent matter more than reproducing the source scene: SPEED is fine.
- For **fidelity edits** (localized inpaint-style changes, preserving specific backgrounds or text): use `euler` or another full-resolution sampler.
- To trade off, use `speed_manual_sigmas` with a threshold in the middle of the trajectory so only 2–3 early steps run at low resolution.

## Known limits (v1)

- Naive O(N²) 2D DCT — ~1.8 s per direction on a 128×86×16 latent. Fine for image-sized latents; video and very large latents would need a proper FFT-based DCT.
- Only image latents (4-D `[W, H, C, N]`). Video (5-D) is not handled.
- Multi-resolution triggers 2 ggml graph builds per generation, cached thereafter.
- Inpainting masks with `!=` full-resolution shape are silently skipped at low-res steps (the mask blend guard checks the shape and falls through when it can't broadcast).

## References

- Paper: Xiao et al., *"SPEED: Spectral Progressive Diffusion for training-free acceleration"*, [arXiv:2605.18736](https://arxiv.org/abs/2605.18736)
- Official code: <https://github.com/howardhx/speed>
- ComfyUI implementation: <https://github.com/ruwwww/ComfyUI-SPEED>
6 changes: 3 additions & 3 deletions examples/common/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ ArgOptions SDGenerationParams::get_options() {
&hires_upscaler},
{"",
"--extra-sample-args",
"extra sampler/scheduler/guidance args, key=value list. CFG supports guidance_schedule; APG supports apg_eta, apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end; flux supports base_shift, max_shift; ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma;; logit_normal supports mu, std, logsnr_min, logsnr_max, resolution_aware",
"extra sampler/scheduler/guidance args, key=value list. CFG supports guidance_schedule; APG supports apg_eta, apg_momentum, apg_norm_threshold, apg_norm_threshold_smoothing; SLG supports slg_uncond; lcm supports noise_clip_std, noise_scale_start, noise_scale_end; flux supports base_shift, max_shift; ltx2 supports max_shift, base_shift, stretch, terminal; euler_ge supports gamma; speed_flow supports speed_scales (use ':' between scales, e.g. speed_scales=0.25:0.5:1.0), speed_levels (2 or 3), speed_delta, speed_manual_sigmas (':'-separated, one per transition, overrides delta), speed_preset (flux|wan21), speed_transform (dct|fft), speed_A, speed_beta, speed_seed;; logit_normal supports mu, std, logsnr_min, logsnr_max, resolution_aware",
(int)',',
&extra_sample_args},
{"",
Expand Down Expand Up @@ -1488,12 +1488,12 @@ ArgOptions SDGenerationParams::get_options() {
on_seed_arg},
{"",
"--sampling-method",
"sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp]"
"sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, speed_flow, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp]"
"(default: euler for Flux/SD3/Wan, euler_a otherwise)",
on_sample_method_arg},
{"",
"--high-noise-sampling-method",
"(high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp]"
"(high noise) sampling method, one of [euler, euler_a, heun, dpm2, dpm++2s_a, dpm++2m, dpm++2mv2, dpm++2m_sde, dpm++2m_sde_bt, speed_flow, ipndm, ipndm_v, lcm, ddim_trailing, tcd, res_multistep, res_2s, er_sde, euler_cfg_pp, euler_a_cfg_pp]"
" default: euler for Flux/SD3/Wan, euler_a otherwise",
on_high_noise_sample_method_arg},
{"",
Expand Down
1 change: 1 addition & 0 deletions include/stable-diffusion.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ enum sample_method_t {
EULER_GE_SAMPLE_METHOD,
DPMPP2M_SDE_SAMPLE_METHOD,
DPMPP2M_SDE_BT_SAMPLE_METHOD,
SPEED_FLOW_SAMPLE_METHOD,
SAMPLE_METHOD_COUNT
};

Expand Down
Loading
Loading