Skip to content

Commit 00800f8

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 831c60c commit 00800f8

21 files changed

Lines changed: 3020 additions & 131 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: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,6 @@ export HF_HUB_ENABLE_HF_TRANSFER=1
5252
export JAX_DEFAULT_MATMUL_PRECISION=bfloat16
5353
export TORCHINDUCTOR_FX_GRAPH_CACHE=1
5454
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.
5755
export JAX_PERSISTENT_CACHE_MIN_ENTRY_SIZE_BYTES=-1
5856
export JAX_PERSISTENT_CACHE_MIN_COMPILE_TIME_SECS=0
5957

@@ -64,14 +62,7 @@ export TORCHINDUCTOR_CACHE_DIR=${TORCHINDUCTOR_CACHE_DIR:-$CACHE_ROOT/torch_comp
6462
mkdir -p "$CACHE_ROOT/jax" "$CACHE_ROOT/aot_wan$MODEL" "$CACHE_ROOT/converted" \
6563
"$OUTPUT_DIR" "$TMPDIR" "$TORCHINDUCTOR_CACHE_DIR"
6664

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.
65+
# Detect TPU generation via env or GCE metadata without initializing the device.
7566
_tpu_metadata() {
7667
curl -s -m 2 -H 'Metadata-Flavor: Google' \
7768
"http://metadata.google.internal/computeMetadata/v1/instance/attributes/$1" 2> /dev/null
@@ -81,8 +72,6 @@ _detect_accel_type() {
8172
local t="${TPU_ACCELERATOR_TYPE:-}"
8273
[ -z "$t" ] && t="$(_tpu_metadata accelerator-type)"
8374
[ -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).
8675
case "$t" in
8776
v[0-9]* | tpu[0-9]*) printf '%s' "$t" ;;
8877
*) printf '' ;;
@@ -91,8 +80,8 @@ _detect_accel_type() {
9180

9281
ACCEL_TYPE=${ACCEL_TYPE:-$(_detect_accel_type)}
9382
if [ -n "$ACCEL_TYPE" ]; then
94-
TPU_GEN="${ACCEL_TYPE%%-*}" # v6e-8 -> v6e
95-
TPU_CHIPS="${ACCEL_TYPE##*-}" # v6e-8 -> 8
83+
TPU_GEN="${ACCEL_TYPE%%-*}"
84+
TPU_CHIPS="${ACCEL_TYPE##*-}"
9685
else
9786
TPU_GEN=""
9887
TPU_CHIPS=""
@@ -182,6 +171,7 @@ case "$LIBTPU_INIT_ARGS" in
182171
esac
183172

184173
ATTENTION=${ATTENTION:-$DEFAULT_ATTENTION}
174+
USE_K_CENTERING=${USE_K_CENTERING:-auto} # auto: on for non-ring (v6e), off for ring (v7)
185175
ULYSSES_SHARDS=${ULYSSES_SHARDS:-$DEFAULT_U}
186176
BQ=${BQ:-$DEFAULT_BQ}
187177
BKV=${BKV:-$DEFAULT_BKV}
@@ -242,6 +232,7 @@ python src/maxdiffusion/generate_wan.py "$CONFIG" \
242232
aot_cache_dir="$CACHE_ROOT/aot_wan$MODEL" \
243233
converted_weights_dir="$CACHE_ROOT/converted" \
244234
attention="$ATTENTION" \
235+
use_k_centering="$USE_K_CENTERING" \
245236
ulysses_shards="$ULYSSES_SHARDS" \
246237
ici_data_parallelism="$DP" ici_fsdp_parallelism=1 \
247238
ici_context_parallelism="$CP" ici_tensor_parallelism=1 \
@@ -252,6 +243,7 @@ python src/maxdiffusion/generate_wan.py "$CONFIG" \
252243
vae_weights_dtype=bfloat16 vae_dtype=bfloat16 \
253244
text_encoder_dtype=bfloat16 compile_text_encoder="$COMPILE_TE" use_batched_text_encoder="$USE_BATCHED_TE" \
254245
use_kv_cache=true use_base2_exp=true use_experimental_scheduler=true \
246+
use_fused_rope_kernel=true \
255247
fps=16 "${GUIDANCE_ARGS[@]}" \
256248
seed="${SEED:-12345}" \
257249
flash_block_sizes="$FLASH_BLOCK_SIZES" \

‎src/maxdiffusion/__init__.py‎

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

1717
__version__ = "0.22.0.dev0"
1818

19+
try:
20+
import jax.core as _jc
21+
import jax.extend.core as _jec
22+
import jax.experimental.hijax as _hjx
23+
24+
if not hasattr(_jc, "Effect") and hasattr(_jec, "Effect"):
25+
_jc.Effect = _jec.Effect
26+
27+
if not hasattr(_hjx, "HiPrimitive"):
28+
_base_prim = getattr(_hjx, "VJPHiPrimitive", _jec.Primitive)
29+
30+
class _HiPrimitiveCompat(_base_prim):
31+
pass
32+
33+
_hjx.HiPrimitive = _HiPrimitiveCompat
34+
except (ImportError, AttributeError):
35+
pass
36+
1937
from typing import TYPE_CHECKING
2038

2139
from .utils import (
@@ -55,6 +73,7 @@
5573
"schedulers": [],
5674
"tpu_utils": [],
5775
"train_utils": [],
76+
"wan_runtime_options": [],
5877
"utils": [
5978
"OptionalDependencyNotAvailable",
6079
"is_flax_available",

‎src/maxdiffusion/aot_cache.py‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ def _extract_graphdef_statics(obj: Any, prefix: str = "") -> list[str]:
235235
return out
236236

237237

238+
def _runtime_env_statics() -> tuple[str, ...]:
239+
"""Graph-changing Wan switches (YAML `wan_*` keys), folded into every signature."""
240+
from maxdiffusion import wan_runtime_options # pylint: disable=import-outside-toplevel
241+
242+
return tuple(f"opt:{k}={v}" for k, v in sorted(wan_runtime_options.snapshot().items()))
243+
244+
238245
def _dynamic_signature(args: tuple, kwargs: dict) -> str:
239246
"""Deterministic digest of everything that selects an executable.
240247
@@ -260,6 +267,7 @@ def _dynamic_signature(args: tuple, kwargs: dict) -> str:
260267
desc = _format_static_val(leaf)
261268
parts.append(f"{jax.tree_util.keystr(path)}={desc}")
262269
parts.extend(_extract_graphdef_statics((args, kwargs)))
270+
parts.extend(_runtime_env_statics())
263271
return hashlib.sha256("|".join(parts).encode()).hexdigest()[:12]
264272

265273

@@ -368,7 +376,7 @@ def __call__(self, *args, **kwargs):
368376
)
369377
static_items = tuple((k, _format_static_val(v)) for k, v in sorted(static.items(), key=lambda item: str(item[0])))
370378
graphdef_statics = tuple(_extract_graphdef_statics((args, kwargs)))
371-
cache_key = (treedef, shapes_dtypes, static_items, graphdef_statics)
379+
cache_key = (treedef, shapes_dtypes, static_items, graphdef_statics, _runtime_env_statics())
372380
signature = getattr(self, "_sig_cache", {}).get(cache_key)
373381
if signature is None:
374382
signature = _dynamic_signature((), {**dynamic, **static})

‎src/maxdiffusion/configs/base_wan_14b.yml‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,34 @@ attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cud
105105

106106
use_base2_exp: True
107107
use_experimental_scheduler: True
108-
use_k_centering: "auto" # auto: on for non-ring ulysses_custom_fixed_m* (free, virtual), off for ring paths (pmean + HBM copy)
108+
# use_k_centering: "auto" turns K-centering on for the non-ring
109+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
110+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
111+
# full-size K subtraction per layer). Set True/False to force either path.
112+
use_k_centering: "auto"
113+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
114+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
115+
use_fused_rope_kernel: False
116+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
117+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
118+
fused_rope_block_s: 1024
119+
fused_rope_head_block: -1
120+
# Wan inference switches. These change the compiled graph, so they are part of
121+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
122+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
123+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
124+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
125+
wan_rope_norm_mode: "exact"
126+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
127+
wan_fuse_qk_prescale: True
128+
# Have the splash kernel write its output already transposed to [B, S, H, D].
129+
wan_splash_transpose_out: False
130+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
131+
wan_cfg_before_unpatchify: True
132+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
133+
wan_cross_attn_prescale_kv: False
134+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
135+
wan_rope_accum: "auto"
109136
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
110137
ulysses_shards: -1
111138
# 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: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cud
102102

103103
use_base2_exp: True
104104
use_experimental_scheduler: True
105+
# use_k_centering: "auto" turns K-centering on for the non-ring
106+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
107+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
108+
# full-size K subtraction per layer). Set True/False to force either path.
109+
use_k_centering: "auto"
110+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
111+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
112+
use_fused_rope_kernel: False
113+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
114+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
115+
fused_rope_block_s: 1024
116+
fused_rope_head_block: -1
117+
# Wan inference switches. These change the compiled graph, so they are part of
118+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
119+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
120+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
121+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
122+
wan_rope_norm_mode: "exact"
123+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
124+
wan_fuse_qk_prescale: True
125+
# Have the splash kernel write its output already transposed to [B, S, H, D].
126+
wan_splash_transpose_out: False
127+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
128+
wan_cfg_before_unpatchify: True
129+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
130+
wan_cross_attn_prescale_kv: False
131+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
132+
wan_rope_accum: "auto"
105133
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
106134
ulysses_shards: -1
107135
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_27b.yml‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,38 @@ attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cud
123123
# ulysses_shards to them now raises instead of being silently ignored.
124124
use_base2_exp: True
125125
use_experimental_scheduler: True
126-
use_k_centering: "auto" # auto: on for non-ring ulysses_custom_fixed_m* (free, virtual), off for ring paths (pmean + HBM copy)
126+
# use_k_centering: "auto" turns K-centering on for the non-ring
127+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
128+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
129+
# full-size K subtraction per layer). Set True/False to force either path.
130+
use_k_centering: "auto"
131+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
132+
# ~1.9x faster in isolation; 3.1% e2e denoise gain on v6e-8, 1.7% on tpu7x-8.
133+
# Off by default: inlined into the model XLA rounds the unfused producer
134+
# differently, so the output is equivalent but not identical (v6e 720p/81f,
135+
# same seed: 53.8 dB PSNR after 1 denoise step, 34.1 dB after 40). Refused on
136+
# platforms whose RoPE rounding is unmeasured; tuned and measured for inference.
137+
use_fused_rope_kernel: False
138+
# Sequence block size for the fused RoPE kernel grid.
139+
fused_rope_block_s: 1024
140+
# Heads processed per grid step; -1 means "all heads in one step".
141+
fused_rope_head_block: -1
142+
# Wan inference switches. These change the compiled graph, so they are part of
143+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
144+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
145+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
146+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
147+
wan_rope_norm_mode: "exact"
148+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
149+
wan_fuse_qk_prescale: True
150+
# Have the splash kernel write its output already transposed to [B, S, H, D].
151+
wan_splash_transpose_out: False
152+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
153+
wan_cfg_before_unpatchify: True
154+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
155+
wan_cross_attn_prescale_kv: False
156+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
157+
wan_rope_accum: "auto"
127158
# For attention=ulysses_ring*, hidden Ulysses shard count; ring shards are context / this.
128159
ulysses_shards: -1
129160
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_animate.yml‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,34 @@ 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+
# use_k_centering: "auto" turns K-centering on for the non-ring
88+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
89+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
90+
# full-size K subtraction per layer). Set True/False to force either path.
91+
use_k_centering: "auto"
92+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
93+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
94+
use_fused_rope_kernel: False
95+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
96+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
97+
fused_rope_block_s: 1024
98+
fused_rope_head_block: -1
99+
# Wan inference switches. These change the compiled graph, so they are part of
100+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
101+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
102+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
103+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
104+
wan_rope_norm_mode: "exact"
105+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
106+
wan_fuse_qk_prescale: True
107+
# Have the splash kernel write its output already transposed to [B, S, H, D].
108+
wan_splash_transpose_out: False
109+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
110+
wan_cfg_before_unpatchify: True
111+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
112+
wan_cross_attn_prescale_kv: False
113+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
114+
wan_rope_accum: "auto"
87115
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
88116
ulysses_shards: -1
89117
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_i2v_14b.yml‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,34 @@ 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+
# use_k_centering: "auto" turns K-centering on for the non-ring
90+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
91+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
92+
# full-size K subtraction per layer). Set True/False to force either path.
93+
use_k_centering: "auto"
94+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
95+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
96+
use_fused_rope_kernel: False
97+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
98+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
99+
fused_rope_block_s: 1024
100+
fused_rope_head_block: -1
101+
# Wan inference switches. These change the compiled graph, so they are part of
102+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
103+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
104+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
105+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
106+
wan_rope_norm_mode: "exact"
107+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
108+
wan_fuse_qk_prescale: True
109+
# Have the splash kernel write its output already transposed to [B, S, H, D].
110+
wan_splash_transpose_out: False
111+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
112+
wan_cfg_before_unpatchify: True
113+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
114+
wan_cross_attn_prescale_kv: False
115+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
116+
wan_rope_accum: "auto"
89117
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
90118
ulysses_shards: -1
91119
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_i2v_27b.yml‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,34 @@ 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+
# use_k_centering: "auto" turns K-centering on for the non-ring
90+
# ulysses_custom_fixed_m* kernels (virtual: q.k_mean folded into the kernel, no
91+
# collective, no HBM copy) and off for ring kernels (materialized: a pmean plus a
92+
# full-size K subtraction per layer). Set True/False to force either path.
93+
use_k_centering: "auto"
94+
# Fused RMSNorm+RoPE+head-transpose Pallas producer for self-attention QK.
95+
# Off by default; only measured on tpu7x and v6e. See base_wan_27b.yml.
96+
use_fused_rope_kernel: False
97+
# Fused RoPE kernel grid: sequence block size, and heads per grid step (-1 =
98+
# all). Tuned at the Wan 2.2 27B shard shape; re-measure for this model.
99+
fused_rope_block_s: 1024
100+
fused_rope_head_block: -1
101+
# Wan inference switches. These change the compiled graph, so they are part of
102+
# the AOT cache fingerprint. (They used to be WAN_* environment variables; the
103+
# env vars are still honoured only when no config is loaded, e.g. in unit tests.)
104+
# RMSNorm reduction for the fused RoPE producer: "exact" (XLA reduction,
105+
# bit-identical to the jitted XLA producer) or "fused" (in-kernel, <=1 bf16 ULP).
106+
wan_rope_norm_mode: "exact"
107+
# Fold log2(e) into Q and 1/sqrt(d) into K inside the fused RoPE producer.
108+
wan_fuse_qk_prescale: True
109+
# Have the splash kernel write its output already transposed to [B, S, H, D].
110+
wan_splash_transpose_out: False
111+
# Apply classifier-free guidance before unpatchify (halves the unpatchify work).
112+
wan_cfg_before_unpatchify: True
113+
# Pre-scale cached cross-attention K by 1/sqrt(d) once per video.
114+
wan_cross_attn_prescale_kv: False
115+
# Fused RoPE accumulation: "auto" (measured per-platform default), "dtype", "f32".
116+
wan_rope_accum: "auto"
89117
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
90118
ulysses_shards: -1
91119
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

0 commit comments

Comments
 (0)