Skip to content

[TRTLLM-14029][feat] LTX-2 tile-parallel VAE decode#15753

Merged
luyiyun1021 merged 4 commits into
NVIDIA:mainfrom
luyiyun1021:dev/ltx2-vae-tile-parallel
Jul 7, 2026
Merged

[TRTLLM-14029][feat] LTX-2 tile-parallel VAE decode#15753
luyiyun1021 merged 4 commits into
NVIDIA:mainfrom
luyiyun1021:dev/ltx2-vae-tile-parallel

Conversation

@luyiyun1021

@luyiyun1021 luyiyun1021 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Description

Tile-parallel VAE decode for LTX-2. The VAE decode (VideoDecoder.tiled_decode) is a large, embarrassingly-parallel pass over ~27 overlapping spatial/temporal tiles that, served serially, dominates the non-transformer wall time at high resolution. This PR distributes those tiles across the VAE ranks and reassembles the result, so decode latency scales with the number of VAE ranks while per-rank peak memory stays flat.

Approach

  • tile_parallel_decode (new parallel_vae.py): assign tiled_decode's tiles across vae_ranks by LPT on input volume (deterministic, identical partition on every rank), decode each assigned tile, blend it into a full-size output buffer, then all_reduce the buffer + the overlap-weight buffer over vae_group and divide. Output matches serial tiled_decode up to bf16 summation re-association in the tile-overlap regions (a few ulp).
  • setup_parallel_vae (override in pipeline_ltx2.py): LTX-2 decodes via self.video_decoder (not self.vae), so the base factory never engages — set the global _parallel_vae_enabled flag directly from config + mapping. decode_video_fn then routes to tile_parallel_decode on vae_ranks, else the original serial tiled_decode.
  • Tile.blend_mask assembles the per-tile blend mask on-device from its 1-D components, used by both the serial and tile-parallel paths. Assembling the full tile-sized mask on the CPU and copying it to the GPU per tile makes the decode loop host-bound: in a busy multi-process pipeline that host work does not overlap the GPU forward and leaves the GPU ~50% idle. On-device assembly keeps the decode GPU-bound (see E2E below).
  • Per-rank peak memory is flat: each rank decodes one tile at a time (same as serial), so the tiling memory benefit is preserved; the only extra is the (shared-size) output buffer.

Performance (B200)

VAE module bench (real VAE size, bf16 VideoDecoder)

tile_parallel_decode vs serial tiled_decode, latent → (1, 3, 121, 768, 1280) (27 tiles), both on the shipped on-device blend mask:

VAE ranks serial (OFF) tile-parallel (ON) speedup peak mem / rank
1 874 ms 872 ms 1.00× 4.0 GB
2 874 ms 449 ms 1.95× 4.0 GB
4 874 ms 231 ms 3.79× 4.0 GB
8 873 ms 129 ms 6.74× 4.0 GB

Per-rank peak memory is constant ~4.0 GB across world sizes (no memory blow-up from parallelism).

End-to-end (FA4 backend, 8-GPU cfg=2×uly=4, 768×1280×121, 40-step, guidance=4.0, CUDA-graph + torch.compile)

Tile-parallel VAE (pvs=8) vs serial VAE (pvs=1), same seed:

Stage serial VAE (pvs=1) tile-parallel VAE (pvs=8) Δ
Denoise (40 steps) 4.38 s 4.40 s identical (VAE-orthogonal)
E2E wall (text-encode + denoise + decode) 7.71 s 6.91 s −10% (−0.80 s, 1.12×)

The clean, same-basis decode comparison is the module bench above (serial 873 ms → tile-parallel 129 ms, 6.74×). In the FA4 pipeline the parallel VAE decode is 0.14 s (GPU-timed via CUDA events), matching that module-bench floor; that decode saving is what surfaces as the ~10% (0.80 s) E2E wall reduction, with the percentage diluted by the fixed, VAE-orthogonal remainder (text-encode + the 4.4 s denoise + result IPC) that this change does not touch. Denoise is unchanged, confirming the change is VAE-only. The on-device blend mask is what lets the in-pipeline decode reach the module-bench floor — with the mask built on the CPU the decode is ~0.25 s here (GPU ~50% idle waiting on the host mask).

Numerical accuracy

tile_parallel_decode is numerically equivalent to serial tiled_decode: identical tiles and blend masks, so the only difference is bf16 summation re-association in the tile-overlap regions — per-rank partial sums combined by all_reduce vs sequential accumulation on one GPU. Direct parity on the real VAE, world=8, full 768×1280×121 output (same latent, tile_parallel_decode vs single-GPU tiled_decode):

max-abs mean-abs rel-max cosine
0.0156 (= 1 bf16 ULP) 6.2e-5 0.6% 0.99999928

So parallelizing the decode adds no algorithmic error — only floating-point re-association at the bf16 ULP level. The multi-GPU parity unit test (below) asserts rel max-abs < 0.02 and passes.

Perceptually this is invisible. On a deterministic attention backend (same seed → identical denoise, so only the VAE decode differs), parallel (pvs=8) vs serial (pvs=1) decoded video is mean LPIPS 0.017 — at/below the run-to-run noise floor (0.021 for two separate serial runs), i.e. indistinguishable (left = serial, right = tile-parallel; per-frame LPIPS baked in):

parallel-vs-serial VAE, deterministic backend

Tests

tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py — multi-GPU (world=2) parity: a small bf16 VideoDecoder (random weights, broadcast so every rank is identical), tile_parallel_decode vs single-GPU serial tiled_decode, asserting relative max-abs diff < 0.02 (bf16 ulp from overlap-region re-association). Passes on B200 (2-GPU).

PR Checklist

  • PR description clearly explains what and why.

  • PR Follows TRT-LLM CODING GUIDELINES.

  • Test cases are provided for new code paths.

  • Any new dependencies have been scanned for license and vulnerabilities.

  • CODEOWNERS updated if ownership changes.

  • Documentation updated as needed.

  • Update tava architecture diagram if significant design change.

  • Please check this after reviewing the above items as appropriate for this PR.

GitHub Bot Help

To see a list of available CI bot commands, please comment /bot help.

@luyiyun1021 luyiyun1021 requested a review from a team as a code owner June 30, 2026 04:22
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a tile-parallel VAE decoding path for LTX-2 distributed inference. A new parallel_vae.py module implements LPT-based tile assignment and blend-buffer all-reduce across a process group. LTX2Pipeline gains a setup_parallel_vae() method and conditionally routes decoding through this path. A 2-GPU pytest validates numerical parity with serial decoding.

Changes

LTX-2 Tile-Parallel VAE Decode

Layer / File(s) Summary
LPT assignment and tile_parallel_decode
tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py
Adds _tile_in_volume, assign_tiles_lpt (greedy LPT scheduler assigning tiles to least-loaded rank), and tile_parallel_decode (allocates full output/weight buffers, decodes rank-owned tiles with blend masks, all_reduces both buffers across the process group, returns normalized blend).
Pipeline wiring in LTX2Pipeline
tensorrt_llm/_torch/visual_gen/models/ltx2/pipeline_ltx2.py
Imports tile_parallel_decode, adds setup_parallel_vae() override to set _parallel_vae_enabled from parallel_vae_size, distributed state, and visual_gen_mapping, and updates decode_video_fn to route through tile_parallel_decode when enabled.
Multi-GPU parity test
tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py
Adds NCCL distributed harness (mp.spawn, broadcast helpers, MPI env cleanup), small VideoDecoder factory, and parity logic comparing tile_parallel_decode against serial tiled_decode with relative error < 0.02 on 2 GPUs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description is detailed and well-structured, covering problem, approach, performance, accuracy, tests, and checklist items.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding LTX-2 tile-parallel VAE decode.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py (1)

177-179: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add coverage for the pipeline routing layer.

This test sufficiently covers tile_parallel_decode parity, but coverage is still insufficient for the pipeline integration added in pipeline_ltx2.py. Add a follow-up test in this file, or a new tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_pipeline_parallel_vae.py, that verifies setup_parallel_vae() enables the flag from visual_gen_mapping and that decode routes through tile_parallel_decode with vgm.vae_group.

As per path instructions, tests under tests/** should be reviewed for TensorRT-LLM coverage sufficiency and actionable follow-up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py` around
lines 177 - 179, The current parity test in TestLTX2ParallelVAEDecode only
validates tile_parallel_decode behavior, but it does not cover the new pipeline
routing in pipeline_ltx2.py. Add a follow-up test in this file or a new
test_ltx2_pipeline_parallel_vae.py that exercises setup_parallel_vae() and
verifies it enables the visual_gen_mapping flag, then confirm decode is routed
through tile_parallel_decode using vgm.vae_group. Use the existing symbols
setup_parallel_vae, visual_gen_mapping, tile_parallel_decode, and vgm.vae_group
so the coverage targets the pipeline integration directly.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py`:
- Around line 177-179: The current parity test in TestLTX2ParallelVAEDecode only
validates tile_parallel_decode behavior, but it does not cover the new pipeline
routing in pipeline_ltx2.py. Add a follow-up test in this file or a new
test_ltx2_pipeline_parallel_vae.py that exercises setup_parallel_vae() and
verifies it enables the visual_gen_mapping flag, then confirm decode is routed
through tile_parallel_decode using vgm.vae_group. Use the existing symbols
setup_parallel_vae, visual_gen_mapping, tile_parallel_decode, and vgm.vae_group
so the coverage targets the pipeline integration directly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 482adfa0-78b8-4f89-8d98-db2ce38b56e1

📥 Commits

Reviewing files that changed from the base of the PR and between 92147d6 and 5ca4023.

📒 Files selected for processing (3)
  • tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py
  • tensorrt_llm/_torch/visual_gen/models/ltx2/pipeline_ltx2.py
  • tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py

Comment thread tests/unittest/_torch/visual_gen/multi_gpu/test_ltx2_parallel_vae.py Outdated
Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py Outdated
@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch 3 times, most recently from 8ea67aa to c921add Compare July 1, 2026 10:32
@luyiyun1021 luyiyun1021 requested a review from zhenhuaw-me July 1, 2026 10:38
@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from c921add to f71fcec Compare July 2, 2026 03:57
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57110 [ run ] triggered by Bot. Commit: f71fcec Link to invocation

@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from f71fcec to 6a3a492 Compare July 2, 2026 06:30
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57148 [ run ] triggered by Bot. Commit: 6a3a492 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57110 [ run ] completed with state ABORTED. Commit: f71fcec

Link to invocation

@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from 6a3a492 to df96232 Compare July 2, 2026 06:49
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57152 [ run ] triggered by Bot. Commit: df96232 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57148 [ run ] completed with state ABORTED. Commit: 6a3a492

Link to invocation

@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from df96232 to 47502a3 Compare July 2, 2026 07:06
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from 47502a3 to 43e5191 Compare July 2, 2026 07:10
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57156 [ run ] triggered by Bot. Commit: 43e5191 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57152 [ run ] completed with state ABORTED. Commit: df96232

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57158 [ run ] triggered by Bot. Commit: 43e5191 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57156 [ run ] completed with state ABORTED. Commit: 43e5191

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57158 [ run ] completed with state SUCCESS. Commit: 43e5191
/LLM/main/L0_MergeRequest_PR pipeline #45937 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57227 [ run ] triggered by Bot. Commit: 43e5191 Link to invocation

Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/pipeline_ltx2.py
Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py Outdated
Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/parallel_vae.py
@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57227 [ run ] completed with state SUCCESS. Commit: 43e5191
/LLM/main/L0_MergeRequest_PR pipeline #45995 completed with status: 'SUCCESS'

CI Report

Link to invocation

Distribute tiled_decode's overlapping tiles across vae_ranks (LPT by input volume), blend each tile into a full-size buffer, all_reduce the buffer + weights, and divide -- output matches serial tiled_decode within bf16 re-association. setup_parallel_vae sets the global _parallel_vae_enabled flag directly (LTX-2 decodes via video_decoder, not vae, so the base factory never engages); decode_video_fn routes to tile_parallel_decode on vae_ranks. Per-rank peak memory stays flat (one tile in flight per rank, same as serial).

Tile.blend_mask assembles the per-tile blend mask on-device from its 1-D components, used by both the serial and tile-parallel decode paths. Building the full tile-sized mask on the CPU with a per-tile H2D copy left the decode host-bound -- the host mask work does not overlap the GPU forward, leaving the GPU ~50% idle in a busy multi-process pipeline.

Signed-off-by: Yiyun Lu <55233584+luyiyun1021@users.noreply.github.com>
Stage 2 (upsample + refinement denoise) runs rank-0 only; rank 0 then broadcasts the refined latents to every vae_rank and all vae_ranks decode collectively via tile_parallel_decode. Fixes the regression where enabling parallel VAE let all vae_ranks fall through into the rank-0-only Stage 2 (decode_latents' rank set widened from {0} to vae_ranks). Non-parallel two-stage keeps the serial rank-0 decode. Validated on B200: bf16 + nvfp4 two-stage, parallel (pvs2) and serial (pvs1).

Signed-off-by: Yiyun Lu <55233584+luyiyun1021@users.noreply.github.com>
get_nvfp4_input_scale decided whether the fused AdaLN norm emits a pre-quantized Fp4QuantizedTensor from the input_scale attribute alone. The two-stage distilled-LoRA merge dequantizes FP4 weights to bf16 and swaps quant_method to UnquantizedLinearMethod but leaves input_scale on the module, so the fused norm still emitted FP4 for a now-unquantized F.linear -> 'linear(): input must be Tensor, not Fp4QuantizedTensor' at Stage 2. Gate on isinstance(quant_method, NVFP4LinearMethod) so the fusion follows the live quant_method. Prerequisite for nvfp4 two-stage (crashes at Stage 2 warmup without it, independent of CUDA graph / parallel VAE). Single-stage nvfp4 unchanged (quant_method stays NVFP4).

Signed-off-by: Yiyun Lu <55233584+luyiyun1021@users.noreply.github.com>
@luyiyun1021 luyiyun1021 force-pushed the dev/ltx2-vae-tile-parallel branch from 43e5191 to aedf74e Compare July 3, 2026 07:54
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57406 [ run ] triggered by Bot. Commit: aedf74e Link to invocation

@luyiyun1021 luyiyun1021 changed the title [None][feat] LTX-2 tile-parallel VAE decode [TRTLLM-14029][feat] LTX-2 tile-parallel VAE decode Jul 3, 2026
… idle vae_ranks

assign_tiles_lpt logs the per-rank tile-count/volume distribution and max/min imbalance ratio at debug (rank 0), and warns once when parallel_vae_size exceeds the tile count so some vae_ranks receive zero tiles (idle at the all_reduce; suggests lowering parallel_vae_size). The load array is computed deterministically on every rank, so no extra communication is needed.

Signed-off-by: Yiyun Lu <55233584+luyiyun1021@users.noreply.github.com>
@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57417 [ run ] triggered by Bot. Commit: 9becdd8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57406 [ run ] completed with state ABORTED. Commit: aedf74e

Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57417 [ run ] completed with state SUCCESS. Commit: 9becdd8
/LLM/main/L0_MergeRequest_PR pipeline #46160 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@luyiyun1021

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57535 [ run ] triggered by Bot. Commit: 9becdd8 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #57535 [ run ] completed with state SUCCESS. Commit: 9becdd8
/LLM/main/L0_MergeRequest_PR pipeline #46264 completed with status: 'SUCCESS'

CI Report

Link to invocation

Comment thread tensorrt_llm/_torch/visual_gen/models/ltx2/pipeline_ltx2.py
@luyiyun1021 luyiyun1021 merged commit bd7daec into NVIDIA:main Jul 7, 2026
7 checks passed
BrianLi23 pushed a commit to BrianLi23/TensorRT-LLM that referenced this pull request Jul 9, 2026
Signed-off-by: Yiyun Lu <55233584+luyiyun1021@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants