Skip to content

[docs] LTX-2.3 distilled i2v example with compile + timing breakdown - #1430

Merged
mergify[bot] merged 5 commits into
hao-ai-lab:mainfrom
FoundationResearch:ltx2.3-distilled-i2v-example
Jun 5, 2026
Merged

[docs] LTX-2.3 distilled i2v example with compile + timing breakdown#1430
mergify[bot] merged 5 commits into
hao-ai-lab:mainfrom
FoundationResearch:ltx2.3-distilled-i2v-example

Conversation

@alexzms

@alexzms alexzms commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds examples/inference/basic/basic_ltx2_3_distilled_i2v.py — a single-GPU LTX-2.3 distilled image-to-video example with torch.compile fully enabled and a per-stage timing breakdown.

Why

After #1397 merged LTX-2.3 transformer support into main, the existing LTX-2 examples leave LTX-2.3 i2v uncovered:

existing i2v? compile? benchmark? LTX-2.3?
basic_ltx2.py — (LTX-2.0)
basic_ltx2_distilled.py — (LTX-2.0)
basic_ltx2_distilled_fast_profile.py — (LTX-2.0)
this PR

Anyone trying to run the just-merged LTX-2.3 i2v path on main currently has to stitch together the right preset, refine config, image conditioning kwargs, and compile flags from the codebase. This file is the one-stop copy-paste.

What the script does

  1. Loads FastVideo/LTX-2.3-Distilled-Diffusers (registered in [feat] LTX-2.3 transformer support (config-gated extension of LTX-2) #1397).
  2. Compiles DiT + text encoder with fullgraph=True, max-autotune-no-cudagraphs, dynamic=False.
  3. 2 warmup + 2 measured runs. Two warmups (not one) because Inductor's per-shape autotune leaves a few cold guards on call 2 even for the distilled (no-refine-LoRA) graph; one warmup typically inflates the first measured run by tens of seconds. The docstring documents this so users don't trim warmup and get a misleading number.
  4. Prints stage breakdown for each measured run + an average.

Usage

export LTX23_I2V_IMAGE=/path/to/your/portrait_or_product.jpg
# optional:
#   export LTX23_I2V_PROMPT="a fashion model walks toward camera..."
#   export LTX23_OUTPUT_DIR=outputs_video/ltx2_3_distilled_i2v
python examples/inference/basic/basic_ltx2_3_distilled_i2v.py

Hardware notes (in docstring)

  • Single-GPU; for multi-GPU SP see the LTX-2.3 gradio demo.
  • First-time compile + autotune ~10–30 min on H100 / GB200, cached after.
  • On Blackwell, launch with env -u LD_LIBRARY_PATH ... to avoid the system-vs-torch cuBLAS mismatch, and _inductor.shape_padding = False (set in the script) to avoid a pad_mm crash in refine.

Test plan

  • py_compile — clean.
  • ruff check — clean.
  • End-to-end run on the actual LTX-2.3 distilled snapshot with enable_torch_compile=True (drafting this PR so reviewers can comment on style/structure in parallel with a full run; will mark ready-for-review once a clean full run lands).

Related

Add `examples/inference/basic/basic_ltx2_3_distilled_i2v.py`: a single-GPU
LTX-2.3 distilled image-to-video example with torch.compile fully enabled,
two warmup runs to settle Inductor's per-shape autotune, two measured
runs, and a per-stage timing breakdown.

Fills a gap on `main`: the existing `basic_ltx2*.py` examples are t2v-only
and target LTX-2.0 distilled. After PR hao-ai-lab#1397 merged LTX-2.3, there was no
copy-paste example for the LTX-2.3 i2v path with compile + benchmark
plumbing wired in.

The script reads the conditioning image from `LTX23_I2V_IMAGE` (errors
out with a helpful message if unset) and uses a generic fashion-runway
prompt that the user can override via `LTX23_I2V_PROMPT`. Defaults match
the production recipe documented in the docstring: 8 denoise + 3 refine
steps, CFG=1, 832x1280 portrait, 121 frames @ 24fps. Includes a comment
calling out the Blackwell `shape_padding=False` requirement and the
`env -u LD_LIBRARY_PATH` launch tip from prior experience on GB200.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new example script, basic_ltx2_3_distilled_i2v.py, which demonstrates running the LTX-2.3 distilled image-to-video model with torch.compile enabled and provides a detailed per-stage timing breakdown. Feedback on the changes points out a potential issue with the cleanup of warmup video files: since the generator automatically appends numeric suffixes to filenames if they already exist, hardcoded cleanup paths might fail to delete the actual generated files. It is recommended to capture and use the actual video paths returned by the generator for a robust cleanup.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +206 to +220
for w in range(warmup_runs):
t0 = time.perf_counter()
print(f"\n[warmup {w + 1}/{warmup_runs}] compiling + generating…")
generator.generate_video(
output_path=str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4"),
seed=7,
**common_kwargs,
)
dt = time.perf_counter() - t0
warmup_secs.append(dt)
print(f"[warmup {w + 1}/{warmup_runs}] wall={dt:.1f}s")

# Cleanup warmup artifacts so the user only sees measured outputs.
for w in range(warmup_runs):
(OUTPUT_DIR / f"_warmup_{w + 1}.mp4").unlink(missing_ok=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In VideoGenerator, the _prepare_output_path method automatically appends a numeric suffix (e.g., _1, _2) to the output filename if a file with the same name already exists in the target directory. If a previous run of this script was interrupted or if the warmup files were not cleaned up, _warmup_1.mp4 might already exist. Consequently, the generator will write the new warmup video to _warmup_1_1.mp4, but the cleanup loop will only attempt to delete _warmup_1.mp4, leaving the newly generated warmup file behind. To prevent this and ensure robust cleanup, capture the returned result from generate_video and use the actual video_path returned by the generator to perform the cleanup.

Suggested change
for w in range(warmup_runs):
t0 = time.perf_counter()
print(f"\n[warmup {w + 1}/{warmup_runs}] compiling + generating…")
generator.generate_video(
output_path=str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4"),
seed=7,
**common_kwargs,
)
dt = time.perf_counter() - t0
warmup_secs.append(dt)
print(f"[warmup {w + 1}/{warmup_runs}] wall={dt:.1f}s")
# Cleanup warmup artifacts so the user only sees measured outputs.
for w in range(warmup_runs):
(OUTPUT_DIR / f"_warmup_{w + 1}.mp4").unlink(missing_ok=True)
warmup_paths = []
for w in range(warmup_runs):
t0 = time.perf_counter()
print(f"\n[warmup {w + 1}/{warmup_runs}] compiling + generating…")
result = generator.generate_video(
output_path=str(OUTPUT_DIR / f"_warmup_{w + 1}.mp4"),
seed=7,
**common_kwargs,
)
if isinstance(result, dict) and result.get("video_path"):
warmup_paths.append(result["video_path"])
dt = time.perf_counter() - t0
warmup_secs.append(dt)
print(f"[warmup {w + 1}/{warmup_runs}] wall={dt:.1f}s")
# Cleanup warmup artifacts so the user only sees measured outputs.
for path in warmup_paths:
Path(path).unlink(missing_ok=True)

@mergify mergify Bot added the scope: inference Inference pipeline, serving, CLI label Jun 3, 2026
@mergify

mergify Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

Your pull request matches the following merge protections and will not be merged until they are valid.

🟢 PR merge requirements

Wonderful, this rule succeeded.
  • #approved-reviews-by>=1
  • check-success=fastcheck-passed
  • check-success=full-suite-passed
  • check-success~=pre-commit
  • title~=(?i)^\[(feat|feature|bugfix|fix|refactor|perf|ci|doc|docs|misc|chore|kernel|new.?model|skill|skills|infra)\]

@alexzms
alexzms force-pushed the ltx2.3-distilled-i2v-example branch from a50bd67 to 4eee71f Compare June 3, 2026 20:34
@alexzms alexzms changed the title [example] LTX-2.3 distilled i2v with compile + timing breakdown [example] LTX-2.3 distilled i2v with timing breakdown Jun 3, 2026
@alexzms
alexzms marked this pull request as ready for review June 3, 2026 20:34
@mergify

mergify Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR title format required

Your PR title must start with a type tag in brackets. Examples:

  • [feat] Add new model support
  • [bugfix] Fix VAE tiling corruption
  • [refactor] Restructure training pipeline
  • [perf] Optimize attention kernel
  • [ci] Update test infrastructure
  • [infra] Add activation trace hooks
  • [docs] Add inference guide
  • [misc] Clean up configs
  • [new-model] Port Flux2 to FastVideo
  • [skill] Add add-model agent skill

Valid tags: feat, feature, bugfix, fix, refactor, perf, ci, infra, doc, docs, misc, chore, kernel, new-model, skill, skills

Please update your PR title and the merge protection check will pass automatically.

@alexzms
alexzms force-pushed the ltx2.3-distilled-i2v-example branch from 4eee71f to a50bd67 Compare June 3, 2026 20:39
@alexzms alexzms changed the title [example] LTX-2.3 distilled i2v with timing breakdown [example] LTX-2.3 distilled i2v with compile + timing breakdown Jun 3, 2026
@alexzms
alexzms marked this pull request as draft June 3, 2026 20:39
alexzms added 2 commits June 4, 2026 18:17
`PipelineConfig.from_pretrained(model_root)` instantiates the registered
pipeline-config subclass with `model_path` threaded into the constructor,
which carries model-specific defaults (notably VAE precision / decoder
configuration) that the generic `PipelineConfig()` path lacks.

Mirrors the existing `basic_ltx2_distilled_fast_profile.py` pattern.
Validated end-to-end on GB200: e2e drops modestly (5.31s -> 4.98s) from
slightly faster DiT denoise + refine timings.

The remaining ~0.8s decode-stage gap vs the internal compare_gallery
harness is unrelated — that one is from the LTX-2 VAE forward methods
running eager on `main` (internal applies `@torch.compile` decorators
to `VideoEncoder.forward` / `VideoDecoder.forward`). To be addressed in
a separate follow-up PR.
The LTX-2 VAE class declares `_compile_conditions = [_is_ltx2_vae_codec]`
so `composed_pipeline_base._compile_with_conditions` targets just the
`encoder` / `decoder` submodules (leaving the surrounding tiling control
flow eager). Setting `enable_torch_compile_vae=True` lets that path
trigger and brings the decoding stage from ~1.3s to ~0.3s.

Validated end-to-end on a single GB200, cold cache:
  warmup wall-times: [2588.3, 4.1]
  measured e2e (n=2): [3.97, 3.74] -> avg 3.85s
  stage breakdown: denoise 1.244s, refine 1.646s, decode 0.275s,
                   audio 0.124s, prompt_encoding 0.046s
  stage_sum_avg 3.831s ~ e2e 3.85s (no hidden overhead)

This matches the per-stage profile we get from the internal benchmark
harness (which uses an older unconditional decorator approach on the
same VAE forward methods) within measurement variance.
@alexzms

alexzms commented Jun 5, 2026

Copy link
Copy Markdown
Collaborator Author

E2E validated on a single GB200, fork @ a5dbaf9 against upstream/main HEAD. Cold inductor cache, default config (LTX23_I2V_IMAGE=person_fullbody.jpg, 832×1280, 121 frames @ 24fps, 8 denoise + 3 refine):

warmup wall-times:      [2588.3, 4.1]
measured e2e (n=2): [3.97, 3.74] -> avg 3.85s
average stage times over 2 measured runs:
  - input_validation_stage: 0.000s
  - prompt_encoding_stage: 0.046s
  - ltx2_refine_init_stage: 0.000s
  - latent_preparation_stage: 0.016s
  - denoising_stage: 1.244s
  - ltx2_upsample_stage: 0.027s
  - ltx2_refine_denoising_stage: 1.646s
  - audio_decoding_stage: 0.124s
  - decoding_stage: 0.275s
  - PostDecodeFrameProcessStage: 0.078s
  - VideoSaveStage: 0.374s
  - AudioMuxStage: 0.000s
  - stage_sum_avg: 3.831s

stage_sum_avg matches e2e within tens of ms — no hidden overhead.

Marking ready-for-review.

@alexzms
alexzms marked this pull request as ready for review June 5, 2026 00:32
@mergify

mergify Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR title format required

Your PR title must start with a type tag in brackets. Examples:

  • [feat] Add new model support
  • [bugfix] Fix VAE tiling corruption
  • [refactor] Restructure training pipeline
  • [perf] Optimize attention kernel
  • [ci] Update test infrastructure
  • [infra] Add activation trace hooks
  • [docs] Add inference guide
  • [misc] Clean up configs
  • [new-model] Port Flux2 to FastVideo
  • [skill] Add add-model agent skill

Valid tags: feat, feature, bugfix, fix, refactor, perf, ci, infra, doc, docs, misc, chore, kernel, new-model, skill, skills

Please update your PR title and the merge protection check will pass automatically.

@alexzms alexzms changed the title [example] LTX-2.3 distilled i2v with compile + timing breakdown [docs] LTX-2.3 distilled i2v example with compile + timing breakdown Jun 5, 2026
@mergify mergify Bot added the type: docs Documentation only label Jun 5, 2026
@alexzms alexzms added the ready PR is ready to merge label Jun 5, 2026
mergify Bot and others added 2 commits June 5, 2026 01:21
The pre-commit workflow's pull_request event triggers on default types
(opened/synchronize/reopened) and was skipped on the original commits
because the PR was still in draft. This empty commit re-triggers the
synchronize event now that the PR is ready-for-review.
@mergify
mergify Bot merged commit 922e7e0 into hao-ai-lab:main Jun 5, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready PR is ready to merge scope: inference Inference pipeline, serving, CLI type: docs Documentation only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants