Repository navigation
Conversation
With CFG on its own axis, sp_size on Wan2.2's dit now means what it means everywhere else. Wan22DiT takes the node's sp_group: after patchify each rank keeps a contiguous slice of the tokens, with their RoPE tables and per-token timesteps, self-attention all-to-alls q/k/v around the ragged kernel (ulysses_attention; heads shard over sp through head_shards), cross-attention, the FFN and the output head run on the slice, and the slices are gathered before unpatchify. The dit is declared SP-enabled. On one rank every step is a no-op (scatter/gather pass through, and ulysses_attention calls the kernel directly). Wan22DiT refuses an sp_size that doesn't divide its heads, as Cosmos3 does. A two-rank gloo test runs the DiT under SP against the same DiT on one rank, on an even and an uneven token split. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
configs/wan22_cfg2_sp2_4gpu.yaml runs one guidance branch per pair of ranks and each branch's tokens over its pair. models.rst lists it, and serving.rst's parallelism example shows the two axes composed. It needs upstream #705 to start: on a mesh with two axes the axis groups' members-only gloo twins disagree on their names without it, and startup hangs. The config's first line says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… has Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… perf/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rf/cfg-axis Brings in #948's merge of #860, #859 and #858's new commits (485ec25): TokenStop leaves the token budget to the decode loop's cap (generated_key is gone), the step closing a reply that ended at prefill withholds its token through StopCheck / BatchedStopCheck instead of the engine's pop, the inline client emit gives a withheld row None in both graph runtimes, the ragged-attention op registry names each manager uniquely, a replica count that fails leaves warmup at one copy, and docs. No semantic resolution was needed: this branch's Wan2.2 CFG x SP changes add no TokenStop adopter, no generated_key, no cap-vs-stop-check test for a TokenStop adopter, and no withheld, inline-emit or Rust inline-value change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTX-2.5's SeqParallel moves to components/distributed/sequence_parallel.py and gains scatter/gather; Wan2.2's dit builds one instead of nesting partials around ulysses_attention. The (q, k, v) -> out alias that LTX-2.5 and LingBot-World each defined becomes one Attend beside RaggedAttentionFn, which also types Wan2.2's attend parameters. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…able Wan2.2's dit is the only CFG-enabled node, and it now runs Ulysses on sp_size, so the hint for a CFG node refusing sp_size can't fire. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efusal test The one-rank reference is the shipped default (sp_group=None) rather than a one-rank gloo spawn. A head count sp_size doesn't divide still fails loudly without the guard, so its test pinned only the message. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nstraint Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…mport The distributed layer annotates run as Callable[..., torch.Tensor], as ulysses_attention does, rather than importing the diffusion scaffold's Attend for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p_size Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to perf/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… into perf/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… into perf/cfg-axis main's #1078 computes the DiT's modulation per frame; under Ulysses a rank's token slice can cut a frame, so the dit expands the timestep per token there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rf/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rf/cfg-axis Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dghosef
marked this pull request as ready for review
November 3, 2026 16:18
This branch has not been deployed
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.
Draft. Stacked on #948 (
perf/cfg-axis-core, the cfg_size axis), up to date with its head by a merge.Wan2.2's dit had no sequence parallelism: its
sp_sizeslot was used for CFG parallelism instead. With CFG now on its owncfg_sizeaxis (#948),sp_sizecan mean what it means for every other model, Ulysses sequence parallelism, and with both axes a 4-GPU deployment can split the guidance branches and each branch's tokens. cfg2 × sp2 on 4 GPUs makes a 1280×704×81 video in 8.76 s, against 13.03 s for cfg2 on 2 (−33%), and cfg2's own output is byte-identical to the base.Wan22DiTtakes the node'ssp_group. After patchify, each rank keeps a contiguous slice of the tokens, with their RoPE tables and per-token timesteps. Self-attention all-to-alls q/k/v around the ragged attention kernel (ulysses_attention; heads shard over SP throughhead_shards), while cross-attention, the FFN and the output head run on each rank's slice; the slices are gathered afterproj_out, before unpatchify. The binding is LTX-2.5'sSeqParallel, moved tomstar/model/components/distributed/sequence_parallel.pyand givenscatter/gather: Wan builds one per forward and passessp.attendto its blocks, and LTX-2.5 imports it from there. The(q, k, v) -> outtype that LTX-2.5 and LingBot-World each defined is oneAttendbesideRaggedAttentionFn, and it types Wan'sattendparameters. The dit is declared SP-enabled. On one rank the scatter and gather do nothing andulysses_attentioncalls the kernel directly, andWan22DiTrefuses ansp_sizethat doesn't divide its 24 heads, as Cosmos3 does.configs/wan22_cfg2_sp2_4gpu.yaml(cfg_size: 2, sp_size: 2) runs one guidance branch per pair of ranks, and Ulysses over each pair. The mesh is row-major over (cfg, sp, tp), so on ranks [0, 1, 2, 3] the SP pairs are [0, 1] and [2, 3], each inside one guidance branch, and the all-to-alls never cross branches.docs/models.rstlists it (split the guidance branches first withcfg_size, then addsp_sizeon top;sp_sizemust divide 24 heads;wan22_dp2.yamlstays the throughput layout), anddocs/serving.rst's parallelism example shows the two axes composed. An old-style Wan config that setsp_size: 2for the guidance split now runs Ulysses. That's correct, but slower thancfg_size: 2on two GPUs (14.20 vs 13.03 s). Only the unmerged stack ever had that shape, so no released config changes. With Wan's dit SP-enabled, #948's placement hint ("split guidance branches with cfg_size instead") could no longer fire for any model, so this PR deletes it.GPU rows were measured at
be35b7409against its base, #948 at1ffb5c019; the heada641f029cdiffers frombe35b7409only by main merges. Eras 4× H200 (NVLink), torch 2.12.1+cu132, Wan2.2 TI2V-5B, 1280×704×81, 20 steps, guidance 5, a fixed seed, a fresh Inductor cache on every boot, 0 failed requests in every cell. Latency is c=1, 2 boots per layout in rotated order, each 1 warmup and 3 videos; peak VRAM was 42,749–43,567 MiB across layouts.TORCH_LOGS=graph_breaks,recompilestorch.compiler.disabled all-to-all. SP's extra recompiles all land in the first forward; a second request has 0 recompiles and 0 graph breaks on every layout.engine.forwardper denoise step; nsys per rankdacb760a9NCCL_NVLS_ENABLE=0SeqParallelmoved)ltx2_5_sp2.yaml, one t2av request, 4 compiled boots per armsp_group=Nonedefault), on an even and an uneven token split.test_startup_graph_validationloads every shipped config,wan22_cfg2_sp2_4gpu.yamlincluded, through placement, which refuses it without the dit'ssp_enabled_nodes. A three-rank gloo case oftest_sequence_parallel(test_ulysses_attention_matches_one_rank) aborts at process teardown on some runs; its code (ulysses_attention,CommGroup.all_to_all) is the same as #948's head, where it fails intermittently too.ruff; CPU Core vs main9f84f18cdOMP_NUM_THREADS=2, heada641f029cruffclean. CPU Core against main9f84f18cd: 9094 passed, 1066 skipped, 2 failed, the same 2 as main (thetest_eval_compare_multigpudump_stacks pair); none fail only on this branch. GitHub CI green ata641f029c.Not yet run on GPU: the full videos watched by eye (the contact sheets above are five frames per layout).
Follow-ups: a compile-traceable all-to-all.
CommGroup.all_to_allruns through atorch.compiler.disabled_all_to_all_single, so every Ulysses exchange breaks the compiled graph, in Cosmos3, LTX-2.5 and Wan alike; making it traceable is a sharedCommGroupchange, not Wan work, and its gain is small: the breaks cost ~1 ms of host time per step, where the exchange and its packing cost ~55 ms. Cosmos3 keeps its own SP binding: it threadsseq_sizesthrough each layer over an unbatched[S, H, D]stream (sequence dim 0), and its attention mixes the Ulysses exchange with a replicated prefix sliced by heads (sp_head_slice/sp_head_gather).SeqParallelis fixed to[B, L, H, D], so moving Cosmos3 onto it needs a layout field, which isn't a mechanical swap. Ulysses in the attention resource: the ragged resource already knows the node's SP group and sizes its plan atheads / (tp × sp), so doing the two all-to-alls in itsrunwould leave each DiT only the boundary scatter and gather. Outside this PR: at c=4, two of the first four queued requests wait out the whole backlog. That also happens on one GPU and on #948's base, so it is the scheduler's service order, not the CFG or SP axis.AI assistance (Claude) was used for this PR.
🤖 Generated with Claude Code