Repository navigation
[Perf] diffusion: Cut LTX-2 denoising-step host overhead - #45966
Merged
Merged
Conversation
Every LTX-2 denoising step moved scheduler.sigmas[i] and [i+1] to the device and called .item() on both, and the stage-1 guider path called .item() on sigma again after the forward. Each read blocks the host until the stream drains, so the host can never enqueue the next step while the GPU runs this one -- the bubble that remains once the forward itself is graph-replayed. Stage 2 also paid a pageable H2D copy per sigma, since its distilled schedule lives on the CPU. Take fp32 host and device copies of the schedule once per loop: steps index the device copy for tensor math and the host copy for Python scalars. The guider's x0 conversion takes the device sigma instead of its float; both scale in fp32, so outputs are bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LTX-2 passes the 0/1 prompt key mask down to every block, and each block's two prompt cross-attentions (LocalAttention) turned it into the additive SDPA mask again: a cast, a subtract and a multiply per call, 288 tiny launches per forward over 48 blocks. Convert it once in the model forward with the helper LocalAttention now shares (prepare_sdpa_mask, previously private to the USP paths), and let the blocks pass the float mask through. Same ops on the same inputs, so outputs are bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
mickqian
requested review from
AgainstEntropy,
BBuf,
HaiShaw,
kevin-mii,
niehen6174,
ping1jing2 and
yichiche
as code owners
October 23, 2026 04:26
Collaborator
Author
|
/tag-and-rerun-ci |
This was referenced Oct 23, 2026
Merged
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.
Motivation
Every LTX-2 denoising step pays two host-side costs.
scheduler.sigmas[i]and[i+1]to the device and called.item()on both. The stage-1 guider path called.item()on sigma again after the forward. Each read blocks the host until the stream drains, so the host cannot enqueue the next step while the GPU runs the current one. Stage 2 also paid a pageable H2D copy per sigma, because its distilled schedule lives on the CPU. Once the forward is graph-replayed (breakable CUDA graphs), this sync is the bubble that remains. Removing it lets the host run ahead.LocalAttention) turned it into the additive SDPA mask again: a cast, a subtract and a multiply per call. That is 288 small launches per forward over 48 blocks.Modifications
ltx_2/denoising.py:_prepare_denoising_looptakes fp32 host and device copies of the sigma schedule once per loop (LTX2DenoisingContext.sigmas_host/sigmas_device). Steps index the device copy for tensor math and the host copy for Python scalars. The guider's x0 conversion takes the device sigma instead of its Python float._ltx2_velocity_to_x0scales in fp32 either way.layers/attention/layer.py:_prepare_sdpa_maskis renamedprepare_sdpa_mask.LocalAttentionnow calls it instead of keeping its own inline copy of the same conversion. The values are the same for float and 0/1 masks.models/dits/ltx_2.py: the model forward converts the video and audio prompt masks once withprepare_sdpa_mask, and the blocks pass the float mask through.LocalAttentiononly casts and reshapes a float mask. Prompt cross-attention is alwaysLocalAttention, and the prompt mask never goes through the SP K/V gather.Tests:
test_ltx2_bcg_coords.py,test_steps_never_read_device_sigmas_on_host: runs one denoising step in each guidance mode (official CFG, batched guider, TI2V, HQ, legacy, legacy batched). The sigma schedule'sitem()/__float__/__bool__raise, and the outputs must equal a run with a plain tensor. It fails on main in every mode.test_ltx2_bcg_coords.py,test_tensor_sigma_x0_matches_python_float: the fp32 x0 conversion gives bitwise the same result for a tensor sigma and a float sigma.test_ltx2_prompt_mask.py:LocalAttentiongives bitwise the same output for a prebuilt additive mask and for the raw 0/1 mask. A 2-block LTX-2 model converts the masks exactly twice per forward (video and audio), and its output matches the per-block conversion bitwise.No files are shared with #45942, #45943, #45944 or #45948, or with "Shard LTX-2 stage 2 over the CFG ranks as Ulysses". This PR merges cleanly with all of them.
Accuracy Tests
2xH200, LTX-2 two-stage,
defaultprofile. Decoded frames and audio are bitwise identical across base, the sigma commit and the prompt-mask commit, in every round.Speed Tests and Profiling
2xH200, LTX-2 two-stage,
default(eager) profile. Three interleaved rounds, e2e median per run:Checklist
🤖 Generated with Claude Code
CI States
Latest PR Test (Base): ✅ Run #39908805023
Latest PR Test (Extra): ❌ Run #39908805019
Latest PR Test (AMD ROCm 10): ❌ Run #39908805036