Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbc4195c5a | ||
|
|
982ddef6da | ||
|
|
0d9d4ad132 | ||
|
|
7445aeabfb |
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"benchmark_id": "wan-t2v-1.3b-1gpu-gb10",
|
||||
"config_schema_version": 2,
|
||||
"workload_id": "wan-t2v",
|
||||
"variant_id": "1.3b-sp1",
|
||||
"benchmark_version": 3,
|
||||
"description": "Wan2.1 T2V 1.3B single-GPU inference performance on NVIDIA DGX Spark (GB10). Single-GPU variant of wan-t2v-1.3b (same workload_id for dashboard comparability). Gated to the GB10 via run_config.gpu_types so it does not run on the shared H100/L40S lanes.",
|
||||
"model": {
|
||||
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
|
||||
"model_short_name": "Wan2.1-T2V-1.3B"
|
||||
},
|
||||
"init_kwargs": {
|
||||
"num_gpus": 1,
|
||||
"flow_shift": 7.0,
|
||||
"sp_size": 1,
|
||||
"tp_size": 1,
|
||||
"vae_sp": false,
|
||||
"vae_tiling": true,
|
||||
"text_encoder_precisions": ["fp32"]
|
||||
},
|
||||
"generation_kwargs": {
|
||||
"height": 480,
|
||||
"width": 832,
|
||||
"num_frames": 45,
|
||||
"num_inference_steps": 4,
|
||||
"guidance_scale": 3,
|
||||
"embedded_cfg_scale": 6,
|
||||
"seed": 1024,
|
||||
"fps": 24,
|
||||
"neg_prompt": "Bright tones, overexposed, static, blurred details, subtitles, style, works, paintings, images, static, overall gray, worst quality, low quality, JPEG compression residue, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn faces, deformed, disfigured, misshapen limbs, fused fingers, still picture, messy background, three legs, many people in the background, walking backwards"
|
||||
},
|
||||
"test_prompts": [
|
||||
"Will Smith casually eats noodles, his relaxed demeanor contrasting with the energetic background of a bustling street food market. The scene captures a mix of humor and authenticity. Mid-shot framing, vibrant lighting."
|
||||
],
|
||||
"run_config": {
|
||||
"num_warmup_runs": 2,
|
||||
"num_measurement_runs": 5,
|
||||
"required_gpus": 1,
|
||||
"gpu_types": ["GB10"]
|
||||
},
|
||||
"thresholds": {
|
||||
"GB10": {
|
||||
"max_generation_time_s": 55.0,
|
||||
"max_peak_memory_mb": 12000.0
|
||||
},
|
||||
"default": {
|
||||
"max_generation_time_s": 120.0,
|
||||
"max_peak_memory_mb": 40000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,6 +58,54 @@ records, see `performance_dashboard/README.md`. The dashboard provides a
|
||||
FastAPI API plus a React UI and can be exposed with `ngrok` after building the
|
||||
frontend.
|
||||
|
||||
## DGX Spark (GB10) local benchmarking
|
||||
|
||||
The NVIDIA DGX Spark (GB10) is not available on Modal, so its coverage is
|
||||
**local/manual** rather than automated CI. The GB10 benchmark
|
||||
`wan-t2v-1.3b-1gpu-gb10` is gated to the GB10 via `run_config.gpu_types`
|
||||
(matched as substrings of the CUDA device name), so the shared H100/L40S
|
||||
performance lanes discover it and skip it, while a GB10 owner runs it locally.
|
||||
|
||||
Run just the GB10 benchmark on a DGX Spark:
|
||||
|
||||
```bash
|
||||
pytest 'fastvideo/tests/performance/test_inference_performance.py::test_inference_performance[wan-t2v-1.3b-1gpu-gb10]' -vs
|
||||
```
|
||||
|
||||
To check run-to-run stability (latency, peak memory, throughput), run it a few
|
||||
times from a clean results directory, then normalize:
|
||||
|
||||
```bash
|
||||
rm -f fastvideo/tests/performance/results/perf_*.json
|
||||
for i in 1 2 3 4 5; do
|
||||
pytest 'fastvideo/tests/performance/test_inference_performance.py::test_inference_performance[wan-t2v-1.3b-1gpu-gb10]' -vs
|
||||
done
|
||||
PERF_REPORTS_DIR=/tmp/fastvideo_perf_reports \
|
||||
python fastvideo/tests/performance/compare_baseline.py
|
||||
```
|
||||
|
||||
`compare_baseline.py` reports `CALIBRATION_NEEDED` until a baseline exists for
|
||||
the GB10 identity, and writes one `normalized_perf_*.json` per run. Reference
|
||||
figures on a GB10 (torch 2.12.0+cu130, transformers 5.14.0): generation ~39.3 s,
|
||||
peak ~8.4 GB, throughput ~1.15 fps, stable to ~0.4% across five runs.
|
||||
|
||||
### Seeding the GB10 baseline (follow-up)
|
||||
|
||||
Seeding a baseline-eligible record for the GB10 identity is intentionally **not**
|
||||
done from a local run: `seed_baseline.py` accepts only `scheduled_main`
|
||||
full-suite source artifacts, so ordinary local/manual uploads stay
|
||||
`baseline_eligible=false` (dashboard-visible, but they do not move the rolling
|
||||
baseline). Establishing the GB10 baseline requires either:
|
||||
|
||||
* a scheduled-main performance run on a GB10 CI runner once one is available, or
|
||||
* a carefully scoped, maintainer-approved manual calibration path that preserves
|
||||
the existing exact-identity, batch-consistency, provenance, and
|
||||
explicit-approval safeguards — it must **not** make arbitrary local uploads
|
||||
baseline-eligible.
|
||||
|
||||
The reviewed five-run GB10 artifacts are the stability evidence for that first
|
||||
baseline. Tracked in #1632.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
|
||||
@@ -127,3 +127,44 @@ def test_regression_thresholds_validate_without_forcing_v2_schema():
|
||||
cfg["regression_thresholds"] = []
|
||||
with pytest.raises(ValueError, match="benchmark config field 'regression_thresholds' must be an object"):
|
||||
_validate_benchmark_config(cfg, "legacy.json")
|
||||
|
||||
|
||||
def test_gpu_type_gate_skips_non_matching_device():
|
||||
from fastvideo.tests.performance.test_inference_performance import (
|
||||
_gpu_type_skip_reason,
|
||||
)
|
||||
|
||||
cfg = {"benchmark_id": "wan-t2v-1.3b-1gpu-gb10"}
|
||||
run_config = {"gpu_types": ["GB10"]}
|
||||
|
||||
# Matching device (substring) runs; non-matching devices skip.
|
||||
assert _gpu_type_skip_reason(cfg, run_config, "NVIDIA GB10") is None
|
||||
assert _gpu_type_skip_reason(cfg, run_config, "NVIDIA H100 80GB HBM3") is not None
|
||||
assert _gpu_type_skip_reason(cfg, run_config, "NVIDIA L40S") is not None
|
||||
|
||||
|
||||
def test_gpu_type_gate_absent_field_runs_everywhere():
|
||||
from fastvideo.tests.performance.test_inference_performance import (
|
||||
_gpu_type_skip_reason,
|
||||
)
|
||||
|
||||
cfg = {"benchmark_id": "wan-t2v-1.3b-2gpu"}
|
||||
# No gpu_types key, and an empty list, both run on any device.
|
||||
assert _gpu_type_skip_reason(cfg, {}, "NVIDIA H100 80GB HBM3") is None
|
||||
assert _gpu_type_skip_reason(cfg, {"gpu_types": []}, "NVIDIA H100") is None
|
||||
|
||||
|
||||
def test_gpu_types_must_be_list_of_nonempty_strings():
|
||||
# A bare string (would iterate characters), non-string entries, and empty
|
||||
# strings are all rejected at config-discovery time.
|
||||
for bad in ("GB10", ["GB10", 3], [""], [None]):
|
||||
cfg = {"benchmark_id": "x", "run_config": {"gpu_types": bad}}
|
||||
with pytest.raises(ValueError, match="gpu_types"):
|
||||
_validate_benchmark_config(cfg, "x.json")
|
||||
|
||||
# A valid list, an absent field, and an empty list all pass.
|
||||
_validate_benchmark_config(
|
||||
{"benchmark_id": "x", "run_config": {"gpu_types": ["GB10"]}}, "x.json")
|
||||
_validate_benchmark_config({"benchmark_id": "x"}, "x.json")
|
||||
_validate_benchmark_config(
|
||||
{"benchmark_id": "x", "run_config": {"gpu_types": []}}, "x.json")
|
||||
|
||||
@@ -98,6 +98,15 @@ def _validate_benchmark_config(cfg, path="<memory>"):
|
||||
if field in cfg and not isinstance(cfg[field], Mapping):
|
||||
raise ValueError(f"{path}: benchmark config field {field!r} must be an object")
|
||||
|
||||
run_config = cfg.get("run_config")
|
||||
if isinstance(run_config, Mapping):
|
||||
gpu_types = run_config.get("gpu_types")
|
||||
if gpu_types is not None and (
|
||||
not isinstance(gpu_types, (list, tuple))
|
||||
or any(not isinstance(g, str) or not g for g in gpu_types)):
|
||||
raise ValueError(
|
||||
f"{path}: run_config.gpu_types must be a list of non-empty strings")
|
||||
|
||||
schema_version = cfg.get("config_schema_version")
|
||||
if schema_version is None:
|
||||
if _has_v2_fields(cfg):
|
||||
@@ -518,6 +527,20 @@ def _build_result_record(
|
||||
|
||||
# -- Test -------------------------------------------------------------------
|
||||
|
||||
def _gpu_type_skip_reason(cfg, run_config, device_name):
|
||||
"""Return a skip reason if the config restricts itself to GPU types the
|
||||
current device does not match, else None.
|
||||
|
||||
``run_config.gpu_types`` is an optional list of substrings matched against
|
||||
the CUDA device name, so a hardware-specific config (e.g. a DGX Spark GB10
|
||||
single-GPU workload) does not run on the shared H100/L40S lanes. Configs
|
||||
without ``gpu_types`` run on any device, as before.
|
||||
"""
|
||||
gpu_types = run_config.get("gpu_types")
|
||||
if gpu_types and not any(g in device_name for g in gpu_types):
|
||||
return (f"{cfg['benchmark_id']} is restricted to gpu_types={gpu_types}, "
|
||||
f"current device is {device_name!r}")
|
||||
return None
|
||||
|
||||
def _run_benchmark(cfg):
|
||||
run_config = cfg.get("run_config") or {}
|
||||
@@ -530,6 +553,11 @@ def _run_benchmark(cfg):
|
||||
if available < num_gpus:
|
||||
pytest.skip(f"Need {num_gpus} GPUs, only {available} available")
|
||||
|
||||
skip_reason = _gpu_type_skip_reason(cfg, run_config,
|
||||
torch.cuda.get_device_name())
|
||||
if skip_reason:
|
||||
pytest.skip(skip_reason)
|
||||
|
||||
gen_kwargs = dict(cfg.get("generation_kwargs", {}))
|
||||
prompts = cfg.get("test_prompts", ["A cinematic video."])
|
||||
prompt = prompts[0]
|
||||
|
||||
Reference in New Issue
Block a user