[TRTLLM-15917][feat] Integrate Sol-Attn sparse attention into VisualGen - #18329
Draft
karljang wants to merge 1 commit into
Draft
[TRTLLM-15917][feat] Integrate Sol-Attn sparse attention into VisualGen#18329karljang wants to merge 1 commit into
karljang wants to merge 1 commit into
Conversation
4 tasks
karljang
force-pushed
the
feat/sol-attn-visualgen-reduced
branch
5 times, most recently
from
August 31, 2026 21:56
befb56d to
34d16d7
Compare
Adds Sol-Attn (arXiv:2607.24027) as a third sparse-attention algorithm for VisualGen, alongside `skip_softmax` and VSA. It folds dynamic block routing, sparse computation, and an approximation-correction term into a single online-softmax pass. Config surface: `SolAttnAttentionConfig` in `visual_gen/args.py` / `sparse_attention.py` -- `tau` (routing threshold), `thresh_type` (`diag`/`exact`), `kv_splits`, `disabled_until_timestep` (dense-prefix cutoff), and `dense_layers` (comma/range layer-skip spec). Dispatch goes through `create_attention` the same way `skip_softmax` and `vsa` do. Cross-attention (`SEPARATE_QKV`) falls back to VANILLA, and context-parallel (`cp_size > 1`) and quantized attention are both rejected, mirroring VSA's existing guards. Dense prefix ------------ `disabled_until_timestep` follows skip-softmax's field of the same name and the same sense: the layer runs dense while the normalized denoising timestep is at or above the cutoff, and switches to the sparse kernel below it. The value arrives as a forward kwarg, which `modules/attention.py` already threads to every backend and every VisualGen pipeline normalizes by `num_train_timesteps`, so no per-pipeline wiring is needed and there is no process-wide state. `models/wan/pipeline_wan.py` is untouched. Because the prefix swaps kernels without changing tensor shapes, the two phases must not share a captured CUDA graph; `register_cuda_graph_extra_key_fns` registers `sol_attn_phase` from the same `kwargs["timestep"]` source as `skip_softmax_phase`. `dense_layers` needs no key, being fixed per layer at construction. Kernel scope ------------ The kernel is vendored from its reference implementation (see `cute_dsl_kernels/blackwell/sol_attn/THIRD_PARTY_NOTICES.md` for the upstream pin and its currency check). Only the two architectures with hardware evidence are carried: sm100 (B200/GB200) and sm120 (RTX Blackwell). Upstream's sm89 and sm90 kernels and its Triton reference path are not included; sm90 covers H100/H200/GH200 and should return in a follow-up with measurements behind it rather than ship unvalidated. Every vendored file carries an SPDX Apache-2.0 header naming its NVlabs/Sana origin; the two files that derive from FlashAttention additionally cite BSD-3-Clause and point at `sm100/LICENSE.flash-attention`, and the cuDNN Frontend license the SM120 kernel adapts is vendored at `sm120/LICENSE.cudnn-frontend` at the commit the notices cite. Upstream also vendors a copy of FlashAttention's CuTe DSL helpers. That copy is not carried: TensorRT-LLM already depends on flash-attn-4, which provides the same `flash_attn.cute` modules, verified on B200 to give bit-identical output. `preprocess.py` implements the routing/threshold stage in Triton, so Triton is a required runtime dependency on every Sol-Attn path. Failure behaviour ----------------- Inputs the kernel cannot serve -- unsupported architecture, `head_dim` other than 128, non-bf16 dtype, or mismatched k/v -- fall back to dense SDPA with a `warning_once` naming the specific reason, and increment `dense_fallback_calls` alongside `kernel_calls`. Kernel exceptions take the same path. `SOL_ATTN_STRICT=1` raises instead, for both arms. Without this the feature degrades to a silent no-op for a whole run and surfaces only as absent speedup. Docs ---- `docs/source/visual-gen/features/sparse-attention.md` gains a `sol_attn` row and a section covering the YAML surface, the sm100/sm120 + head_dim=128 + bf16 + MHA constraints, the cutoff semantics, and the fallback/`SOL_ATTN_STRICT` behaviour. Its claim that VSA is the only CUTEDSL algorithm mutually exclusive with quantized attention is corrected, since Sol-Attn now is too. Tests ----- New `tests/unittest/_torch/visual_gen/test_attention_cute_dsl_sol_attn.py`, registered in `l0_b200.yml` (sm100) and `l0_gb202.yml` (sm120): backend-factory dispatch, cross-attention VANILLA fallback, context-parallel and quantized-attention rejection, GQA/MQA rejection, the `dense_layers` guard, dense-prefix phase semantics at and either side of the cutoff (including tensor-valued timesteps), fail-open on a missing timestep, both CUDA-graph key cases, kernel-eligibility reasons, `SOL_ATTN_STRICT` on the eligibility path, dense-fallback numerics and counters, arch-list drift between `SUPPORTED_ARCHS` and `_CUTE_BACKENDS`, and `kv_splits` rejection. 32 tests plus one documented skip for GPU kernel-vs-dense equivalence at full routing. Validation ---------- * B200 (sm100): 31/31 pass, and 68 passed alongside `test_attention_cute_dsl.py`, which NVIDIA#17781 extended. Kernel output bit-identical across a 12-point (shape, tau) sweep; `kernel_calls=12`, `dense_fallback_calls=0` under `SOL_ATTN_STRICT=1`. Denoise time on B200, 50 steps, mean of 2 reps after 1 warmup, against a dense CuTeDSL baseline: Wan2.2-TI2V-5B 1.127x without CUDA graphs and 1.200x with them; Wan2.2-T2V-A14B 1.451x without and 1.406x with. Enabling graphs helps the 5B and slightly hurts A14B; the cause is not established, so the best A14B configuration remains graphs-off. Run-to-run spread was under 0.06% throughout. * RTX 5090 (sm120): resolves to `cute_sm120`; 9/9 sweep points ran with no dense fallback. End-to-end generation was not possible on that GPU because 32 GB is insufficient for the models used here, so sm120 has kernel-level evidence only. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Kanghwan Jang <861393+karljang@users.noreply.github.com>
karljang
force-pushed
the
feat/sol-attn-visualgen-reduced
branch
from
August 31, 2026 22:14
34d16d7 to
60ef12a
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.
Description
Adds Sol-Attn (arXiv:2607.24027) as a third sparse-attention algorithm for VisualGen, alongside
skip_softmaxand VSA. It folds dynamic block routing, sparse computation, and an approximation-correction term into a single online-softmax pass.Config surface.
SolAttnAttentionConfig(tensorrt_llm/visual_gen/args.py,tensorrt_llm/visual_gen/sparse_attention.py):tauthresh_typediag/exactthreshold policykv_splits1on the shipped architecturesdisabled_until_timestept >= cutoffdense_layers0,2-4disabled_until_timestepdeliberately mirrors skip-softmax's field of the same name and sense: the layer runs dense while the normalized timestep is at or above the cutoff, and switches to the sparse kernel below it. The value arrives as a forward kwarg thattensorrt_llm/_torch/visual_gen/modules/attention.pyalready threads to every backend and every VisualGen pipeline normalizes bynum_train_timesteps, so there is no per-pipeline wiring and no process-wide state —tensorrt_llm/_torch/visual_gen/models/wan/pipeline_wan.pyis untouched. Dispatch goes throughcreate_attentionexactly asskip_softmaxandvsado. Cross-attention (SEPARATE_QKV) falls back to VANILLA; context-parallel (cp_size > 1) andquant_attention_configare both rejected, mirroring VSA's existing guards — Sol-Attn replaces the dense CuTeDSL path, so a quantized-attention request would otherwise be silently ignored.Kernel scope, and what is deliberately not here
The kernel is vendored from its reference implementation;
tensorrt_llm/_torch/visual_gen/cute_dsl_kernels/blackwell/sol_attn/THIRD_PARTY_NOTICES.mdrecords the upstream pin, how it was reconstructed, and its currency check against the current upstream tip.Only the two architectures with hardware evidence behind them are carried: sm100 (B200/GB200) and sm120 (RTX Blackwell). Upstream's sm89 and sm90 kernels and its Triton reference attention path are not included. sm90 in particular would have added ~8,900 lines, since it pulls in 13 further FlashAttention helper files plus a shim standing in for upstream's
quackdependency. sm90 covers H100/H200/GH200 and matters — it should return in a follow-up with measurements behind it, rather than ship unvalidated here.Every vendored file carries an SPDX Apache-2.0 header naming its NVlabs/Sana origin; the two that derive from FlashAttention additionally cite BSD-3-Clause and point at
sm100/LICENSE.flash-attention, and the cuDNN Frontend license the SM120 kernel adapts is vendored atsm120/LICENSE.cudnn-frontendat the commit the notices cite.Upstream also vendors a copy of FlashAttention's CuTe DSL helpers. That copy is not carried. TensorRT-LLM already depends on
flash-attn-4(requirements.txt), which provides the sameflash_attn.cutemodules, so the kernels import them from that dependency. This was verified on B200: swapping the vendored modules for the installed ones produced bit-identical output across a 12-point (shape, tau) sweep, and afterwards no module resolved out of the vendored tree.Two things worth knowing that the code makes easy to miss:
tensorrt_llm/_torch/visual_gen/cute_dsl_kernels/blackwell/sol_attn/preprocess.pyimplements the routing/threshold stage in Triton, so Triton is a required runtime dependency on every Sol-Attn path, not only a fallback.pyproject.toml. The consequence is recorded intensorrt_llm/_torch/visual_gen/cute_dsl_kernels/blackwell/sol_attn/THIRD_PARTY_NOTICES.md: a directdiffagainst upstream now shows formatting noise (upstream wraps at ~80 columns, this repo at 100), so a future currency check should normalize both sides withruff formatbefore comparing.Failure behaviour
Inputs the kernel cannot serve — an unsupported architecture,
head_dimother than 128, a non-bf16 dtype, or mismatched k/v — fall back to dense SDPA with awarning_oncenaming the specific reason, and incrementdense_fallback_callsalongsidekernel_calls. Kernel exceptions take the same path.SOL_ATTN_STRICT=1raises instead, on both arms.Because the dense prefix swaps kernels without changing tensor shapes, the two phases must not share a captured CUDA graph:
register_cuda_graph_extra_key_fnsregisterssol_attn_phasefromkwargs.get("timestep"), structurally identical toskip_softmax_phase.dense_layersneeds no key, being fixed per layer at construction.The eligibility fallback matters because it is what silently turns Sol-Attn into a no-op for an entire run: on an unsupported GPU or a head_dim-64 model the user would otherwise see only absent speedup, with nothing in the log. The counters are the mechanism by which a reviewer can confirm a benchmark actually exercised the kernel rather than dense SDPA.
Performance (ENGINEERING_CRITERIA.md §3.2)
Denoise time on B200, 50 steps, mean of 2 repetitions after 1 warmup, measured$S = T_{base}/T_{new}$ is the speedup ratio;
$\Delta = (1 - T_{new}/T_{base}) \times 100$ is the time reduction. Baseline is
both with and without CUDA graphs.
dense CuTeDSL (not VANILLA), so the comparison isolates sparsity from the
backend choice.
Run-to-run spread was under 0.06 % on every cell (5B ±0.045 s, A14B ±0.22 s),
so the differences are well outside noise.
Enabling CUDA graphs helps 5B and slightly hurts A14B, and the reason is not$S$ from 1.127 to
established. On 5B, removing per-launch overhead lifts
1.200. On A14B the baseline is unchanged (593.92 → 593.86 s — at ~594 s of
denoise, launch overhead is negligible) while the Sol-Attn arm is 3.2 % slower
(409.34 → 422.26 s). A plausible explanation is that a non-null
disabled_until_timestepforces two captured graphs instead of one, and thatfixed cost is not amortized on the larger model — but that is a hypothesis, not
a measurement, and it should be confirmed before anyone treats graphs-on as the
recommended A14B configuration. The best A14B number remains the graphs-off
1.451×.
Accuracy (lossy work — ENGINEERING_CRITERIA.md §3.3)
Evaluated on two models with an internal matched-protection sweep, gated on worst-prompt LPIPS ≤ 0.25.$S$ is the speedup ratio $T_{base}/T_{new}$ ; $\Delta$ is time reduction $(1 - T_{new}/T_{base}) \times 100$ .
tau=2.0, disabled_until_timestep=0.9545tau=2.0, disabled_until_timestep=0.9090Compared head-to-head against$S = 1.42\times$ versus $1.09\times$ .
skip_softmaxat matched dense-prefix protection, Sol-Attn wins on both models — on A14B,skip_softmax'sThe operating points were originally tuned as step counts (10 and 12 dense steps). Replicating
tensorrt_llm/_torch/visual_gen/models/wan/pipeline_wan.py's scheduler setup — checkpoint scheduler config,set_timesteps(50), normalized bynum_train_timesteps— the equivalent cutoffs are0.9545and0.9090. Each sits at the midpoint between consecutive timesteps, so at 50 steps the dense/sparse partition is unchanged and these numbers carry over; unlike a step count, a cutoff keeps its meaning whennum_inference_stepschanges.One tuning constraint worth recording: A14B's high-noise/low-noise expert switch is at
boundary_ratio = 0.875(model_index.json), just below the 0.9090 cutoff. The sparse phase therefore begins on the high-noise expert, and lowering the cutoff much further would move the prefix boundary across that switch.These numbers were measured on the pre-reduction tree. They carry over unchanged because the reduction is bit-identical on sm100 (see below), not because they were re-derived. Full LPIPS-gated results, per-axis sweeps, and a video comparison against the dense reference exist as internal study artifacts and can be shared directly with reviewers on request.
All figures are real GPU evidence (B200, real checkpoints, no synthetic proxies), captured through an evaluation harness with its own integrity checks — backend census, LPIPS gate, denoise-spread. None of this PR's code depends on that harness.
Test Coverage
New:
tests/unittest/_torch/visual_gen/test_attention_cute_dsl_sol_attn.py, registered intests/integration/test_lists/test-db/l0_b200.yml(sm100) andtests/integration/test_lists/test-db/l0_gb202.yml(sm120) so it runs in CI on both shipped architectures. It mirrorstests/unittest/_torch/visual_gen/test_attention_cute_dsl_vsa.pyin structure and scope: backend-factory dispatch, cross-attention VANILLA fallback, context-parallelism rejection, GQA/MQA rejection, thedense_layersguard, the dense-prefix phase semantics at and either side of the cutoff (including tensor-valued timesteps, which is what pipelines actually pass), fail-open on a missing timestep, both CUDA-graph key cases, kernel-eligibility reasons,SOL_ATTN_STRICTon the eligibility path, dense-fallback numerics and counters, quantized-attention rejection, and arch-list drift betweenSUPPORTED_ARCHSand_CUTE_BACKENDS. 32 tests.Hardware validation:
tests/unittest/_torch/visual_gen/test_attention_cute_dsl.py, which [TRTLLM-15585][feat] Wire SkipSoftmax sparse attention into the CuTeDSL backend #17781 extended — a direct check that this PR's edits to the shared config and CUDA-graph-key paths did not regress that work. Measured on the pushed commit, against a working clone rebuilt on the current base. Kernel output bit-identical to the pre-reduction tree across a 12-point (shape, tau) sweep, and an end-to-end Wan2.2-TI2V-5B generation produced a byte-identical MP4, zero differing pixels.kernel_calls=12,dense_fallback_calls=0,SOL_ATTN_STRICT=1.pre-commit runis clean across all 33 changed files.cute_sm120; 9/9 sweep points ran with no dense fallback.Gap, stated plainly: sm120 has kernel-level evidence only. An end-to-end generation was not possible on the available sm120 hardware — a 5090's 32 GB cannot hold Wan2.2-TI2V-5B (OOM at 27.2 GiB during model load), and no larger sm120 node was available. The untested delta is pipeline integration, which is architecture-independent Python already covered on B200; the architecture-specific part is the kernel, and that is covered. Reviewers who consider that insufficient should say so — dropping sm120 to an sm100-only PR is a small change.
The sm120 figures below predate the rebase and were not re-measured: the local build targets
90-real;100-real, and the only available sm120 hardware (a 32 GB RTX 5090) cannot hold the models used here. The rebase did not touch the vendored kernel tree, so those results should carry over, but that is an inference rather than a measurement.One test is skipped with a documented reason: GPU kernel-vs-dense numerical equivalence at full routing (the analogue of VSA's
test_cute_kernel_matches_dense_at_full_topk). It needs the exacttau/thresh_typecombination that guarantees non-sparse routing, which is not simplytau=0because Sol-Attn's routing is score-derived rather than a plain top-k.Notes for reviewers
docs/source/visual-gen/features/sparse-attention.mdgains asol_attnrow and a section covering the YAML surface, the sm100/sm120 +head_dim=128+ bf16 + MHA constraints, the cutoff semantics, and the fallback/SOL_ATTN_STRICTbehaviour. Its claim that VSA is the only CUTEDSL algorithm mutually exclusive with quantized attention is corrected, since Sol-Attn now is too.sparse_attention_configvariant,SolAttnAttentionConfig. It is additive, soapi-compatible. Per ENGINEERING_CRITERIA.md §2.1 and §3.3, both the new public API surface and the lossy-work accuracy study need a team-sync review, not only PR approval.flash-attn-4andtritonare already inrequirements.txt; this PR consumes them rather than adding anything. The vendored subset shipsLICENSE.flash-attentionand retains upstream copyright headers; adapted files carry NVIDIA SPDX headers with attribution.AttentionConfig.cross_attention_backendoverride was developed alongside this work and is not included. It applies to all backends, is unrelated to Sol-Attn, and needs its own review — in particular whether it should be allowed to bypass the cross-attention safety fallback. It will follow as a separate PR.