Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
147 changes: 147 additions & 0 deletions examples/inference/basic/basic_fasth3_simplified.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# SPDX-License-Identifier: Apache-2.0
"""Generate 5-second FastH3 videos with a supported Preview adapter."""

from __future__ import annotations

import argparse
import os
import statistics
import sys
import time
from pathlib import Path

from huggingface_hub import hf_hub_download

FASTVIDEO_ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(FASTVIDEO_ROOT))

from fastvideo import VideoGenerator # noqa: E402
from fastvideo.api import ( # noqa: E402
CompileConfig,
ComponentConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
OffloadConfig,
OutputConfig,
ParallelismConfig,
PipelineSelection,
SamplingConfig,
)

# FastVideo exposes these three backend switches only through environment variables.
os.environ.update({
"FASTVIDEO_FA4": "1",
"FASTVIDEO_MINIMAX_H3_FUSIONS": "all",
"FASTVIDEO_VSA_SM100A": "0",
})
os.environ.pop("FASTVIDEO_INFERENCE_TORCH_COMPILE", None)

VARIANT_BACKENDS = {
"dense-datafree": "FLASH_ATTN",
"vsa-datafree": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1300": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1900": "VIDEO_SPARSE_ATTN_H3",
}


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("variant", choices=VARIANT_BACKENDS)
parser.add_argument("--prompt", required=True)
return parser.parse_args()


def main() -> None:
"""Run one compile warmup and three measured generations with one fixed recipe."""
args = parse_args()

attention_backend = VARIANT_BACKENDS[args.variant]
experimental = {
"attention_backend": attention_backend,
"inference_torch_compile": attention_backend == "FLASH_ATTN",
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
}
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
experimental.update({
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
})

adapter_path = hf_hub_download(
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
filename=f"{args.variant}/adapter_model.safetensors",
)
output_dir = FASTVIDEO_ROOT / "outputs/fasth3_lora_preview" / args.variant
output_dir.mkdir(parents=True, exist_ok=True)

generator = VideoGenerator.from_config(
GeneratorConfig(
model_path="MiniMaxAI/MiniMax-H3",
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
experimental=experimental,
),
engine=EngineConfig(
num_gpus=4,
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
offload=OffloadConfig(dit=False, dit_layerwise=False),
compile=CompileConfig(vae_enabled=True),
),
)
)

generation_count = 4
warmup_count = 1
measured_count = generation_count - warmup_count
measured_seconds: list[float] = []

print(f"Variant: {args.variant} ({attention_backend})")
print(f"Output directory: {output_dir}")
try:
for generation_index in range(generation_count):
measured = generation_index >= warmup_count
if measured:
measured_index = generation_index - warmup_count + 1
label = f"measured {measured_index}/{measured_count}"
output_path = output_dir / f"fasth3_all_run_{measured_index:02d}.mp4"
else:
warmup_index = generation_index + 1
label = f"warmup {warmup_index}/{warmup_count}"
output_path = output_dir / f"_fasth3_warmup_{warmup_index:02d}.mp4"

request = GenerationRequest(
prompt=args.prompt,
negative_prompt="",
sampling=SamplingConfig(
height=768,
width=1344,
num_frames=345, # <-- Change video length: 5sec: 124; 10sec: 243; 15sec: 345.
fps=24,
num_inference_steps=5,
guidance_scale=1.0,
batch_cfg=False,
seed=1000,
),
output=OutputConfig(
output_path=str(output_path),
save_video=True,
return_frames=False,
),
)
started = time.perf_counter()
result = generator.generate(request)
elapsed = time.perf_counter() - started
if measured:
measured_seconds.append(elapsed)
suffix = "" if measured else " (excluded from median)"
print(f"[{label}] {result.video_path or output_path}: {elapsed:.3f}s{suffix}")
finally:
generator.shutdown()

print(f"Median E2E wall time: {statistics.median(measured_seconds):.3f}s")


if __name__ == "__main__":
main()
10 changes: 10 additions & 0 deletions examples/inference/basic/basic_fasth3_simplified.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash

PROMPT="integrated_multimodal_description: A red fox runs through fresh snow at dawn. overall_soundscape: Fast pawsteps in snow, winter wind, and distant birds."

# Options: dense-datafree, vsa-datafree, vsa-synthetic-step1300, vsa-synthetic-step1900
VARIANT="vsa-datafree"

exec python "$(dirname "$0")/basic_fasth3_simplified.py" \
"$VARIANT" \
--prompt "$PROMPT"
126 changes: 126 additions & 0 deletions examples/inference/basic/basic_fasth3_simplified_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# SPDX-License-Identifier: Apache-2.0
"""Profile one FastH3 Preview generation with NVTX after warmup."""

from __future__ import annotations

import argparse
from pathlib import Path

import torch
from huggingface_hub import hf_hub_download

from fastvideo import VideoGenerator
from fastvideo.api import (
CompileConfig,
ComponentConfig,
EngineConfig,
GenerationRequest,
GeneratorConfig,
OffloadConfig,
OutputConfig,
ParallelismConfig,
PipelineSelection,
QuantizationConfig,
SamplingConfig,
)
from fastvideo.profiler import nvtx_range

VARIANT_BACKENDS = {
"dense-datafree": "FLASH_ATTN",
"vsa-datafree": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1300": "VIDEO_SPARSE_ATTN_H3",
"vsa-synthetic-step1900": "VIDEO_SPARSE_ATTN_H3",
}


def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("variant", choices=VARIANT_BACKENDS)
parser.add_argument("--prompt", required=True)
parser.add_argument("--output", required=True)
parser.add_argument("--num-frames", type=int, default=345)
parser.add_argument("--warmup-runs", type=int, default=3)
return parser.parse_args()


def main() -> None:
"""Warm the selected FastH3 recipe, then profile one identical generation."""
args = parse_args()
output_dir = Path(args.output)
output_dir.mkdir(parents=True, exist_ok=True)

attention_backend = VARIANT_BACKENDS[args.variant]
experimental = {
"attention_backend": attention_backend,
"inference_torch_compile": attention_backend == "FLASH_ATTN",
"vae_parallel_decode": True,
"vae_parallel_decode_strategy": "gather",
}
if attention_backend == "VIDEO_SPARSE_ATTN_H3":
experimental.update({
"VSA_sparsity": 0.9,
"VSA_tile_size": 64,
})

adapter_path = hf_hub_download(
repo_id="FastVideo/FastVideo-FastH3-4-step-Preview-v1-LoRA",
filename=f"{args.variant}/adapter_model.safetensors",
)
generator = VideoGenerator.from_config(
GeneratorConfig(
model_path="MiniMaxAI/MiniMax-H3",
pipeline=PipelineSelection(
components=ComponentConfig(lora_path=adapter_path, lora_strength=1.0),
experimental=experimental,
),
engine=EngineConfig(
num_gpus=4,
parallelism=ParallelismConfig(tp_size=1, sp_size=4),
offload=OffloadConfig(dit=False, dit_layerwise=False),
compile=CompileConfig(vae_enabled=True),
quantization=QuantizationConfig(transformer_quant="MXFP8"),
),
)
)

request = GenerationRequest(
prompt=args.prompt,
negative_prompt="",
sampling=SamplingConfig(
height=768,
width=1344,
num_frames=args.num_frames,
fps=24,
num_inference_steps=5,
guidance_scale=1.0,
batch_cfg=False,
seed=1000,
),
output=OutputConfig(
output_path=str(output_dir / "fasth3_profile.mp4"),
save_video=True,
return_frames=False,
),
)

try:
for warmup_index in range(args.warmup_runs):
warmup_result = generator.generate(request)
print(f"Warmup {warmup_index + 1}/{args.warmup_runs}: {warmup_result.video_path}")

torch.cuda.profiler.start()
try:
with nvtx_range("fasth3.profiled_generation"):
measured_result = generator.generate(request)
finally:
torch.cuda.profiler.stop()

print(f"Profiled output: {measured_result.video_path}")
if measured_result.generation_time is not None:
print(f"Generation time: {measured_result.generation_time:.2f}s")
finally:
generator.shutdown()


if __name__ == "__main__":
main()
53 changes: 53 additions & 0 deletions examples/inference/basic/basic_fasth3_simplified_profile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Capture one FastH3 Preview generation with CUDA and FastVideo NVTX ranges.

set -euo pipefail

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
WORKTREE_ROOT="$(cd -- "${SCRIPT_DIR}/../../.." && pwd)"
WORKSPACE_ROOT="$(cd -- "${WORKTREE_ROOT}/.." && pwd)"
PYTHON_BIN="${WORKSPACE_ROOT}/.venv-fv/bin/python"
NSYS_BIN=/usr/local/cuda/bin/nsys
PROFILE_SCRIPT="${SCRIPT_DIR}/basic_fasth3_simplified_profile.py"

export CUDA_VISIBLE_DEVICES=0,1,2,3
export FASTVIDEO_FA4=1
export FASTVIDEO_INFERENCE_TORCH_COMPILE=0
export FASTVIDEO_MINIMAX_H3_FUSIONS=all
export FASTVIDEO_NVTX_PROFILE=1
export FASTVIDEO_VSA_SM100A=0
export PYTHONUNBUFFERED=1
export PYTHONPATH="${WORKTREE_ROOT}${PYTHONPATH:+:${PYTHONPATH}}"

PROMPT="integrated_multimodal_description: A red fox runs through fresh snow at dawn. overall_soundscape: Fast pawsteps in snow, winter wind, and distant birds."

# Options: dense-datafree, vsa-datafree, vsa-synthetic-step1300, vsa-synthetic-step1900
VARIANT="vsa-datafree"
NUM_FRAMES=345
WARMUP_RUNS=3

PROFILE_ID="${VARIANT}_4gpu_sp4_${NUM_FRAMES}frames"
RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$$"
RESULT_DIR="${WORKTREE_ROOT}/runs/fasth3_lora_preview_profile/${PROFILE_ID}/${RUN_ID}"
MEDIA_DIR="${RESULT_DIR}/media"
RUN_LOG="${RESULT_DIR}/run.log"
mkdir -p "${MEDIA_DIR}"

"${NSYS_BIN}" profile \
--trace=cuda,nvtx \
--sample=none \
--cpuctxsw=none \
--capture-range=cudaProfilerApi \
--capture-range-end=stop \
--output="${RESULT_DIR}/${PROFILE_ID}" \
"${PYTHON_BIN}" "${PROFILE_SCRIPT}" \
"${VARIANT}" \
--num-frames "${NUM_FRAMES}" \
--warmup-runs "${WARMUP_RUNS}" \
--prompt "${PROMPT}" \
--output "${MEDIA_DIR}" \
2>&1 | tee "${RUN_LOG}"

printf 'RESULT_DIR=%s\n' "${RESULT_DIR}"
printf 'NSYS_REPORT=%s\n' "${RESULT_DIR}/${PROFILE_ID}.nsys-rep"
printf 'MEDIA_DIR=%s\n' "${MEDIA_DIR}"
Loading
Loading