Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces several optimizations and features for Wan model inference, including a fast-path signature cache in the AOT cache, support for fused LayerNorm and AdaLN kernels, and updated configuration options. The review feedback highlights a potential concurrency issue in the signature cache that could lead to race conditions, suggests moving inline imports of the fused kernel to the top of the file to reduce overhead in hot paths, and recommends avoiding os.path.join on GCS URIs to ensure cross-platform safety.
c9dc7b2 to
80719d9
Compare
80719d9 to
585247a
Compare
585247a to
2ee9c66
Compare
2ee9c66 to
8756432
Compare
8756432 to
2957831
Compare
8eb1b56 to
dc78d28
Compare
dc78d28 to
e98f895
Compare
e98f895 to
ba40a77
Compare
ba40a77 to
d7361d3
Compare
d7361d3 to
3540cb7
Compare
3540cb7 to
89af7b2
Compare
89af7b2 to
a1e652a
Compare
a1e652a to
838af4b
Compare
8631d1f to
a909245
Compare
3a0c913 to
a415017
Compare
a415017 to
6b85dfc
Compare
6b85dfc to
7e38c04
Compare
7e38c04 to
1d65bfa
Compare
|
I was trying to reproduce the results, and I got:
Deltas vs baseline
this is on v7x-8. The stats are slightly different, but I think the perf improvement is bigger than 3.8% |
mbohlool
left a comment
There was a problem hiding this comment.
LGTM after addressing comments.
…ce 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.
Summary
Fast serving for Wan on TPU: a persistent per-shape AOT executable cache, a converted-weights cache, and a tuned launcher (
end_to_end/tpu/run_wan_fast_inference.sh).User-visible changes
generate_wan.run()is plainjax.jit.aot_cache_diris set and the source revision is reusable. An explicitdirty:/unversioned:revision orenable_zero_execution_warmup: True(default False in all 6 Wan ymls) uses an ephemeral temp dir, torn down in atry/finallycovering setup and install.aot_cache_dirmust be a local path:gs://raisesValueErrorregardless of revision..aotxformat v2 (v1 files are recompiled once).What's in it
.pyinmaxdiffusion. Per-host.aotxfiles for multi-host..aotxloading: exact(module, name)pickle allowlist rejecting dotted names (defence in depth; directory write permissions are the real control).local_files_onlybefore network calls.compile_expertscompiles both experts without executing when AOT is installed;InflightWindowbounds queued steps; platform-detected v6e/v7/generic profiles with opt-inPIN_DVFS_P_STATE.Performance
Wan 2.2 T2V-A14B, 720p / 81 frames / 40 steps (
CP=4, DP=2), measured with this PR at top of stack:COMPILE_TE=true)compile_experts)Tests
aot_cache_test.py(41),converted_weights_cache_test.py(16),wan_transformer_test.py(15),wan_warmup_coverage_test.py(9def test_; 14 with subtests).run()AOT gating and teardown (including install failure),gs://rejection,float32_qk_productkeeping bf16 QK operands, fail-closed manifests, disk-space checks, revision/subfolder fingerprinting, and real TPU executable reload.wan_transformertests skip inGITHUB_ACTIONS=true(40 of 199 cumulativedef test_, or 204 counting subtests); all 57 AOT/converted-weights and 9 warmup tests (14 with subtests) run in CI.def test_passed (204 counting the 5 warmup subtests; 81def test_/ 86 with subtests in this PR's 4 test files).Stack: #477 → #507 → #478 → #508 → #479 → #488 → #509 → #510 → #511 → #491