Skip to content

Commit bc11953

Browse files
committed
feat(wan): fast serving with persistent AOT caching and tuned inference recipe
Persistent per-shape AOT executable cache, converted-weights cache and a tuned launcher for Wan inference on TPU. User-visible changes - AOT caching is opt-in. By default `generate_wan.run()` installs no cache directory and every call is plain jax.jit. When `aot_cache_dir` is set, the source revision must be reusable (`get_git_commit_hash()` or an explicit clean `aot_build_revision`, combined with the package content hash); a dirty or unversioned tree downgrades to an ephemeral temp dir rather than writing or reading stale persistent executables. `enable_zero_execution_warmup: True` (False in all 6 Wan ymls) uses an ephemeral temp dir that is removed in a `try/finally` block. - `aot_cache_dir` must be a local filesystem path (`gs://` URLs raise `ValueError`), and `purge_compiled_cache` refuses symlink targets that resolve outside the cache root. - `_prune_stale_generations` prunes `.aotx` files from older fingerprint generations beyond `_DEFAULT_KEEP_GENERATIONS = 3`. - Converted-weights cache format is v3. Format v2 manifests whose `legacy_source_fingerprint` matches are migrated in place to v3 via `_migrate_manifest_header`; older or mismatched manifests are rejected and re-converted once. `try_load_converted_weights` validates leaf keys, shapes, and dtypes against `eval_shapes`. AOT cache (aot_cache.py, generate_wan.py) - Signatures key non-static Python scalars by type (jit traces them), so e.g. guidance values share one executable; statics are keyed by value; nnx GraphDef statics get a process-deterministic digest. - Metadata fingerprint covers model/attention/tiles/mesh/VAE/dtype/remat/ cache options, device kind, process count, jax/jaxlib/libtpu/flax/qwix/ tokamax versions, matmul precision, PRNG impl, x64, threefry, LIBTPU_INIT_ARGS and XLA_FLAGS, plus the source revision. - Multi-host: per-host `-p{idx}` .aotx files. - `_align_inputs` raises on a leaf-count mismatch instead of truncating. - The .aotx pickle envelope is loaded with an exact (module, name) allowlist. Warmup & Denoise helpers (wan_denoise_utils.py) - When aot_cache is in warmup mode, `compile_experts` compiles both experts' forward passes without executing them. Without an installed cache, warmup is an ordinary 2-step run. - `InflightWindow` bounds queued denoise steps (`MAXD_QUEUE_MAX_INFLIGHT`, default 4, validated `>= 1`), shared by T2V and I2V. Other - Unpatchify: when p_t == 1, reshape/transpose through a 7D tensor instead of 8D (same result; avoids an 8D strided copy). - `use_k_centering` is plumbed from the Wan config to the attention layer; `use_k_centering`, `aot_build_revision`, and `enable_zero_execution_warmup` added to the Wan ymls. - Dot-product attention: float32_qk_product uses preferred_element_type=f32 on the QK einsum instead of upcasting Q/K (memory-efficient path unchanged). - Video export is atomic (temp file + os.replace), process-0 only, and shared by `run()` and `inference_generate_video`; trainers print SSIM on process 0. - Launcher (`end_to_end/tpu/run_wan_fast_inference.sh`): platform-detected v6e / v7 profiles, generic profile for everything else; derives `TPU_DEVICES` only for per-chip families (`v5litepod | v5e | v6e | v7* | tpu7*`) and exits with an error for other families unless `DP` and `CP` are set explicitly; DVFS pin is opt-in (`PIN_DVFS_P_STATE`). Performance, measured with this PR at the top of the stack (without #488-#491; Wan 2.2 T2V-A14B, 720p/81f/40 steps, CP=4, DP=2, warm AOT, launcher defaults): v6e-8: generate 140.1s, denoise 137.0s tpu7x-8: generate 115.5s, denoise 112.8s (DVFS unpinned) generate 102.0s, denoise 99.4s (`PIN_DVFS_P_STATE=true`) Tests - aot_cache_test.py and converted_weights_cache_test.py: restricted-unpickler allowlist, dirty-tree ephemeral downgrade and symlink-escape purge guards, stale-generation pruning, float32_qk_product keeping bf16 QK operands, v2-to-v3 manifest migration and fail-closed mismatched/fingerprint-less manifests, leaf dtype validation, disk-space checks, warm start before the network, revision captured through HF symlinks, and distinct fingerprints for subfolders with identical index files. - wan/wan_warmup_coverage_test.py (28): all tests run unconditionally in GitHub Actions; wan/wan_transformer_test.py adds `FlaxWanAttention` k-centering parity and dispatch tests.
1 parent ef5f36b commit bc11953

29 files changed

Lines changed: 3201 additions & 356 deletions

‎end_to_end/tpu/run_wan_fast_inference.sh‎

Lines changed: 243 additions & 43 deletions
Large diffs are not rendered by default.

‎end_to_end/tpu/run_wan_stack_tests.sh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,10 @@ TESTS=(
3737
"$T/custom_splash_unpadded_test.py"
3838
# XLA-fused RMSNorm+RoPE producer (feat/fused-rmsnorm-rope-xla)
3939
"$T/fused_producers_test.py"
40+
# Wan fast serving / AOT cache (feat/wan-fast-serving)
41+
"$T/aot_cache_test.py"
42+
"$T/converted_weights_cache_test.py"
43+
"$T/wan/wan_transformer_test.py"
44+
"$T/wan/wan_warmup_coverage_test.py"
4045
)
4146
PYTHONPATH="src${PYTHONPATH:+:$PYTHONPATH}" exec "${PYTHON:-python3}" -m pytest -q -rs "${TESTS[@]}" "$@"

‎src/maxdiffusion/aot_cache.py‎

Lines changed: 428 additions & 76 deletions
Large diffs are not rendered by default.

‎src/maxdiffusion/configs/base_wan_14b.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,11 @@ svg_low_noise_density: -1.0
101101
# compute blocks and the sparse kernel for small ones. Must stay a dict so the
102102
# command line can override it with JSON.
103103
svg_flash_block_sizes: {}
104-
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_ring
104+
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, tokamax_ring_custom, 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, ulysses_ring_custom_bidir
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* (virtual, no copy), off for ring paths (virtual via k_mean, plus pmean when R>1)
108109
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
109110
ulysses_shards: -1
110111
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.
@@ -309,6 +310,12 @@ dataset_config_name: ''
309310
jax_cache_dir: ''
310311
# Directory for per-shape AOT serialized executables ('' = disabled).
311312
aot_cache_dir: ''
313+
# Zero-execution warmup (compile the transformer passes without executing them) is always
314+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
315+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
316+
enable_zero_execution_warmup: False
317+
aot_build_revision: ''
318+
wan_debug_cond_timers: False
312319
# Directory for memoized torch->flax converted weights ('' = disabled).
313320
converted_weights_dir: ''
314321
hf_data_dir: ''

‎src/maxdiffusion/configs/base_wan_1_3b.yml‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ 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"
105106
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
106107
ulysses_shards: -1
107108
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.
@@ -262,6 +263,12 @@ dataset_config_name: ''
262263
jax_cache_dir: ''
263264
# Directory for per-shape AOT serialized executables ('' = disabled).
264265
aot_cache_dir: ''
266+
# Zero-execution warmup (compile the transformer passes without executing them) is always
267+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
268+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
269+
enable_zero_execution_warmup: False
270+
aot_build_revision: ''
271+
wan_debug_cond_timers: False
265272
# Directory for memoized torch->flax converted weights ('' = disabled).
266273
converted_weights_dir: ''
267274
hf_data_dir: ''

‎src/maxdiffusion/configs/base_wan_27b.yml‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,6 @@ use_base2_exp: True
129129
use_experimental_scheduler: True
130130
# auto: on for non-ring ulysses_custom_fixed_m* (virtual, no copy), off for ring
131131
# paths (virtual K-centering via k_mean, plus a pmean across ring shards when R>1).
132-
# Note: the Wan pipelines do not pass this key to the attention layer yet, so
133-
# the layer's default ("auto") applies.
134132
use_k_centering: "auto"
135133
# For attention=ulysses_ring*, hidden Ulysses shard count; ring shards are context / this.
136134
ulysses_shards: -1
@@ -304,6 +302,12 @@ dataset_config_name: ''
304302
jax_cache_dir: ''
305303
# Directory for per-shape AOT serialized executables ('' = disabled).
306304
aot_cache_dir: ''
305+
# Zero-execution warmup (compile the transformer passes without executing them) is always
306+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
307+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
308+
enable_zero_execution_warmup: False
309+
aot_build_revision: ''
310+
wan_debug_cond_timers: False
307311
# Directory for memoized torch->flax converted weights ('' = disabled).
308312
converted_weights_dir: ''
309313
hf_data_dir: ''

‎src/maxdiffusion/configs/base_wan_animate.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ jit_initializers: True
8181
# Set true to load weights from pytorch
8282
from_pt: True
8383
split_head_dim: True
84-
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_ring
84+
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"
8788
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
8889
ulysses_shards: -1
8990
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.
@@ -253,6 +254,12 @@ dataset_config_name: ''
253254
jax_cache_dir: '.jax_cache'
254255
# Directory for per-shape AOT serialized executables ('' = disabled).
255256
aot_cache_dir: ''
257+
# Zero-execution warmup (compile the transformer passes without executing them) is always
258+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
259+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
260+
enable_zero_execution_warmup: False
261+
aot_build_revision: ''
262+
wan_debug_cond_timers: False
256263
# Directory for memoized torch->flax converted weights ('' = disabled).
257264
converted_weights_dir: ''
258265
hf_data_dir: ''

‎src/maxdiffusion/configs/base_wan_i2v_14b.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ jit_initializers: True
8383
# Set true to load weights from pytorch
8484
from_pt: True
8585
split_head_dim: True
86-
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_ring
86+
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"
8990
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
9091
ulysses_shards: -1
9192
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.
@@ -256,6 +257,12 @@ dataset_config_name: ''
256257
jax_cache_dir: ''
257258
# Directory for per-shape AOT serialized executables ('' = disabled).
258259
aot_cache_dir: ''
260+
# Zero-execution warmup (compile the transformer passes without executing them) is always
261+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
262+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
263+
enable_zero_execution_warmup: False
264+
aot_build_revision: ''
265+
wan_debug_cond_timers: False
259266
# Directory for memoized torch->flax converted weights ('' = disabled).
260267
converted_weights_dir: ''
261268
hf_data_dir: ''

‎src/maxdiffusion/configs/base_wan_i2v_27b.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,10 @@ jit_initializers: True
8383
# Set true to load weights from pytorch
8484
from_pt: True
8585
split_head_dim: True
86-
attention: 'flash' # Supported attention: dot_product, flash, tokamax_flash, cudnn_flash_te, ring, tokamax_ring, ulysses, ulysses_custom, ulysses_ring
86+
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"
8990
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
9091
ulysses_shards: -1
9192
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.
@@ -257,6 +258,12 @@ dataset_config_name: ''
257258
jax_cache_dir: ''
258259
# Directory for per-shape AOT serialized executables ('' = disabled).
259260
aot_cache_dir: ''
261+
# Zero-execution warmup (compile the transformer passes without executing them) is always
262+
# on when aot_cache_dir is set. When aot_cache_dir is '', True installs an ephemeral AOT
263+
# cache for zero-execution warmup; False keeps plain jax.jit (warmup executes the model).
264+
enable_zero_execution_warmup: False
265+
aot_build_revision: ''
266+
wan_debug_cond_timers: False
260267
# Directory for memoized torch->flax converted weights ('' = disabled).
261268
converted_weights_dir: ''
262269
hf_data_dir: ''

‎src/maxdiffusion/generate_ltx2.py‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import time
2020
import os
2121
import subprocess
22-
import uuid
2322
from maxdiffusion.checkpointing.ltx2_checkpointer import LTX2Checkpointer
2423
from maxdiffusion import aot_cache, pyconfig, max_logging, max_utils
2524
from absl import app
@@ -149,7 +148,7 @@ def _canonical_aot_value(value):
149148

150149
def _non_reusable_aot_revision():
151150
"""Returns a unique identity so unversioned source can never hit old HLO."""
152-
return f"unversioned:{uuid.uuid4().hex}"
151+
return aot_cache.non_reusable_aot_revision()
153152

154153

155154
def _resolve_ltx2_aot_source_revision(config, commit_hash=None):
@@ -161,9 +160,7 @@ def _resolve_ltx2_aot_source_revision(config, commit_hash=None):
161160

162161

163162
def _is_reusable_aot_revision(source_revision) -> bool:
164-
if source_revision is None or not str(source_revision).strip():
165-
return False
166-
return not str(source_revision).startswith(("dirty:", "unversioned:"))
163+
return aot_cache.is_reusable_aot_revision(source_revision)
167164

168165

169166
def ltx2_aot_metadata(config, pipeline, source_revision=None):

0 commit comments

Comments
 (0)