Skip to content

feat(wan): fused RMSNorm+RoPE Pallas producer; Wan switches in config, not env - #488

Merged
copybara-service[bot] merged 1 commit into
mainfrom
feat/wan-custom-kernels
Oct 26, 2026
Merged

copybara-service[bot] merged 1 commit into
mainfrom
feat/wan-custom-kernels

Conversation

@Perseus14

@Perseus14 Perseus14 commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds a fused RMSNorm + RoPE + head-transpose Pallas producer for Wan self-attention Q/K, and moves Wan's graph-changing switches out of environment variables into config.

What's in it

  • Pallas producer (kernels/fused_rmsnorm_rope_pallas.py): computes FP32 RMSNorm + RoPE + the [B,S,H*D] → [B,H,S,D] transpose, with optional log2(e) / 1/√d prescale folding.
    • norm_mode="exact" is 0 ULP against the separately jitted XLA producer (fused_rmsnorm_rope from feat/fused-rmsnorm-rope-xla).
    • In isolation on v6e it is 1.86×–2.06× faster than XLA (0.964 vs 1.789 ms at seq 9450; 1.953 vs 4.018 ms at seq 18900).
  • Defaults:
  • Config, not env:
    • wan_rope_norm_mode, wan_fuse_qk_prescale, wan_splash_transpose_out, wan_cfg_before_unpatchify, wan_cross_attn_prescale_kv and wan_rope_accum are yml keys passed via attention_config_entries(config) across WanPipeline, VACE, Animate, and wan_block_benchmark.
    • No process-global store and no trace-time reads. Config-less module builds use built-in defaults.
  • wan_cfg_before_unpatchify (on by default): applies CFG before unpatchify. A compiled parity test shows bit-identical output in f32 everywhere and in bf16 on CPU/v6e, and within 1 bf16 ULP on tpu7x.
  • VMEM: 64 MiB is requested only when every mesh TPU is v6e or tpu7x; other TPUs get Mosaic's default.

Tests

fused_rmsnorm_rope_pallas_test.py (38 test methods / 60 parameterized cases), wan_runtime_options_test.py (11), plus updates to dot_fallback_layout_test.py (12) and aot_cache_test.py (45).

  • TPU tests relying on 64 MiB VMEM or measured rope_accum skip on unvalidated TPUs (vmem_limit_is_validated, rope_accum_is_measured).
  • CI vs Local VM: 48 additional kernel-grid/CFG tests skip under GITHUB_ACTIONS=true (88 of 283 cumulative); guards, VMEM resolver, runtime options, AOT cache, and dot-fallback layout tests run in CI.
  • Results (TPU v6e-8): 283 passed cumulative (run_wan_stack_tests.sh, 575.2 s).

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

@Perseus14
Perseus14 requested a review from entrpn as a code owner September 22, 2026 20:09
@github-actions

Copy link
Copy Markdown

@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from dbd6ace to 7a6e0e6 Compare September 22, 2026 20:10
@Perseus14
Perseus14 added this pull request to stack #486 September 22, 2026 20:11

@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 a fused RMSNorm + RoPE + head-transposition Pallas kernel for TPU, integrates it into the Flax attention pipeline, and implements several optimizations such as in-kernel VMEM transpose and Classifier-Free Guidance (CFG) before unpatchify. Feedback on these changes highlights critical runtime issues, including an AttributeError from using jax.shard_map instead of jax.experimental.shard_map.shard_map and a potential NameError due to an undefined _warn_once helper. Additionally, the reviewer recommends adding defensive checks to validate the sequence length of freqs_cis and removing redundant JAX compatibility shims that are already handled in the package initialization.

Comment thread src/maxdiffusion/models/attention_flax.py Outdated
Comment thread src/maxdiffusion/tests/fused_rmsnorm_rope_pallas_test.py Outdated
Comment thread src/maxdiffusion/models/attention_flax.py Outdated
Comment thread src/maxdiffusion/kernels/fused_rmsnorm_rope_pallas.py
Comment thread src/maxdiffusion/generate_wan.py Outdated
Comment thread src/maxdiffusion/tests/fused_rmsnorm_rope_pallas_test.py Outdated
Comment thread src/maxdiffusion/tests/aot_cache_test.py Outdated
Comment thread src/maxdiffusion/kernels/fused_rmsnorm_rope_pallas.py Outdated
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 7740dcf to 5c0f8bb Compare September 24, 2026 07:00
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch 2 times, most recently from 3deb73c to 94d7dc8 Compare September 24, 2026 07:50
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 5c0f8bb to aee8c4a Compare September 24, 2026 07:50
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 94d7dc8 to 33187c0 Compare September 24, 2026 09:40
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch 2 times, most recently from 2f8b92d to a507d0d Compare September 24, 2026 17:13
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 33187c0 to 6d667bc Compare September 24, 2026 17:13
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 1f36266 to 91d0c00 Compare September 26, 2026 23:03
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 96dd50f to 359249b Compare September 26, 2026 23:03
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 91d0c00 to c458152 Compare September 26, 2026 23:19
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch 2 times, most recently from 593e337 to 4ebc6fd Compare September 27, 2026 09:50
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from c458152 to 7730d38 Compare September 27, 2026 09:50
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 4ebc6fd to 5a1fa32 Compare September 27, 2026 14:59
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch 2 times, most recently from 609f32e to e054d85 Compare September 27, 2026 16:00
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch 2 times, most recently from a75047a to 0b1315d Compare September 27, 2026 16:25
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch 2 times, most recently from add2d8d to 65d75c0 Compare September 28, 2026 16:32
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 0b1315d to 77afa86 Compare September 28, 2026 16:32
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 65d75c0 to 1788230 Compare September 28, 2026 16:50
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 77afa86 to 2e70bc8 Compare September 28, 2026 16:50
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from 1788230 to fdb5230 Compare September 29, 2026 19:22
@Perseus14
Perseus14 force-pushed the feat/wan-custom-kernels branch from 2e70bc8 to 973594c Compare September 29, 2026 19:22
@Perseus14
Perseus14 force-pushed the feat/wan-fast-serving branch from fdb5230 to f9febc3 Compare September 29, 2026 19:29
eltsai
eltsai previously approved these changes Oct 5, 2026
mbohlool
mbohlool previously approved these changes Oct 19, 2026

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

Approving once the inline items are addressed; the Pallas producer matches the unfused reference op for op, and tiling, tails and VMEM look right.

Comment thread src/maxdiffusion/wan_runtime_options.py Outdated
Comment thread end_to_end/tpu/run_wan_fast_inference.sh Outdated
Comment thread src/maxdiffusion/tests/fused_rmsnorm_rope_pallas_test.py
Comment thread src/maxdiffusion/models/attention_flax.py
Comment thread src/maxdiffusion/models/attention_flax.py Outdated
Comment thread src/maxdiffusion/models/attention_flax.py
prishajain1
prishajain1 previously approved these changes Oct 26, 2026
…, not env

Fused producer (kernels/fused_rmsnorm_rope_pallas.py)
- A Pallas kernel computing FP32 RMSNorm + RoPE + the [B,S,H*D] -> [B,H,S,D]
  head transpose for self-attention Q/K. It optionally folds log2(e) into Q
  and 1/sqrt(d) into K.
  - norm_mode="exact" leaves the feature reduction to XLA. It is 0 ULP
    against the separately jitted XLA producer (`fused_rmsnorm_rope`).
  - norm_mode="fused" does the reduction in-kernel. It stays within 2x
    pair-relative bf16 eps.
  - rope_accum ("dtype" | "f32") matches how the local compiler rounds the
    RoPE multiply-add. "auto" uses the per-platform measured mode: tpu7x
    "dtype", v6e "f32".
- Off by default in the YAML (`use_fused_rope_kernel: False`) and in the
  launcher's generic profile. Once inlined into the 40-layer graph, XLA rounds
  the unfused producer differently, so the output is equivalent but not
  identical (720p/81f, 40 steps: pre-decode bf16 latent PSNR is 53.8 dB after
  1 step and 34.1 dB after 40 steps on v6e seed 12345; post-VAE decoded uint8
  video PSNR / SSIM with peak=255 at stack tip is 25.71 dB / 0.9125 on v6e
  seed 777, 17.92 dB / 0.6556 on v6e seed 12345, and 17.69 dB / 0.6918 on
  tpu7x seed 12345).
- Fails closed (`ValueError`) when `use_fused_rope_kernel=True` is explicitly
  requested on non-TPU meshes, unmeasured TPU generations, sharded feature
  axes, `dim_head % 128 != 0`, or uneven sequence splits.
- `run_wan_fast_inference.sh` turns it ON in the v6e and v7 profiles. Measured
  at the stack tip (Wan 2.2 T2V-A14B, 720p/81f/40 steps, warm AOT, CP=4, DP=2),
  kernel on vs off:
    v6e-8 (DVFS unpinned):   denoise 124.5s - 125.0s (generate 126.6s - 127.5s) vs 131.2s off
    tpu7x-8 (DVFS unpinned): denoise 104.9s (generate 107.8s) vs 106.6s (generate 109.6s) off
    tpu7x-8 (DVFS pinned):   denoise  91.7s (generate  94.6s) vs  93.4s (generate  96.4s) off
- Isolated speed, one v6e chip, jax 0.11.2, 40 heads, d=128, bf16, exact
  mode, block_s=1024, median of 50 runs: per-shard seq 9450 takes 0.964 ms
  vs 1.789 ms for XLA (1.86x); seq 18900 takes 1.953 ms vs 4.018 ms (2.06x).
- VMEM budget: 64 MiB is requested only when every TPU in the mesh is a v6e or
  tpu7x (the validated generations).

Wan graph-changing switches
- `wan_rope_norm_mode`, `wan_fuse_qk_prescale`, `wan_splash_transpose_out`,
  `wan_cfg_before_unpatchify`, `wan_cross_attn_prescale_kv` and `wan_rope_accum`
  are pure YAML/config keys declared in every `base_wan*.yml` with the code
  default and resolved at model-build time via `from_config` /
  `attention_config_entries(config)` (no environment-variable fallback or
  process-global store). Every value lives on the GraphDef and in the AOT key.
- `wan_cfg_before_unpatchify` (default True) applies classifier-free guidance
  on packed tokens before unpatchify instead of after unpatchify.
- splash `transpose_out`: an in-kernel output layout [H,S,D] instead of
  [H,D,S], for the custom splash kernel and the non-ring Ulysses wrapper. Off
  by default.

Other changes
- `split_head_dim` is plumbed from the config into `WanModel` and its
  attention layers (`False` in the six `base_wan*.yml` files).
- `_apply_attention_dot`: handles prescaled Q/K (`qk_prescaled` /
  `k_prescaled` / `use_base2_exp`) and refuses prescaled Q/K for unsupported
  kernels and head-local SVG attention.

Tests
- `fused_rmsnorm_rope_pallas_test.py`: parity grid, fail-closed guards,
  backward, VMEM resolver, `FlaxWanAttention` sequence-sharded integration, and
  `transpose_out` tests; runs in GitHub Actions on CPU and unvalidated TPUs via `interpret=not _on_validated_tpu()`; bit-exact (ULP-pinned) assertions skip on unvalidated TPUs because interpret mode on a TPU backend is not rounding-faithful (XLA excess precision elides the kernel's bf16 round trips; reproduced on v6e and documented in the test module header).
- `dot_fallback_layout_test.py`, `wan_runtime_options_test.py`, `aot_cache_test.py`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants