Skip to content

[perf] Add FA4 CuTe backward support for VSA-256 - #1639

Merged
SolitaryThinker merged 8 commits into
hao-ai-lab:mainfrom
ita9naiwa:feat/vsa-fa4-backward-adapter
Aug 19, 2026
Merged

[perf] Add FA4 CuTe backward support for VSA-256#1639
SolitaryThinker merged 8 commits into
hao-ai-lab:mainfrom
ita9naiwa:feat/vsa-fa4-backward-adapter

Conversation

@ita9naiwa

@ita9naiwa ita9naiwa commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Purpose

Enable FA4 backward support for VSA-256 on Blackwell. Related issue/PR: #1354

Changes

  • Add FA4 CuTe VSA-256 backward by wiring _flash_attn_bwd into the existing _flash_attn_fwd path.
  • Add an autograd bridge supporting both BHSD and BSHD layouts, including variable block sizes.
  • Preserve the existing VSA metadata, dispatch, and public interfaces.
  • Add backward parity tests, extend the existing VSA benchmark, and update the README.

Test Plan

export PYTHONPATH="$PWD/fastvideo-kernel/python${PYTHONPATH:+:$PYTHONPATH}"

python -m pytest -q -s \
  fastvideo-kernel/tests/test_vsa256_forward.py \
  fastvideo-kernel/tests/test_vsa256_forward_vbs.py \
  fastvideo-kernel/tests/test_vsa256_forward_cross.py \
  fastvideo-kernel/tests/test_vsa256_triton.py \
  fastvideo-kernel/tests/test_vsa256_backward.py



# similar to WAN 14B 480p
# 480p: 39,936 tokens, 156 x 256-token blocks, top-k 20
python fastvideo-kernel/benchmarks/bench_vsa.py \
  --block_size 256 --force_triton \
  --batch_size 1 --num_heads 12 --head_dim 128 \
  --q_seq_lens 39936 --topk 20 --warmup 20 --rep 50

python fastvideo-kernel/benchmarks/bench_vsa.py \
  --block_size 256 --use_cute \
  --batch_size 1 --num_heads 12 --head_dim 128 \
  --q_seq_lens 39936 --topk 20 --warmup 20 --rep 50

# similar to WAN 14B 720p
# 720p: 92,160 tokens, 360 x 256-token blocks, top-k 45
python fastvideo-kernel/benchmarks/bench_vsa.py \
  --block_size 256 --force_triton \
  --batch_size 1 --num_heads 12 --head_dim 128 \
  --q_seq_lens 92160 --topk 45 --warmup 20 --rep 50

python fastvideo-kernel/benchmarks/bench_vsa.py \
  --block_size 256 --use_cute \
  --batch_size 1 --num_heads 12 --head_dim 128 \
  --q_seq_lens 92160 --topk 45 --warmup 20 --rep 50

Test Results

7 passed in 27.83s

VSA Block-Sparse Attention Benchmark (WRAPPER)
device: NVIDIA B300 SXM6 PC
batch=1, heads=12, head_dim=128, dtype=bf16
block_size=256
NOTE: timings include wrapper overhead (map->index + dispatch).
dispatch: forced Triton (FASTVIDEO_KERNEL_VSA_FORCE_TRITON=1)

================================================================================
q_len=39936, kv_len=39936, num_q_blocks=156, num_kv_blocks=156, topk=20
fwd(wrapper): 2.664 ms  | 471.56 TFLOPs (approx)
bwd(wrapper): 8.442 ms  | 372.04 TFLOPs (approx)

VSA Block-Sparse Attention Benchmark (WRAPPER)
device: NVIDIA B300 SXM6 PC
batch=1, heads=12, head_dim=128, dtype=bf16
block_size=256
NOTE: timings include wrapper overhead (map->index + dispatch).
dispatch: FA4 CuTe

================================================================================
q_len=39936, kv_len=39936, num_q_blocks=156, num_kv_blocks=156, topk=20
fwd(wrapper): 1.407 ms  | 893.19 TFLOPs (approx)
bwd(wrapper): 2.810 ms  | 1117.56 TFLOPs (approx)

VSA Block-Sparse Attention Benchmark (WRAPPER)
device: NVIDIA B300 SXM6 PC
batch=1, heads=12, head_dim=128, dtype=bf16
block_size=256
NOTE: timings include wrapper overhead (map->index + dispatch).
dispatch: forced Triton (FASTVIDEO_KERNEL_VSA_FORCE_TRITON=1)

================================================================================
q_len=92160, kv_len=92160, num_q_blocks=360, num_kv_blocks=360, topk=45
fwd(wrapper): 12.976 ms  | 502.70 TFLOPs (approx)
bwd(wrapper): 41.740 ms  | 390.69 TFLOPs (approx)

VSA Block-Sparse Attention Benchmark (WRAPPER)
device: NVIDIA B300 SXM6 PC
batch=1, heads=12, head_dim=128, dtype=bf16
block_size=256
NOTE: timings include wrapper overhead (map->index + dispatch).
dispatch: FA4 CuTe

================================================================================
q_len=92160, kv_len=92160, num_q_blocks=360, num_kv_blocks=360, topk=45
fwd(wrapper): 5.386 ms  | 1211.16 TFLOPs (approx)
bwd(wrapper): 14.966 ms  | 1089.64 TFLOPs (approx)

Benchmark Summary

Workload Backend Forward Backward
Wan 14B 480p-like Triton fallback 2.664 ms 8.442 ms
Wan 14B 480p-like FA4 CuTe 1.407 ms 2.810 ms
Wan 14B 720p-like Triton fallback 12.976 ms 41.740 ms
Wan 14B 720p-like FA4 CuTe 5.386 ms 14.966 ms

Compared with the Triton fallback, FA4 CuTe provides:

  • 480p-like: 3.00x faster backward
  • 720p-like: 2.79x faster backward

Checklist

  • I ran pre-commit run --all-files and fixed all issues
  • I added or updated tests for my changes
  • I updated documentation if needed
  • I considered GPU memory impact of my changes

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Welcome to FastVideo! Thanks for your first pull request.

How our CI works:

PRs run a two-tier CI system:

  1. Pre-commit — formatting (yapf), linting (ruff), type checking (mypy). Runs immediately on every PR.
  2. Fastcheck — core GPU tests (encoders, VAEs, transformers, kernels, unit tests). Runs automatically via Buildkite on relevant file changes (~10-15 min).
  3. Full Suite — integration tests, training pipelines, SSIM regression. Runs only when a reviewer adds the ready label.

Before your PR is reviewed:

  • pre-commit run --all-files passes locally
  • You've added or updated tests for your changes
  • The PR description explains what and why

If pre-commit fails, a bot comment will explain how to fix it. Fastcheck and Full Suite results appear in the Checks section below.

Useful links:

@mergify mergify Bot added the scope: kernel CUDA kernels, fastvideo-kernel label Jul 24, 2026
@mergify

mergify Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

⚠️ PR title format required

Your PR title must start with a type tag in brackets. Examples:

  • [feat] Add new model support
  • [bugfix] Fix VAE tiling corruption
  • [refactor] Restructure training pipeline
  • [perf] Optimize attention kernel
  • [ci] Update test infrastructure
  • [infra] Add activation trace hooks
  • [docs] Add inference guide
  • [misc] Clean up configs
  • [new-model] Port Flux2 to FastVideo
  • [skill] Add add-model agent skill

Valid tags: feat, feature, bugfix, fix, refactor, perf, ci, infra, doc, docs, misc, chore, kernel, new-model, skill, skills

Please update your PR title and the merge protection check will pass automatically.

@mergify

mergify Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🟠 1 of 1 protections blocking · waiting on 🤖 CI

Protection Waiting on
🟠 PR merge requirements 🤖 CI

🟠 PR merge requirements

Waiting for

  • check-success=fastcheck-passed
  • check-success=full-suite-passed
Waiting checks: fastcheck-passed, full-suite-passed.
  • check-success=fastcheck-passed
  • check-success=full-suite-passed
  • #approved-reviews-by>=1
  • check-success~=pre-commit
  • title~=(?i)^\[(feat|feature|bugfix|fix|refactor|perf|ci|doc|docs|misc|chore|kernel|new.?model|skill|skills|infra)\]

@ita9naiwa ita9naiwa changed the title [pref] Add FA4 CuTe backward support for VSA-256 [perf] Add FA4 CuTe backward support for VSA-256 Jul 24, 2026
@mergify mergify Bot added the type: perf Performance improvement label Jul 24, 2026
@ita9naiwa

Copy link
Copy Markdown
Contributor Author

@alexzms @SolitaryThinker, since this is a follow-up to #1354, would you mind taking a look when you have a chance?

@ita9naiwa

Copy link
Copy Markdown
Contributor Author

The CI failure appears to be unrelated to this PR. It comes from the existing test_backward_asymmetric_q_kv test, and this PR does not modify the varlen VSA path or its tests.

@SolitaryThinker
SolitaryThinker self-requested a review July 28, 2026 04:04
@mergify

mergify Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

This PR has merge conflicts with the base branch. Please rebase:

git fetch origin main
git rebase origin/main
# Resolve any conflicts, then:
git push --force-with-lease

…SL requirement

The VSA-256 FA4 CuTe install instructions pointed at the wrong revision and
steered users into a broken CuTe DSL:

- The README pinned `940cd9680f...` and claimed it was the revision the repo-root
  `pyproject.toml` uses. It has been `82d6441eec...` since the pin was bumped, so
  anyone following the README installed a different FA4 than CI and the Docker
  image run.
- The README told users to `pip install "nvidia-cutlass-dsl>=4.5.0"` first. The
  pinned revision requires `nvidia-cutlass-dsl==4.6.0.dev0`; installing
  `flash-attn-4` under its distribution name resolves that correctly on its own,
  so the manual floor only creates a chance to end up on 4.5.x.
- A CuTe DSL mismatch surfaces only when the kernel JIT-compiles, as errors from
  inside CuTe (`fmax() missing 1 required positional argument`,
  `cute.core has no attribute 'ThrMma'`, `cannot import name
  'alloc_reserved_mbarrier'`), none of which point back at the install. The
  README now maps each symptom to its cause.

Verified on Blackwell (sm_100, GB200). Running the documented install end to end
resolves nvidia-cutlass-dsl 4.6.0.dev0 and quack-kernels 0.5.3, and against that
environment all 67 VSA kernel tests pass, including `tests/test_vsa256_forward*.py`,
`tests/test_vsa256_triton.py` and `tests/test_vsa256_backward.py`. Substituting
nvidia-cutlass-dsl 4.5.2 fails the same forward tests at JIT time, which is how
each row of the symptom table was observed.

Docs only; no runtime code changes.
Resolves the yapf-formatting conflicts introduced by hao-ai-lab#1700 / #6eb95693 by
keeping this branch's logic and adopting main's formatting. No behaviour change.
Follow-ups found while reproducing this PR on GB200 (sm_100).

video_sparse_attn_h3.py composed the compression branch onto the attention
output with an in-place addcmul_. On the CuTe backend that output *is* the
tensor FA4's autograd node saved for its backward, so the moment this path
has a backward at all, training dies with "one of the variables needed for
gradient computation has been modified by an inplace operation ... output 0
of FlashAttnFuncBackward". Same defect the BSHD branch of
video_sparse_attn_bshd already fixed here; H3 landed after this branch's base
and so never got it. Now out-of-place.

The KV-owned backward metadata was built unconditionally. It is a pair of
dense [B, H, kv_blocks, q_blocks] int32 index tensors that FA4 keeps alive on
its ctx until backward runs, and inference never reads it. Gated on
requires_grad: at Wan-14B 720p shape (B=1, H=40, D=128, S=92160, topk 45/360)
a no_grad forward drops from 17.23 ms / 1998 MiB transient to 15.92 ms /
1918 MiB. Training keeps the metadata and is unchanged.

The aux (lse) return was transposed to [B, S, H]. FA4 hands back [B, H, S]
already (interface.py builds lse_shape as (batch, num_head, seqlen_q) when qv
is None), which is what the Triton path's aux contract is, so the transpose
made the two backends disagree and cost a 14 MiB fp32 copy per call at the
shape above. Dropped, and detach happens first now.

Tests
- gated compression branch (compress_attn_weight is not None) backward: the
  branch Wan and H3 actually run, and the one the in-place bug lives in.
- partially filled Q tiles, and q_len != kv_len; forward had cross coverage,
  backward had none.
- inference forward is bitwise identical to the training forward, pinning the
  requires_grad gate.
- aux is [B, H, S] on both entrypoints.
- fastvideo/tests/attention/test_vsa_h3_backward.py covers the H3 backend on
  both backends and cross-checks CuTe gradients against Triton.
- Gradient tolerances tightened from avg_abs 2e-2 / max_rel 0.5 to 1e-3 /
  0.25; measured error across every case above is <= 1.2e-4 and <= 0.11.

test_vsa_varlen.py seeds the RNG now. It draws every tensor and every
variable block size from the global RNG and then asserts max_rel < 0.05, so
adding any test that runs before it shifts its inputs and it fails on unlucky
data. That is the "unrelated" CI failure reported on this PR: it reproduces
in a full-suite run and passes in isolation.

Verified on GB200 (sm_100), FA4 82d6441 + nvidia-cutlass-dsl 4.6.0.dev0 +
quack-kernels 0.5.3: fastvideo-kernel/tests/ and fastvideo/tests/attention/
give 318 passed, stable across repeated runs. The one remaining failure,
test_fa4_quantize_op_fake_matches_real, fails identically on an unmodified
main. Reverting either fix above turns the matching new test red.

pre-commit (yapf, ruff, codespell) passes on every changed file; the mypy
hook only errors on the worktree's directory name, and does so identically on
an unmodified main.
@alexzms

alexzms commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Thank you for this — and welcome. This is a genuinely well-executed piece of work, and it reproduces cleanly: on GB200 (sm_100) I measure 3.06x / 2.76x backward over the Triton fallback at your 480p / 720p shapes, right in line with your B300 numbers.

A few things I want to call out specifically, because they are the parts that are easy to get wrong and you got right. Delegating the autograd bridge to FA4's public flash_attn_func rather than hand-wiring _flash_attn_bwd is the more robust choice, and it keeps us honest against upstream. Working out that FA4's backward is KV-owned, and transposing the metadata to match while keeping full and partial KV tiles separate so the token-level mask only runs on padded tiles, took real reading of block_sparsity.py — it lines up exactly with what normalize_block_sparse_config_bwd expects. And the out-of-place change in video_sparse_attn_bshd is load-bearing rather than cosmetic: revert just that hunk and the BSHD case fails with modified by an inplace operation ... output 0 of FlashAttnFuncBackward. Nice catch. The three commits reverting unrelated formatting churn to keep the diff honest were appreciated too.

I've pushed three commits on top rather than round-tripping them to you:

  1. Docs — the kernel README pinned 940cd968 (pyproject has been on 82d6441e) and told you to pre-install nvidia-cutlass-dsl>=4.5.0, which leaves you on 4.5.x and then dies at JIT time with fmax() missing 1 required positional argument. Unrelated to your change; just what tripped me up reproducing yours.
  2. Merge of main — conflicts were only yapf churn from [misc]: re-run yapf on main so pre-commit passes again #1700 / #6eb95693.
  3. Two fixes + testsvideo_sparse_attn_h3.py has the same in-place pattern on FA4's saved output that you fixed in video_sparse_attn_bshd, and it breaks the moment this lands. That file arrived after your base, so nothing you did. And the KV-owned backward metadata is now gated on requires_grad: inference never reads it, and at Wan-14B 720p shape a no_grad forward goes from 17.23 ms / 1998 MiB to 15.92 ms / 1918 MiB. Tests added for the gated compression branch, partial Q tiles, q_len != kv_len, and the H3 backend.

One more thing worth knowing, since you flagged it: the test_backward_asymmetric_q_kv failure is real and reproducible, but you were right that it is not yours. test_vsa_varlen.py draws all its data from the global RNG and then asserts max_rel < 0.05, so any test added ahead of it shifts its inputs — red in a full-suite run, green in isolation. Seeded it in the same commit.

318 passed locally on GB200; the one remaining failure fails identically on an unmodified main. Merging once CI is green — thanks again for taking this on.

@mergify mergify Bot added scope: attention Attention backends (VSA, STA, Flash, etc.) scope: infra CI, tests, Docker, build labels Aug 17, 2026
@alexzms alexzms added the ready PR is ready to merge label Aug 17, 2026
@alexzms
alexzms force-pushed the feat/vsa-fa4-backward-adapter branch from a1c75bd to b565da4 Compare August 17, 2026 21:41
@mergify mergify Bot removed the needs-rebase PR has merge conflicts label Aug 17, 2026

@alexzms alexzms left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified on GB200 (sm_100), the only arch that actually executes this path: 324 passed, CuTe-vs-Triton parity across 128 shape/head/batch/dtype combinations, and backward at 2.6-3.0x over the Triton fallback (61-66% of measured dense bf16 peak). H3 dispatch confirmed end to end with FASTVIDEO_VSA_CUTEDSL=1.

One deployment note before this goes out: the FA4_CUTE_REF bump in docker/Dockerfile means the published fastvideo-dev image keeps the old FA4 until it is rebuilt from main. VSA-128 backward needs the new pin — the old revision only reads block_size[0] on sm_90, so on sm_100 q_subtile_factor comes out as 2 and the 128 metadata is rejected. VSA-256 works on either pin, so H3 is fine on the current image; just don't expect the 128 path to work in a container until the rebuild lands.

@SolitaryThinker
SolitaryThinker merged commit 00338aa into hao-ai-lab:main Aug 19, 2026
3 of 5 checks passed
SolitaryThinker pushed a commit that referenced this pull request Aug 19, 2026
…ling (bf16 K pre-scaling)

The backward pre-scaled K by sm_scale*log2(e) in bf16 before recomputing
logits, so the recompute drifted from the forward's saved LSE
proportionally to |logit|; exp2 amplifies that into exponentially wrong
probabilities and garbage dK/dV (plus a mis-scaled dQ) at real
activation magnitudes. The forward is exact and unit-scale tests passed,
which is how it survived — and the Triton backward is the default
training path on every GPU except sm_90 with the compiled extension
(the FA4 CuTe backward from #1639 is opt-in and sm_100-only).

Fix: pass raw K, apply sm_scale*RCP_LN2 in fp32 after tl.dot in both
_attn_bwd_dkdv and _attn_bwd_dq, and scale dq by sm_scale instead of
LN2, matching the forward's rounding exactly.

Adds a seeded regression test sweeping input scale against an fp32
masked-dense SDPA reference: on GB200 the unfixed kernel passes at unit
scale but fails at scale=16 (dq rel L2 err 5.9e-1); the fixed kernel
passes all scales (rel err <= 6.9e-3, grad-norm ratios 0.9995-1.0001).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready PR is ready to merge scope: attention Attention backends (VSA, STA, Flash, etc.) scope: infra CI, tests, Docker, build scope: kernel CUDA kernels, fastvideo-kernel type: perf Performance improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants