Skip to content

Commit f85778d

Browse files
fix result handling and metric labels in the benchmark scripts
generate_video returns a plain dict, so getattr(result, ...) always hit the fallback: generation_time silently became wall time and peak_memory_mb was always None. Use dict access. Label the measured metric honestly: generation_time is the full pipeline (text-encode + denoise + decode), not denoise. Also: the sm_121 runtime allowlist landed via hao-ai-lab#1647; hao-ai-lab#1598 is the remaining kernel build.
1 parent f9dab59 commit f85778d

3 files changed

Lines changed: 18 additions & 9 deletions

File tree

docs/getting_started/installation/spark_performance.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ this hardware, for reasons specific to it:
109109
| Building FlashAttention | **no speedup** — Torch SDPA already hits an efficient flash kernel on `sm_121`, and FA2 ties it | ❌ not worth building |
110110
| `torch.compile` of the VAE decode | recompile storm (per-frame varying shapes) → ~1.1× | ❌ dead end |
111111
| 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` (#1598); helps, but needs a QAT-trained checkpoint | ⚠️ opt-in — see below |
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 |
113113
| FP4 linear on short-sequence models (LTX2) | up to −24% denoise at 1080p (#1594) | ⚠️ model/resolution-dependent |
114114

115115
### Why linear quantization is the wrong lever on long-sequence models
@@ -132,7 +132,7 @@ there (#1594). The rule: **on the GB10, the lever that matters is attention
132132
Block-scaled FP4 works on `sm_121` under CUDA 13:
133133

134134
- **FP4 attention** (`FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER`, #1598) is
135-
numerically correct on the GB10 and ~6% faster denoise, but it only preserves
135+
numerically correct on the GB10 and ~6% faster end-to-end generation, but it only preserves
136136
quality on a **quantization-aware-distilled checkpoint** (e.g.
137137
`FastVideo/FastWan-QAD-1.3B`) — stock weights aren't trained to tolerate it.
138138
- **FP4 linear** helps only where sequences are short (LTX2, above).

examples/inference/optimizations/qad_fp4_ab.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,10 @@
2626
invocation and dumps a C stack on any hard crash, so a single misbehaving arm
2727
can never take the others down with it; the runbook loops it four times with
2828
different env. Quality is the eye/ear on the saved mp4 + a matching-frame still;
29-
timing is the mean generation_time over the measured runs.
29+
timing is the mean generation_time (full pipeline: text-encode +
30+
denoise + VAE decode) over the measured runs.
3031
31-
On the GB10, expect FP4 attention ~6% faster denoise vs bf16 and quality-neutral
32+
On the GB10, expect FP4 attention ~6% faster end-to-end generation vs bf16 and quality-neutral
3233
by eye on the QAD checkpoint (both share the 3-step distill's quality ceiling).
3334
FP4 *linear* is roughly break-even at 1.3B/480p (the per-call quantize overhead
3435
eats the small-GEMM saving in eager mode); its win shows at higher resolution
@@ -183,9 +184,12 @@ def _generate():
183184
last = _generate()
184185
torch.cuda.synchronize()
185186
wall = time.perf_counter() - t0
186-
denoise_times.append(getattr(last, "generation_time", wall))
187+
# generate_video returns a plain dict; attribute access would always
188+
# fall back to wall time.
189+
gen_t = last.get("generation_time") if isinstance(last, dict) else None
190+
denoise_times.append(gen_t if gen_t is not None else wall)
187191
print(f"[qad] {tag} run {i + 1}/{runs}: {wall:.2f}s wall "
188-
f"(denoise {denoise_times[-1]:.2f}s)")
192+
f"(gen {denoise_times[-1]:.2f}s)")
189193

190194
# The pipeline wrote the mp4 (full known-good encode) into arm_dir; report
191195
# it and pull a matching-frame still from the [b,c,t,h,w] samples tensor
@@ -206,7 +210,7 @@ def _generate():
206210
print("[qad] note: no 5-D samples tensor; grab a frame from the mp4 above")
207211

208212
mean = sum(denoise_times) / len(denoise_times)
209-
print(f"\n[qad][{tag}] denoise mean {mean:.2f}s over {runs} runs "
213+
print(f"\n[qad][{tag}] generation mean {mean:.2f}s over {runs} runs "
210214
f"({warmup} warmup, {steps} steps)")
211215
generator.shutdown()
212216

examples/inference/optimizations/spark_benchmark.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,16 @@ def _gen():
9393
save_video=True,
9494
sampling_param=sampling_param)
9595
torch.cuda.synchronize()
96-
dt = getattr(video, "generation_time", time.perf_counter() - t0)
96+
# generate_video returns a plain dict (legacy result), not an object —
97+
# attribute access would silently fall back to wall time / None.
98+
dt = video.get("generation_time") if isinstance(video, dict) else None
99+
if dt is None:
100+
dt = time.perf_counter() - t0
97101
# Peak memory is measured *inside the worker process* that runs the
98102
# pipeline and surfaced on the result; reading torch's allocator in this
99103
# (main) process would report ~0 because the allocations aren't here.
100-
return dt, getattr(video, "peak_memory_mb", None)
104+
peak = video.get("peak_memory_mb") if isinstance(video, dict) else None
105+
return dt, peak
101106

102107
for _ in range(args.warmup):
103108
_gen()

0 commit comments

Comments
 (0)