Skip to content

perf(wan): pad sequence to 128-lane multiple for self-attention (wan_seq_pad="lane") - #491

Open
Perseus14 wants to merge 1 commit into
feat/ulysses-shard-major-a2afrom
feat/wan-fast-inference-optimizations
Open

Perseus14 wants to merge 1 commit into
feat/ulysses-shard-major-a2afrom
feat/wan-fast-inference-optimizations

Conversation

@Perseus14

@Perseus14 Perseus14 commented Sep 25, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on feat/ulysses-shard-major-a2a. Last of four pieces split out of the original #491 (this PR keeps the number). Adds wan_seq_pad: "lane" (default "off"): WanModel pads the video tokens at the tail to a per-shard multiple of 128 (75,600 → 75,776 at 720p/CP=4; per ring segment on the Ulysses-ring path) so the Ulysses reshapes are copy-free, pads the rotary embedding to match, and unpads the output/residual. The padding is declared to the attention layers with self_attention_token_padding (TokenPadding, a ContextVar scoped to the block trace); the custom ulysses / ulysses_ring_custom kernels mask the pad keys via the ragged KV tail, and the fixed-m norm reductions (q_valid_len on both the 1-D Ulysses path and the R == 1 ring path, row_ok for R > 1) exclude pad Q/K/V rows so they cannot push a block onto the online-softmax path or move k_mean. Every configuration that disables padding logs why, once per cause. Behaviour change: with padding active, paths that cannot mask it (flash / non-custom ulysses, ulysses_ring, dot_product, cudnn_flash_te, head_local_svg, ring bidir) raise NotImplementedError instead of silently attending to pads. run_wan_fast_inference.sh sets lane for v6e/v7 and carries the LIBTPU flag changes (ici_ag/ag_backward pipelining off on v6e, on on v7; v7 adds --xla_tpu_enable_offloading_copy_to_sparsecore=false and BQ=4736).

Performance (Wan 2.2 T2V-A14B, 720p / 81 frames / 40 steps, CP=4, DP=2)

Per-PR progression across the 10-PR stack on TPU v6e-8 (DVFS unpinned)

# PR Branch Compile Generate Denoise Step Δ vs prev
0 main (f1d0fafb) origin/main (1D Ulysses U=4 / 2D Ring U=2, R=2) 181.1 s / 67.3 s 147.1 s / 155.0 s 144.6 s / 152.5 s baseline
1 #477 feat/fixed-m-kernel (per_q_block, U=4) 193.5 s 139.3 s 136.6 s −8.0 s denoise vs main
2 #507 feat/attn-dot-fallback-fixes 13.2 s 138.5 s 136.4 s neutral (fallback/grid-search fixes)
3 #478 feat/ring-attention (U=4 / 2D Ring U=2, R=2) 68.0 s / 68.6 s 138.3 s / 138.8 s 136.1 s / 136.6 s −15.9 s on 2D Ring vs main
4 #508 feat/fused-rmsnorm-rope-xla 67.1 s 138.2 s 136.0 s neutral e2e (+ fixes ~1.5 GB eager OOM)
5 #479 feat/wan-fast-serving (cold / warm AOT) 31.0 s / 3.1 s 138.2 s / 138.1 s 136.0 s / 135.9 s −98.4% warm AOT compile, −85% weight load
6 #488 feat/wan-custom-kernels (Pallas fused RoPE ON, cold / warm) 30.7 s / 2.8 s 133.8 s / 133.7 s 131.6 s −4.3 s denoise vs #479 (136.0 s with RoPE off)
7 #509 feat/wan-cross-attn-pallas (cold / warm AOT) 32.8 s / 2.5 s 130.4 s / 130.6 s 128.3 s / 128.4 s −3.2 s denoise vs #488
8 #510 feat/wan-token-patch-embed (cold / warm AOT) 30.3 s / 2.2 s 130.7 s / 130.6 s 128.5 s neutral on v6e 1D Ulysses (−12.8 ms/step on tpu7x 2D Ring)
9 #511 feat/ulysses-shard-major-a2a (wan_ulysses_out_a2a="chunked") 29.6 s 128.9 s 126.7 s −1.8 s denoise vs #510
10a #491 feat/wan-fast-inference-optimizations (wan_seq_pad="lane", WAN_ULYSSES_OUT_A2A=flat ablation) 147.6 s (cold XLA) 129.3 s 126.8 s −1.7 s denoise vs #510 from lane padding alone
10b #491 feat/wan-fast-inference-optimizations (full stack, cold / warm AOT) 31.0 s / 2.2 s 125.6 s / 125.5 s 123.4 s / 123.3 s −3.4 s vs #511 (−21.3 s / −14.7% denoise vs main)

Tests: wan_seq_pad_test.py (18, new; padding plan + disable logging, pad-key masking across ulysses custom / ulysses_ring_custom incl. U=2×R=2, U=4×R=1, and the scaled-down tpu7x U=2×R=2 shard-major path, fixed-m metadata ignoring pad rows, NotImplementedError guards), wan_runtime_options_test.py (+enum validation).

Stack: #477 → #507 → #478 → #508 → #479 → #488 → #509 → #510 → #511 → #491

@Perseus14
Perseus14 requested a review from entrpn as a code owner September 25, 2026 16:26
@github-actions

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces several performance optimizations for Wan models on TPU, including a fused short-KV cross-attention Pallas kernel, token-sharded patch embedding, inverse Ulysses all-to-all optimizations, and token sequence padding. A critical issue was identified in the new Pallas cross-attention kernel where the BlockSpec index mapping fails to scale grid indices by their block sizes, which would result in incorrect memory slicing and wrong outputs.

Comment thread src/maxdiffusion/kernels/cross_attention_pallas.py
@Perseus14
Perseus14 added this pull request to stack #486 September 25, 2026 16:30
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from 2ba8966 to e86f174 Compare September 25, 2026 16:34
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from e86f174 to 1d4c9f6 Compare September 25, 2026 17:20
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 2 times, most recently from 0d91078 to 4703103 Compare September 25, 2026 18:28
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 3 times, most recently from 295c3b5 to 7a60c3d Compare September 25, 2026 22:13
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 2 times, most recently from 08d405c to 7db8313 Compare September 26, 2026 10:31
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from 7db8313 to 6fdc94e Compare September 26, 2026 13:13
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 2 times, most recently from ebaac14 to 58f889e Compare September 26, 2026 14:34
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from 58f889e to 68a580f Compare September 26, 2026 17:07
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from 68a580f to 395f932 Compare September 26, 2026 18:21
@eltsai

eltsai commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

thanks for these stacks of PRs @Perseus14 ! I wonder if we can do an experiment on videos with lengths of 2.5 sec, 5 sec (already covered), 7.5 sec, and 10 sec to see if we would see any regression on other seq length?

@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from cdc584f to 2eba645 Compare October 1, 2026 07:31
@Perseus14 Perseus14 changed the title feat(wan): Wan 2.2 fast inference optimizations (Pallas cross-attention, token patch embed, chunked A2A, lane padding) feat(wan): fast inference optimizations (fused cross-attention, token patch embed, shard-major A2A, lane padding) Oct 1, 2026
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 2 times, most recently from 08ccfb2 to 68c3732 Compare October 1, 2026 10:35
@Perseus14

Copy link
Copy Markdown
Collaborator Author

Thanks @eltsai — ran the sweep at 41 / 81 / 121 / 161 frames (2.5 / 5 / 7.5 / 10 s @ 16 fps), 720p, 40 steps, seed 12345, same prompt, DP=2×CP=4, DVFS unpinned, on tpu7x-8 and v6e-8.

A = main (10af306), B = this stack with the #491 switches off (= #477→#488), C = this stack with the launcher defaults. Denoise seconds for 40 steps (load/compile/VAE excluded):

frames tpu7x A tpu7x B tpu7x C C vs A v6e A v6e B v6e C C vs A
41 56.5 43.0 41.5 1.36× 58.8 53.0 49.6 1.19×
81 123.0 109.1 105.0 1.17× 144.6 132.8 125.9 1.15×
121 234.2 216.2 212.7 1.10× 290.8* 308.1 313.0 / 254.6* 1.14×*
161 385.0 362.1 356.5 1.08× 454.7 430.4 419.2 1.08×

No regression vs main at any length; the gain narrows as attention dominates the step. The #491 switches themselves (B→C) are +1.6–3.9 % on tpu7x and +2.7–6.9 % on v6e, except v6e @ 121 f (−1.6 %).

Note that all runs use the launcher's 81 f-tuned flash block sizes (v6e: BQ 9472 / BKV 1024; tpu7x: BQ 4736 / BKV 2048). We did not do a block-size sweep per length: the sequence length (and therefore the per-shard length and its alignment to BQ) differs for each frame count, so the optimal block sizes need to be found separately for each frame choice. The non-81 f numbers above are therefore conservative for all three configurations.

* v6e @ 121 f is the one length where the launcher's v6e profile (Ulysses U=4, BQ 9472) is the wrong pick: the per-shard length (27,900 tokens) is not block-aligned, and the 2D-ring recipe that is also in this stack (ATTENTION=ulysses_ring_custom_fixed_m ULYSSES_SHARDS=2 BQ=4736 BKV=2048) is 19 % faster there (254.6 s; main with its own ring recipe: 290.8 s, with its Ulysses kernel 657.6 s). At 41 / 81 / 161 f Ulysses remains the better v6e choice by 1–37 %.

All runs were bit-reproducible across cold/warm AOT (identical md5s), and the outputs are visually clean on both platforms.

@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch 2 times, most recently from 0414334 to 34789ff Compare October 2, 2026 17:15
eltsai
eltsai previously approved these changes Oct 2, 2026
@Perseus14
Perseus14 force-pushed the feat/wan-fast-inference-optimizations branch from 34789ff to 87c36b2 Compare October 5, 2026 03:57
@Perseus14
Perseus14 requested a review from eltsai October 5, 2026 16:32
eltsai
eltsai previously approved these changes Oct 5, 2026
syhuang22
syhuang22 previously approved these changes Oct 5, 2026
Last of four pieces split out of the Wan fast-inference optimizations PR
(the cross-attention Pallas kernel, token-sharded patch embedding and
shard-major Ulysses A2A are the three PRs below this one). The switch
defaults off in the Wan YAMLs and in wan_runtime_options; it is a plain
config key plumbed to WanModel through `attention_config_entries(config)`.

- Lane sequence padding (wan_seq_pad="lane"): WanModel pads the video tokens
  at the tail to a per-shard multiple of 128 (75,600 -> 75,776 at
  720p/CP=4; on the Ulysses-ring path each ring segment is padded at its own
  tail) so the Ulysses reshapes are copy-free, pads the rotary embedding to
  match, and unpads the output and residual. The padding is declared to the
  attention layers with `self_attention_token_padding` (a ContextVar scoped
  to the block trace); `TokenPadding` describes it.
- The custom ulysses / ulysses_ring_custom kernels mask the pad keys by
  passing the real length as the ragged KV tail; the fixed-m norm reductions
  (`_compute_fixed_m_metadata` via `q_valid_len` on both the 1-D Ulysses
  path and the R == 1 ring path, `_ring_fixed_m_norms_pre_a2a` via `row_ok`
  for R > 1) exclude the pad Q/K/V rows so
  pad-token values cannot push a block onto the online-softmax path or move
  the k_mean. The ring's `ring_kv_seq_len` is now always the real length.
- Every configuration that disables it logs why, once per cause (TI2V
  per-token timesteps, SVG attention, memory-efficient attention, short
  sequences, unsupported kernels, ulysses_shards that do not divide the
  context axis, indivisible sequences).
- Behaviour change: if padding is active, the paths that cannot mask it
  (flash and non-custom ulysses via _tpu_flash_attention, ulysses_ring,
  dot_product, cudnn_flash_te, head_local_svg, ulysses_ring_custom_bidir)
  raise NotImplementedError via `_reject_active_token_padding` instead of
  silently attending to pads.
- wan_block_benchmark.py applies the same padding plan to the benchmarked
  sequence length.
- run_wan_fast_inference.sh: the v6e and v7 profiles set WAN_SEQ_PAD=lane
  (overridable); the generic profile keeps off. LIBTPU: v6e now disables
  --xla_tpu_enable_ici_ag_pipelining / --xla_tpu_enable_ag_backward_pipelining,
  v7 enables them and adds --xla_tpu_enable_offloading_copy_to_sparsecore=false
  and BQ=4736. These two flags moved out of COMMON_LIBTPU, so the generic
  (non-v6e/v7) profile no longer sets them at all. The launcher's
  reference-benchmark comment carries the stack-tip numbers below.

Performance, measured at the stack tip (Wan 2.2 T2V-A14B, 720p/81f/40 steps,
warm AOT, DVFS unpinned unless noted; CP=4, DP=2, launcher defaults including
the fused RoPE kernel):
  v6e-8:   denoise 125.9s, generate 128.05s
  tpu7x-8: denoise 105.1s, generate 107.6s
  tpu7x-8, DVFS pinned (PIN_DVFS_P_STATE=true), fused RoPE kernel off:
           denoise 93.6s, generate 96.1s (pinned with it on was not measured)
Per-feature deltas were not measured separately and are omitted.

Tests
- wan_seq_pad_test.py (18): padding plan and per-cause disable logging,
  pad-key masking for ulysses custom (unaligned and aligned real_len, flat and
  shard-major out A2A, every fixed-m mode) and ulysses_ring_custom (U=2 x R=2
  with k-centring on/off; U=4 x R=1 with k-centring on/off, asserting through
  a spy that the R == 1 metadata call receives q_valid_len=real_len; and the
  tpu7x production path scaled down: U=2 x R=2, aligned real_len, chunked
  out A2A, asserting the shard-major layout was taken; all need >= 4
  devices), fixed-m metadata ignoring pad query rows, and the
  NotImplementedError guards.
- wan_runtime_options_test.py (12): the new switch's YAML default and enum
  validation; `wan_seq_pad` is excluded from the FlaxWanAttention attribute
  check since WanModel consumes it.
- CI: the five multi-device masking / end-to-end tests in wan_seq_pad_test skip in GitHub
  Actions; padding-plan logic and runtime options run in CI.
- end_to_end/tpu/run_wan_stack_tests.sh: adds wan_seq_pad_test.py.
Verified (final tree): TPU v6e-8 (jax 0.11.2, end_to_end/tpu/run_wan_stack_tests.sh):
356 passed, 1 skipped (off-TPU fallback only), 282 subtests passed.
wan_seq_pad_test.py run directly: 18 tests OK (all 5 multi-device tests ran).
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.

3 participants