Fixed-m softmax for 2D-ring x Ulysses attention (WAN 2.2) - #452
Open
syhuang22 wants to merge 1 commit into
Open
Conversation
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Removes the online-softmax running-max and rescale chain from the ring attention kernel, replacing it with a per-query Cauchy-Schwarz bound that is known before streaming starts. Heads whose bound cannot be proven safe keep online softmax, so the result is exact either way. 720p 81f T2V, 40 steps, v7x-8, dp2 x (ring2 x uly2), denoise total: attention=ulysses_ring_custom 9472/1024 109.3s 2.733 s/step attention=ulysses_ring_custom_fixed_m 6400/2048 105.3s 2.633 s/step The tile differs per mode because the optimum moves. Plain is MXU-bound and peaks at bq=9472, degrading to ~3.14 s/step at 6400/2048; fixed-m is flat across both (2.63-2.69), which is the mechanism working -- with the softmax VPU chain gone the kernel stops caring which tile it was given. In-kernel the ring hop goes 22.65 -> 21.15 ms, i.e. 85% -> 91% of the K=128 MXU roofline. Ring specifics, over the flat fixed-m design: - No k-smoothing is possible on a ring (no rank holds the full K, and RoPE does not preserve a zero mean), so the gate uses the two-sided halved bound 106.5 rather than the flat 213. - The bound is global over every K shard, so a head's pinned m is identical on every hop and the partials combine by accumulation. When any head fails the gate the call falls back to an LSE-space merge, which is invariant to the bound's overshoot; a naive (m, l) merge would flush the other partials. - The all-heads-fixed branch compiles a branch-free kernel. Per-head dispatch in the ragged last KV block degrades the whole grid's instruction schedule (measured 3x), so exactly one body is emitted there. Also in this change: - Warmup now runs one real step per weight set. A 2-step warmup puts both steps on the high-noise transformer, leaving the low-noise weights untouched until mid-generation, where their first call cost 26.4s against a 2.6s step. The first generation now reports steady-state latency instead of needing a throwaway run (denoise 118.5s -> 105.3s on a cold process). - _ulysses_attention no longer folds the CFG batch into heads when data/fsdp already shard it; the folded size-1 batch is not divisible by those axes. - Cross-attention demotes to ulysses_custom for the fixed-m ring variant too, matching the other two ring variants. - run_wan_fast_inference.sh takes FIXEDM=0/1 and picks the matching tile. Tests: src/maxdiffusion/tests/ring_fixed_m_test.py covers the online path, an all-eligible call, a sink head that falls back everywhere, a ragged KV tail, and a caller-supplied local max||k|| being promoted to the global bound.
syhuang22
force-pushed
the
fixed-m-2dring-v3
branch
from
July 27, 2026 08:24
95b9766 to
8dd6305
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the online-softmax running-max and rescale chain in the 2D-ring x
Ulysses attention kernel with a per-query Cauchy-Schwarz bound known before
streaming starts. Heads whose bound cannot be proven safe keep online softmax,
so the output is exact either way.
Result
720p 81f T2V, 40 steps, v7x-8,
dp2 x (ring2 x uly2):block_q/block_kvulysses_ring_custom(baseline)ulysses_ring_custom_fixed_m-3.7%. In-kernel the ring hop goes 22.65 -> 21.15 ms
Each mode uses its own optimal tile. Plain is MXU-bound and peaks at
block_q=9472, degrading to ~3.14 s/step at 6400/2048; fixed-m is flat acrossboth (2.63-2.69 s/step). That flatness is the mechanism working -- with the
softmax VPU chain gone the kernel stops caring which tile it was given.
Reproduce
One TPU v7x-8 host.
run_wan_fast_inference.shselects the attention kerneland the matching tile:
Read
Denoise Totalfrom the timing summary. The first run per variantcompiles (~200s) and already reports steady-state latency; later runs reuse the
AOT cache. Give each variant its own
WAN_CACHE_ROOTif you change kernelsource between runs -- the AOT fingerprint covers config, not source.
Notes
not preserve a zero mean), so the gate uses the two-sided halved bound 106.5
rather than the flat 213.
mis identicalon every hop and the hop partials combine by accumulation; a gate failure
falls back to an LSE-space merge, which is invariant to the overshoot.
instruction schedule (measured 3x), so the all-heads-fixed branch compiles a
single branch-free body.
Tests
src/maxdiffusion/tests/ring_fixed_m_test.py(5 cases, TPU) checks against anf32 dense-softmax reference: the online path, an all-eligible call, a sink head
that falls back everywhere, a ragged KV tail, and a caller-supplied per-shard
max||k||being promoted to the global bound.