You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 182357d
Browse filesBrowse the repository at this point in the historyBrowse files
perf(wan): batch-sharded cross-attn KV cache and batch-aware VAE mesh
Two memory fixes for Wan 2.2 T2V at per-device batch >= 1, motivated by a
B=1..16 batch-scaling sweep at 720p/81f/40 steps on v6e-8 and tpu7x-8.
1. Cross-attention KV cache (transformer_wan.py)
compute_kv_cache ran an eager nnx.vmap over all 40 layers on replicated
prompt embeds, so every device materialised the full CFG-doubled cache
plus the FP32 RMSNorm temporaries for every layer at once. It is now one
jitted executable that pins the batch axis to ('data', 'fsdp') on input
and output and scans the layers one at a time. The cross-attn consumer
already re-constrains K/V to that layout, so nothing is resharded at use
time; each device just holds its own B_local slice.
If the batch is not divisible by data * fsdp (e.g. data_parallelism > 1
with per_device_batch_size = 1 and CFG off, the case the pipelines
already tolerate for the CFG cache) the batch constraint is skipped and
the cache stays replicated as before, rather than raising inside the jit.
Sharding the 512-token text axis on 'context' as well was tried and
rejected: XLA hoists the per-layer all-gather out of the layer scan, so
the forward program's reservation grew by 5.8 GiB (net loss on v6e-8).
2. VAE mesh (wan_pipeline.py), T2V pipelines only
With vae_spatial=-1 (auto) the VAE mesh is now ('redundant', 'vae_spatial')
= (N, 1) when the global batch is a multiple of the device count N, i.e.
one video per device with no cross-chip collectives, and (1, N) otherwise
(unchanged). Mixed meshes were measured and are not a win: at B=2 on
v6e-8 a (2, 4) mesh needed a 21.2 GiB decode program vs 15.5 GiB for
(1, 8). An explicit vae_spatial still overrides.
The batch-aware choice is gated by a new batch_parallel_vae argument on
_create_common_components, enabled only by wan_pipeline_2_1 / 2_2. The
VAE hard-constrains activations to P('redundant', ..., 'vae_spatial', ...),
so a (N, 1) mesh requires every tensor through the VAE to have a batch
divisible by N. That holds for T2V (only the final full-batch decode) but
not for VACE (reference images encoded one at a time, [1, 1, H, W, C]) or
the i2v / animate conditioning encodes; those keep the (1, N) mesh.
Tests: new tests/wan/wan_vae_mesh_test.py (CPU, 8 host devices) covers the
_auto_vae_spatial table for B in {0..16} gated and ungated, a guard that only
the T2V pipelines set the gate, and _batch_sharding /
_shard_kv_cache_on_batch layouts including the non-divisible fallback.
Measured on main (DP=2, CP=4 unless noted, use_kv_cache=true):
tpu7x-8 B=2 peak HBM 27.49 -> 16.65 GiB, denoise 270.5 -> 269.8 s
tpu7x-8 B=8 peak HBM 68.12 -> 24.32 GiB, VAE decode 41.6 -> 6.3 s,
denoise 897.8 -> 893.4 s
v6e-8 B=8 (DP=1, FSDP=2, CP=4, vae_decode_chunk=1) main OOMs in
compute_kv_cache (6.25 GiB alloc, 4.99 GiB free); with this
change it completes: peak 17.41 GiB, denoise 1302.6 s,
VAE decode 4.8 s.
0 commit comments