Skip to content

Commit b33e35e

Browse files
committed
feat(wan): fused RMSNorm+RoPE+transpose Pallas producer kernel, Fixed-M optimizations, and serving stabilization
- Add fused_rmsnorm_rope_pallas custom Pallas TPU kernel (src/maxdiffusion/kernels/fused_rmsnorm_rope_pallas.py) combining fp32 RMSNorm scaling, complex rotary position embeddings, in-register Q/K prescaling (q * LOG2E, k * scale), and [B, S, H*D] -> [B, H, S, D] head transpose in a single VMEM pass. - Add TPU-only platform guard in _fused_rope_producer and prevent cuDNN Flash Attention (cudnn_flash_te) from double-scaling prescaled cached keys. - Eliminate two 387 MB standalone HBM multiply+copy round-trips per self-attention layer (774 MB/layer, 2.48 TB over 40 steps) prior to Ulysses/Ring all-to-all, worth 1.7% (tpu7x) to 3.1% (v6e) of denoise time end to end. The kernel is 0 ULP against a compiled XLA reference in isolation; inlined in the full model it is arithmetically equivalent but not bit-identical (34.1 dB PSNR over a 40-step denoise on v6e). - Enable in-kernel VMEM transpose in custom splash attention (custom_splash_attention.py) to streamline output relayout across Ulysses sequences. - Optimize cross-attention projection handling with split_head_dim in WanModel to eliminate unnecessary head transpose barriers. - Update run_wan_fast_inference.sh with tuned latency-hiding scheduler options, p_state=7 on v7x, environment variable overrides, and optimized collective overlapping. - Add JAX >= 0.11.0 compatibility shim in __init__.py and generate_wan.py for Flax NNX HiPrimitive deprecation. - Add comprehensive test suites: fused_rmsnorm_rope_pallas_test.py (including production JIT + shard_map + f32 RoPE + prescale parity, finite/NaN assertions, and true integer ULP metrics), dot_fallback_layout_test.py, and aot_cache_test.py. Key the AOT cache on the resolved RoPE accumulation mode. WAN_ROPE_ACCUM changes the lowered graph's rounding but was not part of the Wan AOT metadata fingerprint, so flipping it could silently reload an executable compiled under the other rounding mode. Both settings produced identical metadata. The underlying problem was a duplicated rule: attention_flax.py computed the platform default inline, and generate_wan.py did not model it at all. Adding a second copy to the fingerprint would have reintroduced the same class of bug, so the rule now lives in one place, resolve_rope_accum(mesh), called by both the production call site and _build_wan_aot_metadata. The fingerprint records the RESOLVED mode rather than the raw env var. Recording the raw value would both over-invalidate (an explicit "f32" on v6e describes the same executable as the unset default) and fail to describe what was actually compiled. An unrecognised value now raises instead of being passed through. Also closes a gap in parity coverage. The prescaled parity test pinned rope_accum="f32" and checked bit-equality against another Pallas result, so the combination that actually ships on v7 -- prescale ON, rope_accum="dtype", and the production 1024 tile -- had no 0-ULP assertion anywhere; a regression in it could only have surfaced as a quality drop in a 40-step denoise. The added test resolves the mode the same way production does, uses fused_rope_block_s / fused_rope_head_block instead of the kernel defaults, and compares against an XLA reference whose prescale is applied inside the same jit. Verified on tpu7x-8 and v6e-8: * 16/16 aot_cache_test, 4/4 production-shape parity tests on both. * The shipped config is 0 ULP against fused-in-jit, barrier-staged and post-hoc XLA references alike (0/774,144,000 elements differ), so the strict bound is measured, not assumed. * Negative control: forcing WAN_ROPE_ACCUM=f32 on tpu7x fails the new test with 26.4% of elements differing, confirming it discriminates. * Through the real AOT cache at a fixed commit, the two modes key different executables and reverting reloads the original rather than recompiling; the two modes produce different output, which is what made the missing key a wrong-answer bug rather than a hygiene issue. * E2E unchanged by this cache-keying change: denoise 96.7s (v7x) and 129.6s (v6e), with byte-identical output video at a fixed seed for a given resolved rope_accum mode. Compare the kernel against a compiled reference, on both sides. The new parity tests compared an eagerly-dispatched XLA reference against the compiled kernel. That is not a comparison of the kernel: eager dispatch evaluates each op separately, while XLA under jit contracts the RoPE multiply-add into an FMA that rounds once. Measured on v6e at seq=50, the eager and jitted references disagree on 30.6% of elements, and the kernel matches whichever one shares its rounding -- exactly, with nothing in between. The pairing happened to be self-consistent on v6e and tpu7x and was not on the v4-8 CI runner, where it failed with 35.3% of elements differing. References now go through _compiled_reference and the kernel through _compiled_kernel, with the mode from resolve_rope_accum(). One test keeps the eager pairing deliberately: under an identity rotation both modes denote the same arithmetic, which is the point of that test. Compiling only the reference was not enough. norm_mode="exact" leaves the fp32 feature-axis reduction in XLA, so the kernel has an XLA prologue of its own, and eager and jitted reductions diverge at full size: 665/96,768,000 outputs on tpu7x and 503/96,768,000 on v6e, max |diff| 3.125e-02. Small-shape tests cannot see it. Also fixes resolve_rope_accum off TPU. It assumed every non-tpu7x platform contracts into fp32 FMAs, but XLA:CPU emits the multiply and the add separately; measured, the eager and jitted references agree to 0 ULP on CPU and only "dtype" matches either. The f32 default is now gated on the platform actually being a TPU, which is what makes the interpret-mode tests meaningful off TPU. Verified: 34/34 kernel tests and 16/16 aot_cache_test on CPU (interpret), tpu7x-8 (resolves to "dtype") and v6e-8 (resolves to "f32"). Negative control: forcing WAN_ROPE_ACCUM=dtype on v6e fails 7 tests, including every converted one. On a parity failure the assertion now prints the reference-dispatch x accumulation-mode grid. A bare "31% of elements differ" cannot distinguish a real algebra bug from the kernel being built against the wrong rounding convention for the platform, and those want opposite fixes. If some cell is 0 ULP the kernel is correct and only the resolver is wrong; if none is, the arithmetic itself disagrees with the producer. That question has to be answerable from a CI log on a TPU generation that is not available locally. Scope the bit-exactness claim to the platforms where it was measured. The v4-8 CI runner reproduces the producer under no accumulation mode at all. Its grid, at seq=50: ref:eager vs kernel/dtype 35.30% ref:jit vs kernel/dtype 20.88% ref:eager vs kernel/f32 39.76% ref:jit vs kernel/f32 30.96% The kernel is not wrong there. The normalisation is still bit-exact, the drift is confined to the RoPE combine, and it is at most one bf16 ULP (max |diff| 3.125e-02, exactly ulp(bf16) at that magnitude); v4 rounds `a*cos + b*sin` in a way neither mode models. Note also that the kernel is dispatch-invariant on v4 -- the eager and jit columns are identical -- so this is not reachable by another resolver tweak. Bit-exactness is therefore a per-platform property, and `rope_accum_is_measured` now says where it holds: tpu7x ("dtype"), v6 ("f32") and XLA:CPU ("dtype"), which covers both platforms Wan serves on. Tests asserting 0 ULP skip elsewhere rather than assert something untrue. Tests with a non-zero ULP budget still run everywhere, so v4 keeps real coverage of the algebra. An explicit WAN_ROPE_ACCUM counts as measured, so overriding it does not quietly soften the negative control. Verified after the change: 34/34 kernel and 16/16 aot_cache_test on tpu7x-8 and v6e-8, with nothing skipped on either -- the guard does not remove coverage on the platforms that matter -- and 50/50 on CPU. Remove the last inline copy of the platform rule. The shard_map production parity test re-derived "dtype on tpu7x, f32 everywhere else" locally instead of calling the resolver, so it kept asserting 0 ULP on v4 after the shared rule had learned better. It now calls resolve_rope_accum(mesh) and is gated the same way.
1 parent 10e0f42 commit b33e35e

18 files changed

Lines changed: 2620 additions & 204 deletions

‎benchmarks/vbench/run_tpu_generation.sh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ WAN_OVERRIDES=(
5757
"use_batched_text_encoder|USE_BATCHED_TEXT_ENCODER|true"
5858
"flash_block_sizes|FLASH_BLOCK_SIZES|"
5959
"prompt_file|PROMPT_FILE|./benchmarks/vbench/prompts_110.txt"
60+
# Matches the shipped config default; set USE_FUSED_ROPE_KERNEL=true to test it.
61+
"use_fused_rope_kernel|USE_FUSED_ROPE_KERNEL|false"
6062
)
6163

6264
usage() {
@@ -70,6 +72,9 @@ Common options:
7072
RUN_NAME Generation run name (default: wan-inference; videos are saved to <RUN_NAME>/videos)
7173
PROMPT_FILE Prompt file path (default: ./benchmarks/vbench/prompts_110.txt)
7274
CONFIG_FILE WAN config file (default: src/maxdiffusion/configs/base_wan_27b.yml)
75+
USE_FUSED_ROPE_KERNEL
76+
Enable the fused RMSNorm+RoPE Pallas producer (default: false).
77+
Only measured on TPU v6e and v7; the model refuses it elsewhere.
7378
EXTERNAL_DISK Mounted disk root for large local files (default: /mnt/disks/external_disk)
7479
HF_CACHE_ROOT Hugging Face cache root (default: \$EXTERNAL_DISK/hf_cache)
7580
HF_HOME Hugging Face home directory (default: \$HF_CACHE_ROOT)

‎end_to_end/tpu/run_wan_fast_inference.sh‎

Lines changed: 15 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,10 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16-
# WAN T2V fast-serving example: AOT executable cache + converted-weights
17-
# cache + zero-exec warmup, with a tuned 2D-ring attention recipe.
18-
#
19-
# The XLA flag set, attention tile and text-encoder options differ per TPU
20-
# generation, so the platform is auto-detected from the GCE metadata server
21-
# and the matching recipe is selected (see "TPU platform detection" below).
22-
# Supported profiles: v6e (Trillium) and v7. Anything else falls back to a
23-
# conservative generic profile.
24-
#
25-
# First run per (model, shape) pays one-time conversion + compile and
26-
# populates the caches; every later process start is ~25s to ready.
27-
#
28-
# Usage:
29-
# ./run_wan_fast_inference.sh [21|22] [steps] ["prompt..."]
30-
# Env overrides:
31-
# WAN_CACHE_ROOT cache root (default ~/.cache/maxdiffusion_wan)
32-
# OUTPUT_DIR video/metrics output (default /tmp/wan_out)
33-
# COMPILE_TE=true torch.compile the text encoder (adds ~30s to load,
34-
# saves ~10s/encode; worth it for long-lived processes)
35-
# EXTRA_LIBTPU extra libtpu flags, appended to the tuned set
36-
# TPU_PROFILE force a platform profile (v6e|v7|generic), skipping
37-
# autodetection
38-
# ACCEL_TYPE force the raw accelerator type (e.g. v6e-8)
39-
# ATTENTION / ULYSSES_SHARDS / BQ / BKV / BQ_DKV / VMEM_LIMIT_BYTES
40-
# override the per-platform attention recipe
41-
# DP / CP override the derived ici_data/context_parallelism
16+
# WAN T2V fast-serving launcher (auto-detects TPU v6e / v7 profiles).
17+
# Usage: ./run_wan_fast_inference.sh [21|22] [steps] ["prompt..."]
18+
# Overrides: WAN_CACHE_ROOT, OUTPUT_DIR, TPU_PROFILE (v6e|v7|generic),
19+
# COMPILE_TE, ATTENTION, ULYSSES_SHARDS, BQ, BKV, DP, CP, EXTRA_LIBTPU
4220
set -u
4321
MODEL=${1:-22}
4422
STEPS=${2:-40}
@@ -52,8 +30,6 @@ export HF_HUB_ENABLE_HF_TRANSFER=1
5230
export JAX_DEFAULT_MATMUL_PRECISION=bfloat16
5331
export TORCHINDUCTOR_FX_GRAPH_CACHE=1
5432
export XLA_PYTHON_CLIENT_MEM_FRACTION=0.95
55-
# Without these the JAX persistent cache silently skips most entries, so the
56-
# "warm" start still recompiles a large part of the graph.
5733
export JAX_PERSISTENT_CACHE_MIN_ENTRY_SIZE_BYTES=-1
5834
export JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS=0
5935

@@ -64,14 +40,7 @@ export TORCHINDUCTOR_CACHE_DIR=${TORCHINDUCTOR_CACHE_DIR:-$CACHE_ROOT/torch_comp
6440
mkdir -p "$CACHE_ROOT/jax" "$CACHE_ROOT/aot_wan$MODEL" "$CACHE_ROOT/converted" \
6541
"$OUTPUT_DIR" "$TMPDIR" "$TORCHINDUCTOR_CACHE_DIR"
6642

67-
# ---------------------------------------------------------------------------
68-
# TPU platform detection
69-
# ---------------------------------------------------------------------------
70-
# Preference order: explicit override -> TPU_ACCELERATOR_TYPE env (set by some
71-
# runtimes) -> GCE metadata "accelerator-type" (e.g. "v6e-8") -> the
72-
# ACCELERATOR_TYPE line inside the "tpu-env" metadata blob. Detection is pure
73-
# metadata/env: it must not initialise the TPU, or it would take the device
74-
# before the real process starts.
43+
# Detect TPU generation via env or GCE metadata without initializing the device.
7544
_tpu_metadata() {
7645
curl -s -m 2 -H 'Metadata-Flavor: Google' \
7746
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/$1" 2> /dev/null
@@ -81,8 +50,6 @@ _detect_accel_type() {
8150
local t="${TPU_ACCELERATOR_TYPE:-}"
8251
[ -z "$t" ] && t="$(_tpu_metadata accelerator-type)"
8352
[ -z "$t" ] && t="$(_tpu_metadata tpu-env | sed -n "s/^ACCELERATOR_TYPE: *'\([^']*\)'.*/\1/p")"
84-
# Guard against metadata returning an HTML error page. Real names seen in the
85-
# wild: "v6e-8", "v5litepod-8", "tpu7x-8" (v7 reports as tpu7x, not v7x).
8653
case "$t" in
8754
v[0-9]* | tpu[0-9]*) printf '%s' "$t" ;;
8855
*) printf '' ;;
@@ -91,8 +58,8 @@ _detect_accel_type() {
9158

9259
ACCEL_TYPE=${ACCEL_TYPE:-$(_detect_accel_type)}
9360
if [ -n "$ACCEL_TYPE" ]; then
94-
TPU_GEN="${ACCEL_TYPE%%-*}" # v6e-8 -> v6e
95-
TPU_CHIPS="${ACCEL_TYPE##*-}" # v6e-8 -> 8
61+
TPU_GEN="${ACCEL_TYPE%%-*}"
62+
TPU_CHIPS="${ACCEL_TYPE##*-}"
9663
else
9764
TPU_GEN=""
9865
TPU_CHIPS=""
@@ -109,59 +76,14 @@ if [ -z "${TPU_PROFILE:-}" ]; then
10976
esac
11077
fi
11178

112-
# ---------------------------------------------------------------------------
113-
# Per-platform XLA flags
114-
# ---------------------------------------------------------------------------
115-
# One line per variable: libtpu tokenises LIBTPU_INIT_ARGS on whitespace and
116-
# stops parsing at the first literal backslash, so a multi-line continuation
117-
# inside a *single-quoted* string silently drops every flag after the first
118-
# line break. Keep these as single-line strings.
119-
COMMON_LIBTPU="--xla_tpu_spmd_rng_bit_generator_unsafe=true --xla_tpu_enable_async_collective_fusion_fuse_all_gather=true --xla_tpu_enable_async_collective_fusion_multiple_steps=true --xla_tpu_memory_bound_loop_optimizer_options=enabled:true --xla_tpu_enable_dot_strength_reduction=true --xla_enable_async_collective_permute=true --xla_tpu_enable_data_parallel_all_reduce_opt=true --xla_tpu_data_parallel_opt_different_sized_ops=true --xla_tpu_overlap_compute_collective_tc=true --xla_enable_async_all_gather=true --xla_tpu_scoped_vmem_limit_kib=65536 --xla_tpu_enable_async_all_to_all=true --xla_tpu_enable_all_experimental_scheduler_features=true --xla_tpu_enable_scheduler_memory_pressure_tracking=true --xla_tpu_host_transfer_overlap_limit=24 --xla_tpu_aggressive_opt_barrier_removal=ENABLED --xla_lhs_prioritize_async_depth_over_stall=ENABLED --xla_should_allow_loop_variant_parameter_in_chain=ENABLED --xla_should_add_loop_invariant_op_in_chain=ENABLED --xla_tpu_enable_ici_ag_pipelining=true --xla_max_concurrent_host_send_recv=100 --xla_tpu_scheduler_percent_shared_memory_limit=100 --xla_latency_hiding_scheduler_rerun=2 --xla_tpu_use_minor_sharding_for_major_trivial_input=true --xla_tpu_relayout_group_size_threshold_for_reduce_scatter=1 --xla_tpu_enable_latency_hiding_scheduler=true --xla_tpu_enable_ag_backward_pipelining=true --xla_tpu_use_single_sparse_core_for_all_gather_offload=true --xla_tpu_sparse_core_all_gather_latency_multiplier=1 --xla_tpu_sparse_core_reduce_scatter_latency_multiplier=3 --xla_tpu_enable_sparse_core_collective_aggregator=true --xla_tpu_enable_sparse_core_offload_queuing_in_lhs=true --xla_tpu_enable_sparse_core_reduce_scatter_v2=true --xla_tpu_enable_sparse_core_collective_offload_all_gather=true --xla_tpu_enable_sparse_core_collective_offload_2d_all_gather=true --xla_tpu_enable_sparse_core_collective_offload_all_reduce=true --xla_tpu_enable_sparse_core_collective_offload_reduce_scatter=true --xla_tpu_enable_sparse_core_collective_offload_3d_all_gather=true --xla_tpu_enable_concurrent_sparse_core_offloading=true --xla_tpu_assign_all_reduce_scatter_layout=true"
120-
121-
# v6e (Trillium). xla_tpu_enable_async_collective_fusion MUST stay false here:
122-
# continuation fusion for AllGather is a Viperlite-only path and aborts backend
123-
# init on v6e with
124-
# FAILED_PRECONDITION: Continuation fusion for AllGather is enabled on
125-
# platform ghostlite ... Please disable it with
126-
# xla_tpu_enable_async_collective_fusion=false
127-
# The two sub-flags are inert while the parent is false; they are kept so the
128-
# set stays a one-line edit away from a platform that does support it.
129-
# Note xla_tpu_memory_bound_loop_optimizer_options is proto-valued, so
130-
# "enabled:true" is its correct syntax -- unlike the bare enum flags above,
131-
# which only accept true/enabled/ENABLED.
132-
V6E_LIBTPU="--xla_tpu_enable_async_collective_fusion=false"
133-
134-
# v7 has two TensorCores per chip, so megacore fusion applies; v6e has one and
135-
# ignores these.
136-
V7_LIBTPU="--xla_tpu_enable_async_collective_fusion=true --xla_tpu_enable_async_collective_fusion_fuse_all_gather=true --xla_tpu_enable_async_collective_fusion_multiple_steps=true --xla_tpu_enable_megacore_fusion=true --xla_tpu_megacore_fusion_allow_ags=true"
137-
138-
# ---------------------------------------------------------------------------
139-
# Attention recipe: measurements behind the per-platform defaults
140-
# ---------------------------------------------------------------------------
141-
# The kernel, Ulysses degree and tile are set per platform in the profile case
142-
# below:
143-
# - v6e profile: ulysses_custom_fixed_m_per_q_block, U=4 (R=1 on CP=4), BQ=9472, BKV=1024
144-
# - v7 profile: ulysses_ring_custom_fixed_m_per_q_block, U=2 (R=2 on CP=4), BQ=6400, BKV=2048
145-
#
146-
# Cloud TPU v6e-8 (Wan 2.2 T2V-A14B, 720p, 81f, 40 steps, CP=4, DP=2):
147-
# this script, single prompt, end to end: 138.7s (denoise 136.0s, VAE 0.8s)
148-
# origin/main @ a4ff3aec, single prompt: 156.4s (denoise 153.3s, VAE 2.9s)
149-
# Denoise improvement: -17.3s (-11.3%); VAE decode: 3.6x faster (0.8s vs 2.9s).
150-
# Outputs are not bit-identical to the baseline (fixed-m reorders float
151-
# accumulation): mean pairwise PSNR +3.25 dB vs exact online softmax, all VBench dimensions within 0.25%.
152-
#
153-
# Cloud TPU v7 (tpu7x-8, Wan 2.2 T2V-A14B, 720p, 81f, 40 steps, CP=4, DP=2):
154-
# this script (profile v7, U=2, R=2, BQ=6400, BKV=2048): 115.2s (denoise 114.4s, VAE 0.8s)
155-
# origin/main @ 1bc54811, single prompt: 119.8s (denoise 119.0s, VAE 0.8s)
156-
# Total inference beats origin/main by -4.6s (-3.9%); xprof collective-permute-done: 2405ms -> 47ms.
157-
# persistent AOT: compile 94.7s cold -> 15.0s warm; 100% bit-identical video output vs unpadded reference.
158-
# vae_decode_chunk: 1 -> 0.8s, -1 (unchunked) -> 3.0s, so keep chunking on
79+
# Keep LIBTPU flags single-line (literal backslashes truncate libtpu flag parsing).
80+
COMMON_LIBTPU=${COMMON_LIBTPU:-"--xla_tpu_spmd_rng_bit_generator_unsafe=true --xla_tpu_enable_async_collective_fusion=true --xla_tpu_enable_async_collective_fusion_fuse_all_gather=false --xla_tpu_enable_async_collective_fusion_multiple_steps=true --xla_tpu_memory_bound_loop_optimizer_options=enabled:true --xla_tpu_enable_dot_strength_reduction=true --xla_enable_async_collective_permute=true --xla_tpu_enable_data_parallel_all_reduce_opt=true --xla_tpu_data_parallel_opt_different_sized_ops=true --xla_tpu_overlap_compute_collective_tc=true --xla_enable_async_all_gather=true --xla_tpu_scoped_vmem_limit_kib=65536 --xla_tpu_enable_async_all_to_all=true --xla_tpu_enable_all_experimental_scheduler_features=true --xla_tpu_enable_scheduler_memory_pressure_tracking=true --xla_tpu_host_transfer_overlap_limit=24 --xla_tpu_aggressive_opt_barrier_removal=ENABLED --xla_lhs_prioritize_async_depth_over_stall=ENABLED --xla_should_allow_loop_variant_parameter_in_chain=ENABLED --xla_should_add_loop_invariant_op_in_chain=ENABLED --xla_tpu_enable_ici_ag_pipelining=true --xla_max_concurrent_host_send_recv=100 --xla_tpu_scheduler_percent_shared_memory_limit=100 --xla_latency_hiding_scheduler_rerun=2 --xla_tpu_use_minor_sharding_for_major_trivial_input=true --xla_tpu_relayout_group_size_threshold_for_reduce_scatter=1 --xla_tpu_enable_latency_hiding_scheduler=true --xla_tpu_enable_ag_backward_pipelining=true --xla_tpu_use_single_sparse_core_for_all_gather_offload=true --xla_tpu_sparse_core_all_gather_latency_multiplier=1 --xla_tpu_sparse_core_reduce_scatter_latency_multiplier=3 --xla_tpu_enable_sparse_core_collective_aggregator=true --xla_tpu_enable_sparse_core_offload_queuing_in_lhs=true --xla_tpu_enable_sparse_core_reduce_scatter_v2=true --xla_tpu_enable_sparse_core_collective_offload_all_gather=true --xla_tpu_enable_sparse_core_collective_offload_2d_all_gather=true --xla_tpu_enable_sparse_core_collective_offload_all_reduce=true --xla_tpu_enable_sparse_core_collective_offload_reduce_scatter=true --xla_tpu_enable_sparse_core_collective_offload_3d_all_gather=true --xla_tpu_enable_concurrent_sparse_core_offloading=true --xla_tpu_assign_all_reduce_scatter_layout=true"}
81+
V6E_LIBTPU=${V6E_LIBTPU:-"--xla_tpu_enable_async_collective_fusion=true --xla_tpu_enable_async_collective_fusion_fuse_all_gather=false"}
82+
V7_LIBTPU=${V7_LIBTPU:-"--xla_tpu_enable_async_collective_fusion=true --xla_tpu_enable_async_collective_fusion_fuse_all_gather=false --xla_tpu_enable_megacore_fusion=true --xla_tpu_megacore_fusion_allow_ags=true --xla_tpu_dvfs_p_state=7"}
15983

160-
# ---------------------------------------------------------------------------
161-
# Platform profile: attention recipe, XLA flags, VMEM budget, text encoder
162-
# ---------------------------------------------------------------------------
163-
# Each arm carries the best measured configuration for that generation. Every
164-
# value is overridable via the matching env var (see the header).
84+
# Reference benchmarks (Wan 2.2 T2V-A14B, 720p/81f/40-step, CP=4, DP=2):
85+
# v6e-8: 131.7s e2e (denoise 129.6s)
86+
# tpu7x-8: 99.4s e2e (denoise 96.7s)
16587
case "$TPU_PROFILE" in
16688
v6e)
16789
PLATFORM_LIBTPU="$V6E_LIBTPU"
@@ -171,9 +93,6 @@ case "$TPU_PROFILE" in
17193
DEFAULT_BKV=1024
17294
DEFAULT_BKV_COMPUTE=512
17395
DEFAULT_BKV_COMPUTE_IN=512
174-
# v6e carries 128MiB of VMEM per core; the larger budget lets the 9472-wide
175-
# Q tile stay resident. Measured identical to the 64MiB budget on this
176-
# shape, so lower it if a different resolution spills.
17796
DEFAULT_VMEM=127506841
17897
DEFAULT_BQ_DKV=$DEFAULT_BQ
17998
DEFAULT_COMPILE_TE=true
@@ -297,6 +216,7 @@ python src/maxdiffusion/generate_wan.py "$CONFIG" \
297216
vae_weights_dtype=bfloat16 vae_dtype=bfloat16 \
298217
text_encoder_dtype=bfloat16 compile_text_encoder="$COMPILE_TE" use_batched_text_encoder="$USE_BATCHED_TE" \
299218
use_kv_cache=true use_base2_exp=true use_experimental_scheduler=true \
219+
use_fused_rope_kernel=true \
300220
fps=16 "${GUIDANCE_ARGS[@]}" \
301221
seed="${SEED:-12345}" \
302222
flash_block_sizes="$FLASH_BLOCK_SIZES" \

‎src/maxdiffusion/__init__.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@
1616

1717
__version__ = "0.22.0.dev0"
1818

19+
try:
20+
import jax.extend.core as _jec
21+
import jax.experimental.hijax as _hjx
22+
23+
if not hasattr(_hjx, "HiPrimitive"):
24+
25+
class _HiPrimitiveCompat(_jec.Primitive):
26+
pass
27+
28+
_hjx.HiPrimitive = _HiPrimitiveCompat
29+
except Exception:
30+
pass
31+
1932
from typing import TYPE_CHECKING
2033

2134
from .utils import (

‎src/maxdiffusion/configs/base_wan_14b.yml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,17 @@ split_head_dim: True
8686
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_custom_fixed_m, ulysses_custom_fixed_m_per_q_block, ulysses_ring, ulysses_ring_custom, ulysses_ring_custom_fixed_m, ulysses_ring_custom_fixed_m_per_q_block
8787
use_base2_exp: True
8888
use_experimental_scheduler: True
89+
# Ring/Ulysses attention only: subtract the globally-reduced K mean before the
90+
# all-to-all. Off by default -- costs a pmean and a full-size K subtraction per
91+
# layer. Enable only if the uncentered path shows softmax range problems.
8992
use_k_centering: False
93+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
94+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
95+
use_fused_rope_kernel: False
96+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
97+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
98+
fused_rope_block_s: 1024
99+
fused_rope_head_block: -1
90100
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
91101
ulysses_shards: -1
92102
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_1_3b.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ split_head_dim: True
8383
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_ring
8484
use_base2_exp: True
8585
use_experimental_scheduler: True
86+
# Ring/Ulysses attention only: subtract the globally-reduced K mean before the
87+
# all-to-all. Off by default -- costs a pmean and a full-size K subtraction per
88+
# layer. Enable only if the uncentered path shows softmax range problems.
89+
use_k_centering: False
90+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
91+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
92+
use_fused_rope_kernel: False
93+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
94+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
95+
fused_rope_block_s: 1024
96+
fused_rope_head_block: -1
8697
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
8798
ulysses_shards: -1
8899
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_27b.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,21 @@ attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cud
101101
# ulysses_shards to them now raises instead of being silently ignored.
102102
use_base2_exp: True
103103
use_experimental_scheduler: True
104+
# Ring/Ulysses attention only: subtract the globally-reduced K mean before the
105+
# all-to-all. Off by default -- costs a pmean and a full-size K subtraction per
106+
# layer. Enable only if the uncentered path shows softmax range problems.
104107
use_k_centering: False
108+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
109+
# ~1.9x faster in isolation; 3.1% e2e denoise gain on v6e-8, 1.7% on tpu7x-8.
110+
# Off by default: inlined into the model XLA rounds the unfused producer
111+
# differently, so the output is equivalent but not identical (v6e 720p/81f,
112+
# same seed: 53.8 dB PSNR after 1 denoise step, 34.1 dB after 40). Refused on
113+
# platforms whose RoPE rounding is unmeasured; tuned and measured for inference.
114+
use_fused_rope_kernel: False
115+
# Sequence block size for the fused RoPE kernel grid.
116+
fused_rope_block_s: 1024
117+
# Heads processed per grid step; -1 means "all heads in one step".
118+
fused_rope_head_block: -1
105119
# For attention=ulysses_ring*, hidden Ulysses shard count; ring shards are context / this.
106120
ulysses_shards: -1
107121
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_animate.yml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ split_head_dim: True
8484
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_custom_fixed_m, ulysses_custom_fixed_m_per_q_block, ulysses_ring, ulysses_ring_custom, ulysses_ring_custom_fixed_m, ulysses_ring_custom_fixed_m_per_q_block
8585
use_base2_exp: True
8686
use_experimental_scheduler: True
87+
# Ring/Ulysses attention only: subtract the globally-reduced K mean before the
88+
# all-to-all. Off by default -- costs a pmean and a full-size K subtraction per
89+
# layer. Enable only if the uncentered path shows softmax range problems.
90+
use_k_centering: False
91+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
92+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
93+
use_fused_rope_kernel: False
94+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
95+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
96+
fused_rope_block_s: 1024
97+
fused_rope_head_block: -1
8798
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
8899
ulysses_shards: -1
89100
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

0 commit comments

Comments
 (0)