Skip to content

Commit 3c4b194

Browse files
committed
feat(wan): lane sequence padding for self-attention (wan_seq_pad="lane")
Last of four pieces split out of the Wan fast-inference optimizations PR (the cross-attention Pallas kernel, token-sharded patch embedding and shard-major Ulysses A2A are the three PRs below this one). The switch defaults off in the Wan YAMLs and in wan_runtime_options; it is a plain config key plumbed to WanModel through `attention_config_entries(config)`. - Lane sequence padding (`wan_seq_pad="lane"`): `WanModel` pads the video tokens at the tail to a per-shard multiple of 128 (`75,600 -> 75,776` at 720p/CP=4; on the Ulysses-ring path each ring segment is padded at its own tail) so the Ulysses reshapes are copy-free, pads the rotary embedding to match, and unpads the output and residual. The padding descriptor (`TokenPadding`) is planned via `plan_token_padding` and passed explicitly as a `token_padding` argument from `WanModel.__call__` -> `WanTransformerBlock.__call__` -> `FlaxWanAttention.__call__` -> `NNXAttentionOp.apply_attention` (no process/thread-global `ContextVar`). - The custom `ulysses_custom*` / `ulysses_ring_custom*` kernels mask the pad keys by passing the real length as the ragged KV tail; the fixed-m norm reductions (`_compute_fixed_m_metadata` via `q_valid_len` on both the 1-D Ulysses path and the R == 1 ring path, `_ring_fixed_m_norms_pre_a2a` via `row_ok` for R > 1) exclude the pad Q/K/V rows so pad-token values cannot push a block onto the online-softmax path or move the `k_mean`. The ring's `ring_kv_seq_len` is always the real length. - Fails closed on invalid static/mode configs: when `wan_seq_pad="lane"` is requested, incompatible static configurations (`per_token_t=True` TI2V timesteps, `use_svg_attention`, `use_memory_efficient_attention`, unsupported kernels, or `ulysses_shards` that do not divide the context axis) raise `ValueError` in `plan_token_padding` (checked at `WanModel.__init__` time as well as per call); `_resolve_token_padding` raises `ValueError` on any `TokenPadding.total_len != seq_len` mismatch; and attention kernels that cannot mask active token padding raise `NotImplementedError` via `_reject_token_padding`. Dynamic short (`S < flash_min_seq_length`) or indivisible (`S % context_shards != 0`) sequences log a warn-once message and run unpadded. - `wan_block_benchmark.py` applies `plan_token_padding` to the benchmarked sequence length and passes `token_padding` explicitly to the block. - `run_wan_fast_inference.sh`: the v6e and v7 profiles set `WAN_SEQ_PAD=lane` and `DEFAULT_ULYSSES_OUT_A2A=shard_major` (overridable); the generic profile keeps `off` and `flat`. LIBTPU: v6e disables `--xla_tpu_enable_ici_ag_pipelining` / `--xla_tpu_enable_ag_backward_pipelining`, v7 enables them and adds `--xla_tpu_enable_offloading_copy_to_sparsecore=false` and `BQ=4736`. Performance, measured at the stack tip (Wan 2.2 T2V-A14B, 720p/81f/40 steps, warm AOT, DVFS unpinned unless noted; CP=4, DP=2, launcher defaults): v6e-8: denoise 124.5s - 125.0s, generate 126.6s - 127.5s tpu7x-8: denoise 104.9s, generate 107.8s (106.6s / 109.6s with fused RoPE off) tpu7x-8, DVFS pinned (`PIN_DVFS_P_STATE=true`): denoise 91.7s, generate 94.6s with the fused RoPE kernel on denoise 93.4s, generate 96.4s with the fused RoPE kernel off Tests - `wan_seq_pad_test.py` (19): padding plan, `ValueError` guards for invalid static/mode configs (including `per_token_t=True`) and `_resolve_token_padding` length mismatches, warn-once logging for short/indivisible sequences, pad-key masking for `ulysses_custom*` and `ulysses_ring_custom*` (U=2 x R=2, U=4 x R=1, and shard-major U=2 x R=2), fixed-m metadata ignoring pad query rows, `NotImplementedError` kernel guards, and end-to-end `WanModel` lane padding parity. All 19 tests run unconditionally in GitHub Actions (no `_SKIP_IN_GITHUB_ACTIONS`). - `wan_runtime_options_test.py` (12): the new switch's YAML default and enum validation; `wan_seq_pad` is excluded from the `FlaxWanAttention` attribute check since `WanModel` consumes it. - `end_to_end/tpu/run_wan_stack_tests.sh`: adds `wan_seq_pad_test.py`.
1 parent 5b4eddb commit 3c4b194

15 files changed

Lines changed: 1177 additions & 36 deletions

‎end_to_end/tpu/run_wan_fast_inference.sh‎

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
# v6e seed 777, 17.92 dB / 0.6556 on v6e seed 12345, and
6969
# 17.69 dB / 0.6918 on tpu7x seed 12345 (visually clean).
7070
# USE_FUSED_ROPE_KERNEL=false restores the XLA producer.
71-
# WAN_CROSS_ATTN_KERNEL / WAN_PATCH_EMBED_MODE / WAN_ULYSSES_OUT_A2A
71+
# WAN_CROSS_ATTN_KERNEL / WAN_PATCH_EMBED_MODE / WAN_ULYSSES_OUT_A2A / WAN_SEQ_PAD
7272
# override Wan fast-inference kernel/layout switches
7373
# DP / CP / PER_DEVICE_BATCH / SEED
7474
# override mesh parallelism, per-device batch, or RNG seed (default 12345)
@@ -146,8 +146,10 @@ if [ -z "${TPU_PROFILE:-}" ]; then
146146
fi
147147

148148
# Keep LIBTPU flags single-line (literal backslashes truncate libtpu flag parsing).
149-
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"}
150-
V6E_LIBTPU=${V6E_LIBTPU:-""}
149+
# ici_ag_pipelining and ag_backward_pipelining are configured per-profile below
150+
# (disabled on v6e, enabled on v7).
151+
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_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_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"}
152+
V6E_LIBTPU=${V6E_LIBTPU:-"--xla_tpu_enable_ici_ag_pipelining=false --xla_tpu_enable_ag_backward_pipelining=false"}
151153
# fuse_all_gather is false in every profile (see COMMON_LIBTPU); libtpu fails
152154
# backend init with it enabled on any non-v5e platform, including v6e and v7
153155
# ("Continuation fusion for AllGather ... not supported ... other than Viperlite").
@@ -158,13 +160,14 @@ V7_DVFS_FLAG=""
158160
if [ "$PIN_DVFS_P_STATE" = "true" ]; then
159161
V7_DVFS_FLAG=" --xla_tpu_dvfs_p_state=7"
160162
fi
161-
V7_LIBTPU=${V7_LIBTPU:-"--xla_tpu_enable_megacore_fusion=true --xla_tpu_megacore_fusion_allow_ags=true${V7_DVFS_FLAG}"}
163+
V7_LIBTPU=${V7_LIBTPU:-"--xla_tpu_enable_ici_ag_pipelining=true --xla_tpu_enable_ag_backward_pipelining=true --xla_tpu_enable_megacore_fusion=true --xla_tpu_megacore_fusion_allow_ags=true --xla_tpu_enable_offloading_copy_to_sparsecore=false${V7_DVFS_FLAG}"}
162164

163-
# Reference benchmarks (Wan 2.2 T2V-A14B, 720p/81f/40-step, CP=4, DP=2, warm AOT
164-
# cache, these launcher defaults, measured at this PR without the later PRs):
165-
# tpu7x-8: 115.5s generate (denoise 112.8s) DVFS unpinned;
166-
# 102.0s generate (denoise 99.4s) with PIN_DVFS_P_STATE=true
167-
# v6e-8: 140.1s generate (denoise 137.0s)
165+
# Reference benchmarks (Wan 2.2 T2V-A14B, 720p/81f/40-step, CP=4, DP=2, full
166+
# stack, these launcher defaults, warm AOT cache, DVFS unpinned):
167+
# v6e-8: 126.6s - 127.5s generate (denoise 124.5s - 125.0s)
168+
# tpu7x-8: 107.8s generate (denoise 104.9s; 109.6s / 106.6s with fused RoPE off)
169+
# tpu7x-8 with PIN_DVFS_P_STATE=true: 94.6s generate (denoise 91.7s) with the
170+
# fused RoPE kernel on, and 96.4s generate (denoise 93.4s) with it off.
168171
case "$TPU_PROFILE" in
169172
v6e)
170173
PLATFORM_LIBTPU="$V6E_LIBTPU"
@@ -183,13 +186,14 @@ case "$TPU_PROFILE" in
183186
DEFAULT_FUSED_ROPE=true
184187
DEFAULT_CROSS_ATTN_KERNEL=pallas
185188
DEFAULT_PATCH_EMBED_MODE=tokens
186-
DEFAULT_ULYSSES_OUT_A2A=chunked
189+
DEFAULT_ULYSSES_OUT_A2A=shard_major
190+
DEFAULT_SEQ_PAD=lane
187191
;;
188192
v7)
189193
PLATFORM_LIBTPU="$V7_LIBTPU"
190194
DEFAULT_ATTENTION="ulysses_ring_custom_fixed_m"
191195
DEFAULT_U=2
192-
DEFAULT_BQ=6400
196+
DEFAULT_BQ=4736
193197
DEFAULT_BKV=2048
194198
DEFAULT_BKV_COMPUTE=2048
195199
DEFAULT_BKV_COMPUTE_IN=2048
@@ -202,7 +206,8 @@ case "$TPU_PROFILE" in
202206
DEFAULT_FUSED_ROPE=true
203207
DEFAULT_CROSS_ATTN_KERNEL=pallas
204208
DEFAULT_PATCH_EMBED_MODE=tokens
205-
DEFAULT_ULYSSES_OUT_A2A=chunked
209+
DEFAULT_ULYSSES_OUT_A2A=shard_major
210+
DEFAULT_SEQ_PAD=lane
206211
;;
207212
*)
208213
echo "== warning: unrecognised or non-v6e/v7 accelerator '${ACCEL_TYPE:-unknown}';" \
@@ -224,6 +229,7 @@ case "$TPU_PROFILE" in
224229
DEFAULT_CROSS_ATTN_KERNEL=xla
225230
DEFAULT_PATCH_EMBED_MODE=tokens
226231
DEFAULT_ULYSSES_OUT_A2A=flat
232+
DEFAULT_SEQ_PAD=off
227233
;;
228234
esac
229235

@@ -258,6 +264,7 @@ FUSED_ROPE_ARGS=()
258264
WAN_CROSS_ATTN_KERNEL=${WAN_CROSS_ATTN_KERNEL:-$DEFAULT_CROSS_ATTN_KERNEL}
259265
WAN_PATCH_EMBED_MODE=${WAN_PATCH_EMBED_MODE:-$DEFAULT_PATCH_EMBED_MODE}
260266
WAN_ULYSSES_OUT_A2A=${WAN_ULYSSES_OUT_A2A:-$DEFAULT_ULYSSES_OUT_A2A}
267+
WAN_SEQ_PAD=${WAN_SEQ_PAD:-$DEFAULT_SEQ_PAD}
261268

262269
# Mesh: context parallelism carries the Ulysses shards, data parallelism takes
263270
# whatever devices remain. Defaults to CP=4 / DP=2 on an 8-device slice. On a
@@ -327,6 +334,7 @@ python src/maxdiffusion/generate_wan.py "$CONFIG" \
327334
wan_cross_attn_kernel="$WAN_CROSS_ATTN_KERNEL" \
328335
wan_patch_embed_mode="$WAN_PATCH_EMBED_MODE" \
329336
wan_ulysses_out_a2a="$WAN_ULYSSES_OUT_A2A" \
337+
wan_seq_pad="$WAN_SEQ_PAD" \
330338
fps=16 ${GUIDANCE_ARGS[@]+"${GUIDANCE_ARGS[@]}"} \
331339
seed="${SEED:-12345}" \
332340
flash_block_sizes="$FLASH_BLOCK_SIZES" \

‎end_to_end/tpu/run_wan_stack_tests.sh‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,7 @@ TESTS=(
5151
"$T/wan_patch_embed_test.py"
5252
# Shard-major Ulysses output A2A (feat/ulysses-shard-major-a2a)
5353
"$T/ulysses_out_a2a_test.py"
54+
# Lane sequence padding (feat/wan-fast-inference-optimizations)
55+
"$T/wan_seq_pad_test.py"
5456
)
5557
PYTHONPATH="src${PYTHONPATH:+:$PYTHONPATH}" exec "${PYTHON:-python3}" -m pytest -q -rs "${TESTS[@]}" "$@"

‎src/maxdiffusion/configs/base_wan_14b.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ wan_patch_embed_mode: "conv"
147147
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
148148
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
149149
wan_ulysses_out_a2a: "flat"
150+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
151+
# of the video token sequence (or at the end of each ring segment on the ring
152+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
153+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
154+
# Only the custom Ulysses / Ulysses-ring kernels use it.
155+
wan_seq_pad: "off"
150156
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
151157
ulysses_shards: -1
152158
# 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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,12 @@ wan_patch_embed_mode: "conv"
144144
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
145145
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
146146
wan_ulysses_out_a2a: "flat"
147+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
148+
# of the video token sequence (or at the end of each ring segment on the ring
149+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
150+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
151+
# Only the custom Ulysses / Ulysses-ring kernels use it.
152+
wan_seq_pad: "off"
147153
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
148154
ulysses_shards: -1
149155
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_27b.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,12 @@ wan_patch_embed_mode: "conv"
181181
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
182182
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
183183
wan_ulysses_out_a2a: "flat"
184+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
185+
# of the video token sequence (or at the end of each ring segment on the ring
186+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
187+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
188+
# Only the custom Ulysses / Ulysses-ring kernels use it.
189+
wan_seq_pad: "off"
184190
# For attention=ulysses_ring*, hidden Ulysses shard count; ring shards are context / this.
185191
ulysses_shards: -1
186192
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

‎src/maxdiffusion/configs/base_wan_animate.yml‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ wan_patch_embed_mode: "conv"
126126
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
127127
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
128128
wan_ulysses_out_a2a: "flat"
129+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
130+
# of the video token sequence (or at the end of each ring segment on the ring
131+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
132+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
133+
# Only the custom Ulysses / Ulysses-ring kernels use it.
134+
wan_seq_pad: "off"
129135
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
130136
ulysses_shards: -1
131137
# 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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ wan_patch_embed_mode: "conv"
128128
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
129129
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
130130
wan_ulysses_out_a2a: "flat"
131+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
132+
# of the video token sequence (or at the end of each ring segment on the ring
133+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
134+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
135+
# Only the custom Ulysses / Ulysses-ring kernels use it.
136+
wan_seq_pad: "off"
131137
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
132138
ulysses_shards: -1
133139
# 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: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,12 @@ wan_patch_embed_mode: "conv"
128128
# when S_local % block_q == 0); "shard_major" strictly requires the Pallas
129129
# 4D-q / out_num_shards path and raises if any kernel precondition fails.
130130
wan_ulysses_out_a2a: "flat"
131+
# Self-attention token padding: "off", or "lane" to append pad tokens at the end
132+
# of the video token sequence (or at the end of each ring segment on the ring
133+
# path) so every context shard has a multiple of 128 tokens. Aligned shards make
134+
# the Ulysses all-to-all reshapes free; the custom kernels mask the pad keys.
135+
# Only the custom Ulysses / Ulysses-ring kernels use it.
136+
wan_seq_pad: "off"
131137
# For attention=ulysses_ring, hidden Ulysses shard count; ring shards are context / this.
132138
ulysses_shards: -1
133139
# Splits Ulysses all-to-all into head-group chunks. The last chunk carries any remainder.

0 commit comments

Comments
 (0)