Skip to content

[optimization] Added Wan 2.2 VAE encode optimizations - #8067

Merged
princepride merged 40 commits into
vllm-project:mainfrom
MaciejBalaNV:mbala/wan-vae-encoder-opt
Oct 11, 2026
Merged

princepride merged 40 commits into
vllm-project:mainfrom
MaciejBalaNV:mbala/wan-vae-encoder-opt

Conversation

@MaciejBalaNV

@MaciejBalaNV MaciejBalaNV commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

This PR optimizes Wan 2.2 VAE encode, mirroring #7056 which optimized decode. There are 2 new modes added: lossless and channels_last. lossless is bit-identical to the baseline. channels_last can have minimal differences. lossless achieves 1.76x speedup over the baseline while channels_last is 2.39x faster than the baseline for GB200 GPU.

Purpose

This PR adds VAE encoder optimizations for Cosmos3’s residual Wan2.2 architecture, building on the decoder optimizations in #7056.

The lossless mode implements the following optimizations:

  • RMSNorm epilogue: ATen vector_norm retains the fp32 reduction; divide, scale, gamma and + 0.0 are fused while preserving ATen’s intermediate rounding. SiLU is folded in only for dtypes that pass the installation-time bitwise exactness check.
  • Causal-convolution input assembly: cache cloning, temporal concatenation, padding and cache refresh are fused. Spatial padding is delegated to cuDNN after a per-shape bitwise check; rejected shapes use explicitly padded inputs. Zero-padding operations are skipped when all padding sizes are zero.
  • Normalization-to-cache fusion: eligible RMSNorm+SiLU operations write directly into the temporally assembled convolution input and the next feature-cache entry, avoiding a separate normalized activation tensor. Already-normalized history is copied unchanged.
  • Residual bias fusion: conv2 and conv_shortcut biases are folded into the residual-add kernel while preserving ATen’s rounding points. Unnecessary shortcut clones are removed when the modules cannot mutate their inputs.
  • Downsampling input assembly: spatial padding and frame-layout conversion are fused into one kernel. Temporal downsampling reuses fused concatenation and cache refresh while preserving the first-chunk behavior. Shortcut averaging retains the reference ATen reduction.
  • Chunked input and output assembly: untiled encoding patchifies one temporal chunk at a time and writes encoder features into a preallocated output tensor. The reference schedule—one initial frame, then four frames per chunk—is preserved, and quant_conv runs once after assembly.

The channels_last mode additionally converts convolution weights to channels-last memory formats to reduce runtime transposes. It uses single-pass RMSNorm+SiLU with the preceding conv1 bias folded in, and fuses AvgDown3D shortcut averaging with residual addition.

These paths can change floating-point reduction order, so outputs are validated within tolerance rather than bitwise.

The encoder is configured independently from the decoder through --vae-encode-fast-path, accepting off, lossless and channels_last. lossless is the default for supported encoders. Unsupported architectures retain reference encoding.

The change supports VAE tiling and tile parallelism and can coexist with spatially sharded decoding. Encoder spatial parallelism continues to use tiling.

A dedicated encoder benchmark compares all three modes, reporting startup and warmed latency, throughput, peak memory, posterior accuracy and optional reconstruction quality. It enforces bitwise equality for lossless and numerical quality gates for channels_last.

Test Plan

Unit tests

This PR adds 55 new test functions which check for bit-identical outputs and correct shapes of the new Triton kernels.

Serving tests

We test the outputs of Cosmos3-Nano model following this notebook. All of the baseline and lossless outputs are bit-identical.

vLLM Version:
0.30
vLLM-Omni Commit:
ToT

Test Result

Unit tests

All unit tests pass.

Serving tests

The lossless and off modes are bit-identical in generated videos. The channels_last examples will be added soon.

Performance tests

Gathered with this command: python benchmarks/diffusion/bench_wan_vae_encode.py --model nvidia/Cosmos3-Nano --frames 93 --check-reconstruction --json encode-video.json
GB200:

--vae-fast-path Encode time Speedup Output vs. off
off 0.972 s 1.00x reference
lossless 0.551 s 1.76x bit-identical
channels_last 0.406 s 2.39x PSNR 39.77 dB, max abs diff 0.0156

H100:

--vae-fast-path Encode time Speedup Output vs. off
off 2.7278 s 1.00x reference
lossless 2.0839 s 1.31x bit-identical
channels_last 1.7810 s 1.53x PSNR 40.34 dB, max abs diff 0.0156

BEFORE SUBMITTING: read CONTRIBUTING.md and run the precheck-pr skill with the code agent for a self-check against project conventions.
(anything written below this line will be removed by GitHub Actions)

Signed-off-by: Maciej Bala <mbala@nvidia.com>
…ecode

bench_wan_vae_decode.py now runs under torchrun with --vae-patch-parallel-size and --vae-parallel-mode, loading DistributedAutoencoderKLWan, syncing every rank around each timed decode and reporting time, peak memory and exactness from rank 0. Tile-parallel decode already runs the fast path inside every tile, so this needs no decoder changes.

Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: MaciejBalaNV <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
# Conflicts:
#	vllm_omni/diffusion/distributed/autoencoders/autoencoder_kl_wan.py
#	vllm_omni/engine/async_omni_engine.py
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
# Conflicts:
#	tests/diffusion/test_diffusion_config_propagation.py
#	tests/entrypoints/test_serve.py
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
…-opt

# Conflicts:
#	docs/user_guide/diffusion/vae_fast_path.md
#	tests/diffusion/distributed/test_wan_vae_fastpath_install.py
#	tests/diffusion/distributed/test_wan_vae_fastpath_kernels.py
#	tests/diffusion/test_diffusion_config_propagation.py
#	tests/entrypoints/test_serve.py
#	vllm_omni/config/omni_config.py
#	vllm_omni/config/stage_config.py
#	vllm_omni/diffusion/data.py
#	vllm_omni/diffusion/distributed/autoencoders/autoencoder_kl_wan.py
#	vllm_omni/diffusion/distributed/autoencoders/wan_vae_fastpath/__init__.py
#	vllm_omni/diffusion/distributed/autoencoders/wan_vae_fastpath/forwards.py
#	vllm_omni/diffusion/distributed/autoencoders/wan_vae_fastpath/install.py
#	vllm_omni/diffusion/distributed/autoencoders/wan_vae_fastpath/triton_data_movement.py
#	vllm_omni/diffusion/distributed/autoencoders/wan_vae_fastpath/triton_upsample.py
#	vllm_omni/diffusion/registry.py
#	vllm_omni/engine/arg_utils.py
#	vllm_omni/entrypoints/cli/serve.py
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: Maciej Bala <mbala@nvidia.com>
@MaciejBalaNV

Copy link
Copy Markdown
Contributor Author

I also fixed additional edge-case where the Wan VAE encoder in channels_last mode would break the VAE decoder in lossless mode, since the latter would blindly accept the input as a non-channels last format.

Signed-off-by: Maciej Bala <mbala@nvidia.com>
@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (InferMatrix Direct completion gate rejected: invalid or wrong-head finding recheck: 44bc455346f17674; missing finding recheck: 44bc455346f17674).

@vllm-omni-review-bot vllm-omni-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Omni ReviewBot review

PR description

This PR adds an independent Wan 2.2 / Cosmos3 residual VAE encoder fast path beside the existing decoder path, with CLI/config --vae-encode-fast-path (off / lossless / channels_last, default lossless). On supported encoders it installs fused Triton assembly/norm/downsample forwards and an optional chunked encode_frames path; unsupported shapes or architectures keep the Diffusers reference encode. User-visible effect: faster encode by default when the residual patchified Wan encoder is present, with lossless aiming for bit-identical posteriors and channels_last trading a small numeric delta for more speed; a large encode benchmark and unit tests accompany the change.

Change flow

flowchart TD
  A["[EXISTING] Cosmos3 / Wan encode callers"]:::existing --> B["[CHANGED] --vae-encode-fast-path<br/>config + CLI + registry install"]:::changed
  B --> C["[NEW] Encoder fastpath kernels<br/>and encoder_forwards"]:::new
  C --> D["[CHANGED] OmniAutoencoderKLWan._encode<br/>encode_frames when eligible"]:::changed
  D --> E["[EXISTING] Reference encode / tiling fallback"]:::existing
  B --> F["[NEW] bench_wan_vae_encode.py<br/>mode A/B + quality gates"]:::new
  classDef existing fill:#e5e7eb,stroke:#6b7280,color:#111827
  classDef changed fill:#fef3c7,stroke:#d97706,color:#451a03,stroke-width:2px
  classDef new fill:#dcfce7,stroke:#16a34a,color:#052e16,stroke-width:2px
  classDef removed fill:#fee2e2,stroke:#dc2626,color:#450a0a,stroke-width:2px
Loading

CI at e2193b2b4cde (2026-10-01T20:02:18.876595+00:00): required check(s) blocking: buildkite/vllm-omni (missing).

Findings

  • [P2] Non-test diff exceeds the 1000-line budget — benchmarks/diffusion/bench_wan_vae_encode.py:3
    Supplied diff_size is 2270 non-test added lines against a 1000-line budget (19 counted files, 10 exempt). The PR body explains kernels, the independent flag, and the benchmark, but still gives no split plan or concrete exemption (vendored sync, generated code, etc.). Largest contributors: benchmarks/diffusion/bench_wan_vae_encode.py (969), wan_vae_fastpath/forwards.py (285), triton_norm_cache.py (260), triton_downsample.py (205), install.py (164). Please split the benchmark/docs follow-up from the install+kernel change, or record an explicit exemption for the largest file.
    Existing thread: #8067 (comment)

🤖 This review was generated by InferMatrix Copilot, an open-source repo-maintenance agent for PR review, CI debugging and issue triage. Try it on your own repo, and ⭐ star it if it helped!

Copy link
Copy Markdown
Collaborator

Reviewed head e2193b2b4cde077f072daf31067f28bfde1cc2d3 against base c0331048d0e28cb66a18a278ef93e68d4ab1c878.

No additional high-confidence correctness findings from this static pass. I traced CLI/config propagation, independent encoder/decoder installation and rollback, temporal chunk/cache ownership, tiling/slicing fallbacks, the new kernels, and benchmark timing/quality reporting. The current head adds random-operand probes for the earlier degenerate-input/weight verdict cases, runtime checks for global and module hooks, and decoder layout regressions for channels-last encoder outputs.

All 28 changed Python files pass static syntax parsing and the diff passes whitespace checks; reported CI is green. This was a static review of the fork snapshot: CPU tests, the earlier RTX 5090 reproductions, CUDA bitwise parity, distributed tiling, and GB200/H100 speedups were not rerun. The probes/regressions are present, but this review does not establish universal bitwise equivalence from a finite probe or independently verify the performance claims.

@MaciejBalaNV

Copy link
Copy Markdown
Contributor Author

Thanks @0z5a and @hsliuustc0106, can we merge this PR in this case?

Signed-off-by: MaciejBalaNV <mbala@nvidia.com>
@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (InferMatrix Direct completion gate rejected: invalid or wrong-head finding recheck: 44bc455346f17674; missing finding recheck: 44bc455346f17674).

@vllm-omni-review-bot

Copy link
Copy Markdown
Omni ReviewBot attempt record

Review attempt ended as failed (InferMatrix Direct completion gate rejected: invalid or wrong-head finding recheck: 44bc455346f17674; missing finding recheck: 44bc455346f17674).

@vllm-omni-review-bot vllm-omni-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Omni ReviewBot review

Changes since the previous review

  • 1 new inline finding(s); 0 finding(s) below.
  • Still needs attention: Non-test diff exceeds the 1000-line budget — benchmarks/diffusion/bench_wan_vae_encode.py

CI at 77f6f0266e60 (2026-10-07T20:04:34.243786+00:00): required check(s) blocking: buildkite/vllm-omni (missing).

Full review analysis

PR description

This PR adds an independent Wan 2.2 / Cosmos3 residual VAE encoder fast path (off / lossless / channels_last), wired through --vae-encode-fast-path and OmniDiffusionConfig.vae_encode_fast_path (default lossless), separate from the existing decoder flag. Install-time Triton/ATen fusions cover RMSNorm epilogues, causal-conv assembly, norm-to-cache writes, residual bias folding, downsampling assembly, and chunked encode output; channels_last further converts encoder conv weights and uses tolerance-checked math. Untiled encoding can use a preallocated chunk schedule while tiling still rides the patched module forwards, with a dedicated encode benchmark and expanded unit coverage.

Change flow

flowchart TD
  A["[EXISTING] serve CLI / OmniDiffusionConfig"]:::existing
  B["[CHANGED] vae_encode_fast_path projection<br/>omni_config / data / registry"]:::changed
  C["[NEW] install_wan_vae_encoder_fastpath<br/>+ encode_frames"]:::new
  D["[CHANGED] wan_vae_fastpath forwards<br/>+ encoder Triton kernels"]:::changed
  E["[CHANGED] OmniAutoencoderKLWan._encode"]:::changed
  F["[NEW] bench_wan_vae_encode + encoder tests"]:::new
  A --> B --> C --> D --> E
  C --> F
  classDef existing fill:#e5e7eb,stroke:#6b7280,color:#111827
  classDef changed fill:#fef3c7,stroke:#d97706,color:#451a03,stroke-width:2px
  classDef new fill:#dcfce7,stroke:#16a34a,color:#052e16,stroke-width:2px
  classDef removed fill:#fee2e2,stroke:#dc2626,color:#450a0a,stroke-width:2px
Loading

See inline comments below.


🤖 This review was generated by InferMatrix Copilot, an open-source repo-maintenance agent for PR review, CI debugging and issue triage. Try it on your own repo, and ⭐ star it if it helped!

@@ -0,0 +1,969 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Benchmark Cosmos3's Wan encoder against the reference on H100/GB200.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] Non-test diff exceeds the 1000-line budget

Evidence and suggested fix

Supplied diff_size is 2270 non-test added lines against a 1000-line budget (19 counted files, 10 exempt). The PR body explains kernels, the independent flag, and the benchmark, but still gives no split plan or concrete exemption (vendored sync, generated code, etc.). Largest contributors: benchmarks/diffusion/bench_wan_vae_encode.py (969), wan_vae_fastpath/forwards.py (285), triton_norm_cache.py (260), triton_downsample.py (205), install.py (164). Please split the benchmark/docs follow-up from the install+kernel change, or record an explicit exemption for the largest file.

@princepride princepride self-assigned this Oct 8, 2026

@princepride princepride left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@princepride princepride added the ready label to trigger buildkite CI label Oct 11, 2026
@princepride
princepride enabled auto-merge (squash) October 11, 2026 08:42
princepride and others added 2 commits October 11, 2026 09:22
… memory, avoid per-resolution recompiles

- Decoder channels_last RMSNorm keeps vllm-project#7056's fast math. Exact BF16
  roundings are now an encoder-only switch (FastPathConfig.exact_bf16_norm,
  EXACT_BF16 constexpr); the fused norm-cache kernel always uses them.
- Convolution verdicts are decided on seeded random operands only, before
  the real convolution runs once on the selected path. The probe input is
  regenerated per formulation and bits are compared without contiguous
  copies, so the first call for a new shape peaks no higher than main.
- Encoder Triton kernels take spatial sizes and strides at runtime; only
  per-model values (channels, chunk frames, padding, factors) and layout stay
  constexpr. 2D/3D grids keep per-element index math to constant divisions,
  so throughput matches the specialized kernels.
- Update encoder-norm tests to use the encoder config and the verdict tests
  to expect one comparison per new shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: princepride <wangzhipeng628@gmail.com>
… layout

At the channels_last level the encoder runs channels-last and quant_conv's
weights are converted, so latent_dist.parameters (and the channel slices
mode() returns) carried channels-last strides on both the untiled and tiled
paths. Bind quant_conv to a forward that returns a contiguous output, as the
decoder's conv_out already does; already-contiguous lossless outputs are
unchanged. The latent is small, so the copy is negligible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: princepride <wangzhipeng628@gmail.com>
@princepride princepride added ready label to trigger buildkite CI and removed ready label to trigger buildkite CI labels Oct 11, 2026
@vllm-omni-review-bot

vllm-omni-review-bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Omni ReviewBot: superseded

The CI failure noted on a41627d750cc refers to an earlier head; the pull request now points at 8d3f1fe1f115.

princepride and others added 2 commits October 11, 2026 12:48
Signed-off-by: princepride <wangzhipeng628@gmail.com>
…tate

The test's stand-in pipeline lacked ``sampling_dtype``, which
``_encode_conditioning_image_latent`` reads since vllm-project#7592, so it failed with
AttributeError on every platform. Use the pipeline's class attribute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: princepride <wangzhipeng628@gmail.com>
@princepride princepride added ready label to trigger buildkite CI and removed ready label to trigger buildkite CI labels Oct 11, 2026
@princepride
princepride merged commit 6bacbf2 into vllm-project:main Oct 11, 2026
7 of 9 checks passed
khairulkabir1661 pushed a commit to khairulkabir1661/vllm-omni that referenced this pull request Oct 14, 2026
)

Signed-off-by: Maciej Bala <mbala@nvidia.com>
Signed-off-by: MaciejBalaNV <mbala@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request high priority high priority issue, needs to be done asap ready label to trigger buildkite CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants