|
| 1 | +# DGX Spark (GB10): Performance & Tuning |
| 2 | + |
| 3 | +You have FastVideo [installed on a DGX Spark](spark.md) — this page is what to |
| 4 | +run next. It covers **which models are practical on the GB10, what actually |
| 5 | +makes them faster, and what won't help (and why)**, so you don't burn a night |
| 6 | +tuning knobs that can't move on this hardware. |
| 7 | + |
| 8 | +!!! tip "TL;DR" |
| 9 | + - **Use distilled few-step models** (e.g. `FastVideo/FastWan2.1-T2V-1.3B-Diffusers`). |
| 10 | + They run in ~40 s/video. Full-step models are 12–47 min on the GB10. |
| 11 | + - On few-step models, **VAE decode is the bottleneck**, not attention — it's |
| 12 | + bandwidth-bound on the Spark's unified memory. |
| 13 | + - **bf16 VAE decode** is the real, lossless lever (FastVideo already turns it |
| 14 | + on for Wan). **FlashAttention, linear quantization, and `torch.compile` of |
| 15 | + the VAE give little or nothing here** — see the table below. |
| 16 | + - Heavy runs can make the box unreachable — run generations with VAE tiling on |
| 17 | + and `nice -n 19`. See [Running safely](#running-safely-dont-lock-the-box). |
| 18 | + |
| 19 | +## The hardware reality (this explains everything below) |
| 20 | + |
| 21 | +The GB10 pairs a Blackwell GPU (`sm_121`) with **128 GB of unified LPDDR5X memory |
| 22 | +(~270 GB/s) shared between CPU and GPU**. That bandwidth is roughly **10× below a |
| 23 | +datacenter GPU's HBM**. Two consequences drive every tuning decision: |
| 24 | + |
| 25 | +1. **Memory-bandwidth-bound stages hurt disproportionately.** VAE decode moves a |
| 26 | + lot of data and becomes the dominant cost on short (few-step) generations. |
| 27 | +2. **Compute-bound stages scale with step count.** Full-step diffusion (50+ |
| 28 | + steps) is denoise-bound and simply takes a long time here. |
| 29 | + |
| 30 | +## Use distilled few-step models |
| 31 | + |
| 32 | +The single biggest lever on the GB10 is **model choice**. A 3-step distilled |
| 33 | +model is ~18× faster than the full-step version of the same architecture: |
| 34 | + |
| 35 | +| Model | Steps | Time / video | Bottleneck | |
| 36 | +|---|---|---|---| |
| 37 | +| FastWan2.1-T2V-1.3B (distilled) | 3 | **~40 s** | VAE decode | |
| 38 | +| Wan2.1-T2V-1.3B (full-step) | 50 | ~12 min | denoise | |
| 39 | +| Cosmos-Predict2.5-2B (full-step) | 51 | ~47 min | denoise | |
| 40 | +| LTX2.3-distilled (+audio) | 8 | ~6 min | mixed | |
| 41 | + |
| 42 | +The bottleneck flips from decode to denoise at around **4 steps**. Below that, |
| 43 | +you're paying mostly for VAE decode; above it, mostly for the denoising loop. |
| 44 | + |
| 45 | +!!! note "Few-step timings are noisy — measure in-process" |
| 46 | + On a 3-step run, one-time per-process startup (Triton autotune, allocator |
| 47 | + warmup) dominates and never amortizes, so single-run totals wobble ~±30%. |
| 48 | + Compare levers **back-to-back in one process or as medians**, never as two |
| 49 | + separate single runs. The [reproduction script](#reproduce-these-numbers) |
| 50 | + does this for you. |
| 51 | + |
| 52 | +## bf16 VAE decode — the real lever (already on for Wan) |
| 53 | + |
| 54 | +Because few-step generation is decode-bound, VAE decode precision is where the |
| 55 | +time is. Decoding in **bf16 instead of fp32 is essentially lossless** (MS-SSIM |
| 56 | +~0.9999 vs fp32 on the identical latent) and ~1.14× faster — worth roughly |
| 57 | +5–7% end-to-end on a decode-bound few-step model. |
| 58 | + |
| 59 | +**FastVideo already defaults Wan's decode to bf16** (`vae_decode_precision="bf16"`, |
| 60 | +with encode kept at fp32), so for the recommended Wan/FastWan models there's |
| 61 | +nothing to set. If you run a model that still defaults to an fp32 decode, set the |
| 62 | +decode-only override yourself: |
| 63 | + |
| 64 | +```python |
| 65 | +from fastvideo.configs.pipelines.base import PipelineConfig |
| 66 | + |
| 67 | +pipeline_config = PipelineConfig.from_pretrained(model_id) |
| 68 | +pipeline_config.vae_decode_precision = "bf16" # decode-only; leaves encode precision alone |
| 69 | +``` |
| 70 | + |
| 71 | +Decode is output-only, so lowering its precision is safe. (Encode seeds the |
| 72 | +denoising trajectory for I2V/causal models, so that stays at the pipeline's |
| 73 | +default — don't lower `vae_precision` blindly for those.) |
| 74 | + |
| 75 | +## Memory: one unified 128 GB pool |
| 76 | + |
| 77 | +The GB10 has **no separate VRAM** — CPU and GPU share one 128 GB LPDDR5X pool |
| 78 | +(~118 GB usable). Two practical consequences: |
| 79 | + |
| 80 | +- **`nvidia-smi` reports memory as `[N/A]`** on the GB10, and the system "used" |
| 81 | + figure conflates CPU + GPU + cache, so it's only a soft upper bound — treat the |
| 82 | + whole 128 GB as one shared budget. For a per-run figure, use FastVideo's own |
| 83 | + `peak_memory_mb` (reported on the generation result and by the performance |
| 84 | + benchmark), which is measured inside the worker that runs the model. |
| 85 | +- **The 128 GB is a *working-set* ceiling, not storage** — the model cache lives |
| 86 | + on the NVMe (3.7 TB, ample). What has to fit in 128 GB is the weights, |
| 87 | + activations, and KV cache — and, critically, the **VAE decode buffers**, which |
| 88 | + is why tiling matters |
| 89 | + (an untiled high-res decode can spike the pool into swap and lock the box). |
| 90 | + |
| 91 | +The recommended few-step models are comfortable here: their weights are small |
| 92 | +(1.3–2 B) and few-step generation keeps activations modest — a Wan2.1-1.3B |
| 93 | +few-step generation peaks at **~8.4 GB** (measured), a small fraction of the pool. |
| 94 | +The pressure comes from **decode resolution/frames**, not the model — a |
| 95 | +1080p×121-frame untiled decode is what pushes the pool toward its ceiling, which |
| 96 | +is why VAE tiling stays |
| 97 | +on by default. |
| 98 | + |
| 99 | +## What helps vs. what doesn't on the GB10 |
| 100 | + |
| 101 | +The honest summary — most "obvious" GPU optimizations don't move the needle on |
| 102 | +this hardware, for reasons specific to it: |
| 103 | + |
| 104 | +| Lever | Effect on the GB10 | Use it? | |
| 105 | +|---|---|---| |
| 106 | +| Distilled few-step model | ~18× vs full-step | ✅ **the primary lever** | |
| 107 | +| bf16 VAE decode | ~1.14×, lossless; ~5–7% e2e on few-step | ✅ default for Wan | |
| 108 | +| VSA (video sparse attention) | works out of the box (Triton kernel auto-selects on `sm_121`) | ✅ automatic | |
| 109 | +| Building FlashAttention | **no speedup** — Torch SDPA already hits an efficient flash kernel on `sm_121`, and FA2 ties it | ❌ not worth building | |
| 110 | +| `torch.compile` of the VAE decode | recompile storm (per-frame varying shapes) → ~1.1× | ❌ dead end | |
| 111 | +| Linear (fp8 / nvfp4) quantization on long-sequence models (e.g. Cosmos) | ~nothing — see below | ❌ wrong lever here | |
| 112 | +| FP4 attention (`ATTN_QAT_INFER`) | works on `sm_121` (runtime allowlist landed in #1647; kernel build is #1598); helps, but needs a QAT-trained checkpoint | ⚠️ opt-in — see below | |
| 113 | +| FP4 linear on short-sequence models (LTX2) | up to −24% denoise at 1080p (#1594) | ⚠️ model/resolution-dependent | |
| 114 | + |
| 115 | +### Why linear quantization is the wrong lever on long-sequence models |
| 116 | + |
| 117 | +Quantizing the linear (GEMM) layers is a natural first instinct, but on a |
| 118 | +long-sequence video model it buys almost nothing on the GB10. A video-DiT denoise |
| 119 | +step is dominated by **O(N²) attention** at these sequence lengths (tens of |
| 120 | +thousands of tokens); the linear layers are a small single-digit fraction of the |
| 121 | +work. Quantizing them faster leaves the attention-bound total essentially |
| 122 | +unchanged — measured at ~1% on Cosmos-2.5, i.e. noise, and full-step CFG models |
| 123 | +also lose quality to per-step quantization error. |
| 124 | + |
| 125 | +The same mechanism **does** help on **short-sequence** models: LTX2's aggressive |
| 126 | +VAE compression gives it short attention sequences, so FP4 linear reaches −24% |
| 127 | +there (#1594). The rule: **on the GB10, the lever that matters is attention |
| 128 | +(sparse or FP4), not the linear layers** — unless the model has short sequences. |
| 129 | + |
| 130 | +### FP4 on the GB10 (opt-in) |
| 131 | + |
| 132 | +Block-scaled FP4 works on `sm_121` under CUDA 13: |
| 133 | + |
| 134 | +- **FP4 attention** (`FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER`, #1598) is |
| 135 | + numerically correct on the GB10 and ~6% faster end-to-end generation, but it only preserves |
| 136 | + quality on a **quantization-aware-distilled checkpoint** (e.g. |
| 137 | + `FastVideo/FastWan-QAD-1.3B`) — stock weights aren't trained to tolerate it. |
| 138 | +- **FP4 linear** helps only where sequences are short (LTX2, above). |
| 139 | + |
| 140 | +The [`qad_fp4_ab.py`](#reproduce-these-numbers) harness reproduces the FP4 |
| 141 | +attention A/B on the QAD checkpoint. |
| 142 | + |
| 143 | +## Running safely (don't lock the box) |
| 144 | + |
| 145 | +The GB10 is easy to make **unreachable** — a heavy build or an untiled high-res |
| 146 | +decode starves the ~20 ARM cores and unified memory, `sshd` can't get cycles, and |
| 147 | +you're locked out at *"Connection timed out during banner exchange"* until the box |
| 148 | +is power-cycled. To avoid it: |
| 149 | + |
| 150 | +- **Inference:** keep **VAE tiling on** (the default), use sane resolution/frames, |
| 151 | + and run under `nice -n 19`: |
| 152 | + |
| 153 | + ```bash |
| 154 | + nice -n 19 nohup python your_script.py > run.log 2>&1 & |
| 155 | + ``` |
| 156 | + |
| 157 | +- **Builds** (flash-attn, kernel): `nice -n 19`, `MAX_JOBS=2`, `nohup`. Never a |
| 158 | + bare foreground high-parallelism build. |
| 159 | +- Leave `*_cpu_offload` at the example defaults — "CPU" offload is the *same* |
| 160 | + unified RAM on the GB10, so the win is tiling + sane resolution, not offloading. |
| 161 | + |
| 162 | +## Gotchas specific to the GB10 |
| 163 | + |
| 164 | +A few things that surprise people on this box (beyond the memory notes above): |
| 165 | + |
| 166 | +- **Don't force `TORCH_SDPA` on a VSA checkpoint** (FastWan, LTX2.3-distilled). |
| 167 | + The SDPA path builds a model without the gate weights the checkpoint carries and |
| 168 | + fails to load. Run the model natively — VSA auto-routes to its Triton kernel on |
| 169 | + `sm_121`. |
| 170 | +- **Few-step timings are noisy run-to-run** (~±30%) — one-time startup dominates a |
| 171 | + 3-step run. Compare in-process / as medians, never two separate single runs (the |
| 172 | + benchmark script does this). |
| 173 | +- **`nvidia-smi` shows `[N/A]` for memory** — see [Memory](#memory-one-unified-128-gb-pool). |
| 174 | +- **Cosmos-2.5** uses a Qwen2.5-VL text encoder; make sure you're on a FastVideo |
| 175 | + build recent enough to include its `transformers`-compatibility handling before |
| 176 | + running it. |
| 177 | + |
| 178 | +## Reproduce these numbers |
| 179 | + |
| 180 | +Two scripts under `examples/inference/optimizations/` reproduce the claims on |
| 181 | +your own GB10: |
| 182 | + |
| 183 | +```bash |
| 184 | +# Headline: few-step generation timing (median) + the bf16-vs-fp32 decode A/B. |
| 185 | +# FASTVIDEO_STAGE_LOGGING=1 also prints the denoise / decode / text split. |
| 186 | +FASTVIDEO_STAGE_LOGGING=1 nice -n 19 \ |
| 187 | + python examples/inference/optimizations/spark_benchmark.py |
| 188 | + |
| 189 | +# FP4 attention quality/speed A/B on the QAD checkpoint (one arm per run). |
| 190 | +QAD_LINEAR=0 FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER nice -n 19 \ |
| 191 | + python examples/inference/optimizations/qad_fp4_ab.py |
| 192 | +``` |
| 193 | + |
| 194 | +See also the [Optimizations](../../inference/optimizations.md) reference for the |
| 195 | +full list of attention backends and quantization options. |
0 commit comments