Skip to content

Commit eed2e2f

Browse files
SolitaryThinkerMister-Raggs
authored andcommitted
[bugfix]: harden SM121 Attn-QAT support
1 parent 3a3bbc9 commit eed2e2f

9 files changed

Lines changed: 201 additions & 68 deletions

File tree

.github/workflows/publish-kernel.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868
- os: ubuntu-22.04
6969
arch: x86_64
7070
wheel-plat: manylinux_2_35_x86_64
71-
# aarch64 is Blackwell (GB200 sm_100a + DGX Spark / consumer sm_120a), not
71+
# aarch64 is Blackwell (GB200 sm_100a + sm_120a + DGX Spark sm_121a), not
7272
# Hopper, and Blackwell needs CUDA >= 12.8 — so only the cu130 leg applies.
7373
# Added via include so x86 keeps cu126 + cu130 while aarch64 stays cu130-only.
7474
include:
@@ -164,16 +164,17 @@ jobs:
164164
cd fastvideo-kernel
165165
git submodule update --init --recursive # Ensure ThunderKittens submodule is initialized
166166
# Release builds run on GPU-less runners, so set kernels + arch explicitly:
167-
# * aarch64 = Blackwell (GB200 sm_100a + DGX Spark/consumer sm_120a), NOT
168-
# Hopper, so TK (sm_90a wgmma) is OFF. The C++ FP4 (attn_qat_infer, SM120)
169-
# covers sm_120a; turbodiffusion covers sm_100a+sm_120a. The sm_100 FP4
170-
# forward is the FA4 CuTe DSL path in the fastvideo package (PR #1221),
167+
# * aarch64 = Blackwell (GB200 sm_100a + sm_120a + DGX Spark sm_121a), NOT
168+
# Hopper, so TK (sm_90a wgmma) is OFF. The C++ FP4 (attn_qat_infer)
169+
# covers sm_120a+sm_121a; turbodiffusion covers every listed arch. The
170+
# sm_100 FP4 forward is the FA4 CuTe DSL path in the fastvideo package (PR #1221),
171171
# JIT-compiled at runtime — not built into this wheel.
172172
# * x86_64 cu130 = Hopper TK + data-center Blackwell sm_100a VSA
173173
# + consumer Blackwell sm_120a FP4.
174174
# * x86_64 cu126 = Hopper TK only (older drivers; CUDA < 12.8 has no FP4).
175-
# The per-arch split in CMakeLists pins the FP4 targets to sm_120a and builds
176-
# the main extension for the full arch list. CMAKE_BUILD_PARALLEL_LEVEL caps
175+
# The per-arch split in CMakeLists pins the FP4 targets to requested
176+
# sm_120a/sm_121a and builds the main extension for the full arch list.
177+
# CMAKE_BUILD_PARALLEL_LEVEL caps
177178
# Ninja so heavy CUTLASS/TK template TUs don't OOM the 16 GB runner (exit 143).
178179
if [ "${{ matrix.platform.arch }}" = "aarch64" ]; then
179180
export TORCH_CUDA_ARCH_LIST="10.0a;12.0a;12.1a"
@@ -250,7 +251,8 @@ jobs:
250251
- name: Download PyPI wheels
251252
# Publish the cu130 (CUDA 13) wheels to PyPI for both architectures:
252253
# x86_64 — Hopper sm_90a TK + consumer Blackwell sm_120a FP4
253-
# aarch64 — Blackwell: turbodiffusion (sm_100a/sm_120a) + C++ FP4 (sm_120a);
254+
# aarch64 — Blackwell: turbodiffusion (sm_100a/sm_120a/sm_121a)
255+
# + C++ FP4 (sm_120a/sm_121a);
254256
# no TK (Hopper). sm_100 FP4 forward is the FA4 CuTe DSL path in the
255257
# fastvideo package (#1221), shipped/JIT separately.
256258
# The x86_64 cu126 wheel stays available as a build artifact / GitHub-release asset.

docs/getting_started/installation/spark.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ With no GPU visible the kernel build can't probe the arch and `auto` can't detec
5858
the driver — name both explicitly:
5959

6060
```bash
61-
UV_TORCH_BACKEND=cu130 TORCH_CUDA_ARCH_LIST=12.1 uv pip install -e .
61+
UV_TORCH_BACKEND=cu130 TORCH_CUDA_ARCH_LIST=12.1a uv pip install -e .
6262
```
6363

6464
## Verify the install
@@ -128,8 +128,8 @@ uv pip install "https://github.com/mjun0812/flash-attention-prebuild-wheels/rele
128128
|---|---|
129129
| `Could NOT find Python (missing: ... Development.Module)` | venv built from system Python without headers. Recreate with `--python-preference only-managed` (add `--clear` to reuse the path), or `sudo apt install python3.12-dev`. |
130130
| kernel build can't find cutlass headers | Submodules not initialised — run the `git submodule update` step. |
131-
| `fastvideo-kernel: could not determine the target CUDA architecture` | The build couldn't see a GPU and no arch was given. Build on the Spark itself, or pass `TORCH_CUDA_ARCH_LIST=12.1` (see [Building without a visible GPU](#building-without-a-visible-gpu-ci--docker)). |
132-
| `nvcc fatal: Unsupported gpu architecture 'compute_121'` | `nvcc` older than CUDA 12.9/13. Confirm `nvcc --version` is 13.x and `CUDACXX=/usr/local/cuda/bin/nvcc`. |
131+
| `fastvideo-kernel: could not determine the target CUDA architecture` | The build couldn't see a GPU and no arch was given. Build on the Spark itself, or pass `TORCH_CUDA_ARCH_LIST=12.1a` (see [Building without a visible GPU](#building-without-a-visible-gpu-ci--docker)). |
132+
| `sm_121a requires CUDA Toolkit 13.0+` | Confirm `nvcc --version` is 13.x and `CUDACXX=/usr/local/cuda/bin/nvcc`; architecture-specific sm_120a code cannot run on sm_121. |
133133
| `ninja: command not found` (manual build only) | `uv pip install scikit-build-core cmake ninja setuptools wheel`. |
134134

135135
If you hit other issues, please open an issue on our

docs/inference/optimizations.md

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -184,20 +184,19 @@ gen.generate_video(prompt="A raccoon in sunflowers", save_video=True)
184184
- Per-call cosine similarity vs BF16: ~0.99 (slight quantization error accumulates over denoising steps)
185185
- Only supports `headdim >= 128`
186186

187-
### NVFP4 + Attn-QAT (modified SageAttention3, Blackwell sm_120a/sm_121a)
187+
### NVFP4 Attn-QAT attention (modified SageAttention3, Blackwell sm_120a/sm_121a)
188188

189-
**`ATTN_QAT_INFER`** with **`transformer_quant=nvfp4_qat`**
189+
**`ATTN_QAT_INFER`**
190190

191-
Runs the DiT fully in 4-bit: NVFP4 linear layers (activations quantized on the
192-
fly) plus the modified SageAttention3 FP4 attention backend. This is the
193-
inference half of the Quantization-Aware Distillation (QAD) recipe and the path
194-
used for the RTX 5090 release.
191+
Quantizes Q/K/V inside the modified SageAttention3 FP4 attention backend. On
192+
DGX Spark, keep the transformer linear layers in BF16: the separate
193+
`transformer_quant=nvfp4_qat` linear path is not supported on sm_121 yet.
195194

196195
The `attn_qat_infer` kernel targets consumer/workstation Blackwell — **sm_120a
197196
(RTX 5090 / PRO 6000)** and **sm_121a (DGX Spark GB10)**; the block-scaled FP4
198197
MMA is numerically correct on both (GB10-verified: cos ~0.98 vs bf16 SDPA).
199-
sm_121a support needs a CUDA 13 build (`TORCH_CUDA_ARCH_LIST=12.1a`). On other
200-
GPUs the backend logs a notice and falls back to Flash Attention.
198+
sm_121a support needs a CUDA 13 build (`TORCH_CUDA_ARCH_LIST=12.1a`). A runtime
199+
capability gate falls back to Flash Attention on other GPUs.
201200

202201
> **Quality note (stock weights):** on stock Wan-2.1 the FP4-attention output is
203202
> below bf16 — QAD expects a QAT-distilled checkpoint the model was *trained* to
@@ -206,26 +205,24 @@ GPUs the backend logs a notice and falls back to Flash Attention.
206205
207206
See the [Attn-QAT paper](https://arxiv.org/abs/2603.00040).
208207

209-
Enable both halves — attention via the env var, linear via `transformer_quant`:
208+
Enable FP4 attention with BF16 transformer linears on DGX Spark:
210209

211210
```python
212211
import os
213212
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "ATTN_QAT_INFER"
214213

215214
from fastvideo import VideoGenerator
216-
from fastvideo.layers.quantization import get_quantization_config
217215
gen = VideoGenerator.from_pretrained(
218216
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
219217
num_gpus=1,
220-
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific). Pass an
221-
# instance — the bare string is not resolved on the from_pretrained path.
222-
transformer_quant=get_quantization_config("nvfp4_qat")(),
223-
use_fsdp_inference=False, # FSDP shards invalidate the FP4 tensor pointers
218+
use_fsdp_inference=False,
224219
)
225220
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
226221
```
227222

228-
Or run the example script:
223+
The full QAD configuration, pairing this attention backend with
224+
`transformer_quant=nvfp4_qat`, is currently limited to sm_120. Its RTX 5090
225+
example is:
229226

230227
```bash
231228
python examples/inference/optimizations/nvfp4_qat_wan2_1_1_3b.py

fastvideo-kernel/CMakeLists.txt

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
4444
# ---------------------------------------------------------------------------
4545
if(NOT GPU_BACKEND STREQUAL "ROCM")
4646
if(DEFINED ENV{TORCH_CUDA_ARCH_LIST})
47+
set(TORCH_CUDA_ARCH_LIST "$ENV{TORCH_CUDA_ARCH_LIST}")
4748
message(STATUS "CUDA arch: TORCH_CUDA_ARCH_LIST=$ENV{TORCH_CUDA_ARCH_LIST} (from environment)")
4849
elseif(TORCH_CUDA_ARCH_LIST)
4950
set(ENV{TORCH_CUDA_ARCH_LIST} "${TORCH_CUDA_ARCH_LIST}")
@@ -52,14 +53,14 @@ if(NOT GPU_BACKEND STREQUAL "ROCM")
5253
set(_FV_ARCH_LIST "")
5354
if(_FASTVIDEO_USER_CUDA_ARCH)
5455
# Caller pinned -DCMAKE_CUDA_ARCHITECTURES (which torch ignores); translate it
55-
# to the TORCH_CUDA_ARCH_LIST spelling: "121" -> "12.1", "90a" -> "9.0a".
56+
# to the TORCH_CUDA_ARCH_LIST spelling: "121a" -> "12.1a", "90a" -> "9.0a".
5657
# Only numeric spellings translate; keywords like "native"/"all" would
5758
# otherwise be mangled into nonsense ("nativ.e").
5859
foreach(_fv_arch IN LISTS _FASTVIDEO_USER_CUDA_ARCH)
5960
if(NOT _fv_arch MATCHES "^[0-9]+[af]?$")
6061
message(FATAL_ERROR
6162
"fastvideo-kernel: CMAKE_CUDA_ARCHITECTURES='${_fv_arch}' is not "
62-
"supported. Use a numeric arch (e.g. 90a, 121), set "
63+
"supported. Use a numeric arch (e.g. 90a, 121a), set "
6364
"TORCH_CUDA_ARCH_LIST directly (e.g. 9.0a), or unset both to "
6465
"auto-detect from the visible GPU.")
6566
endif()
@@ -93,14 +94,26 @@ if(NOT GPU_BACKEND STREQUAL "ROCM")
9394
"fastvideo-kernel: could not determine the target CUDA architecture.\n"
9495
"Refusing to let torch auto-detect an arch that may not run on this GPU. "
9596
"Fix with one of:\n"
96-
" - set TORCH_CUDA_ARCH_LIST (e.g. 12.1, or 9.0a for Hopper), or\n"
97-
" - pass -DCMAKE_CUDA_ARCHITECTURES=<arch> (e.g. 121), or\n"
97+
" - set TORCH_CUDA_ARCH_LIST (e.g. 12.1a, or 9.0a for Hopper), or\n"
98+
" - pass -DCMAKE_CUDA_ARCHITECTURES=<arch> (e.g. 121a), or\n"
9899
" - build where the target GPU is visible to torch.\n"
99100
"Note: 'pip/uv pip install' builds under build isolation, which hides the "
100101
"GPU; set TORCH_CUDA_ARCH_LIST or add --no-build-isolation. "
101102
"fastvideo-kernel/build.sh sets all of this for you.")
102103
endif()
103104
endif()
105+
106+
string(REGEX MATCH "(^|[; ,])((12\\.0a)|(120a)|(sm_120a))([; ,]|$)"
107+
_FV_HAS_120A "${TORCH_CUDA_ARCH_LIST}")
108+
string(REGEX MATCH "(^|[; ,])((12\\.1a)|(121a)|(sm_121a))([; ,]|$)"
109+
_FV_HAS_121A "${TORCH_CUDA_ARCH_LIST}")
110+
if(_FV_HAS_121A AND CUDAToolkit_VERSION VERSION_LESS 13.0)
111+
message(FATAL_ERROR
112+
"fastvideo-kernel: sm_121a requires CUDA Toolkit 13.0+, but found "
113+
"${CUDAToolkit_VERSION}. Architecture-specific sm_120a code is not "
114+
"compatible with sm_121; use CUDA 13 or remove 12.1a from "
115+
"TORCH_CUDA_ARCH_LIST.")
116+
endif()
104117
endif()
105118

106119
# Robustly find Torch include paths using Python
@@ -268,13 +281,7 @@ else()
268281
set(_WANTS_ATTN_QAT_INFER ON)
269282
elseif(FASTVIDEO_KERNEL_BUILD_ATTN_QAT_INFER STREQUAL "AUTO")
270283
if(TORCH_CUDA_ARCH_LIST)
271-
# Consumer/workstation Blackwell: sm_120a (RTX 5090 / PRO 6000) and
272-
# sm_121a (DGX Spark GB10). The block-scaled FP4 MMA is valid on both
273-
# (GB10-verified: cos ~0.98 vs bf16 SDPA across shapes).
274-
string(REGEX MATCH
275-
"(^|[; ,])((12\\.0a)|(120a)|(sm_120a)|(12\\.1a)|(121a)|(sm_121a))([; ,]|$)"
276-
_HAS_BLACKWELL_FP4 "${TORCH_CUDA_ARCH_LIST}")
277-
if(_HAS_BLACKWELL_FP4)
284+
if(_FV_HAS_120A OR _FV_HAS_121A)
278285
set(_WANTS_ATTN_QAT_INFER ON)
279286
endif()
280287
else()
@@ -302,6 +309,18 @@ else()
302309
endif()
303310

304311
if(ENABLE_ATTN_QAT_INFER)
312+
set(FASTVIDEO_FP4_CUDA_ARCHS "")
313+
if(_FV_HAS_120A)
314+
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "120a")
315+
endif()
316+
if(_FV_HAS_121A)
317+
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "121a")
318+
endif()
319+
if(NOT FASTVIDEO_FP4_CUDA_ARCHS)
320+
message(FATAL_ERROR
321+
"FASTVIDEO_KERNEL_BUILD_ATTN_QAT_INFER=ON requires 12.0a or "
322+
"12.1a in TORCH_CUDA_ARCH_LIST; got '${TORCH_CUDA_ARCH_LIST}'.")
323+
endif()
305324
message(STATUS "attn_qat_infer kernels: ENABLED")
306325
else()
307326
message(STATUS
@@ -333,14 +352,6 @@ if(ENABLE_ATTN_QAT_INFER AND TORCH_CUDA_ARCH_LIST)
333352
set(FASTVIDEO_MAIN_CUDA_ARCHS "${TORCH_CUDA_ARCH_LIST}")
334353
string(REPLACE "sm_" "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
335354
string(REPLACE "." "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
336-
# sm_121a needs CUDA 13's ptxas. On older toolchains, downgrade any requested
337-
# 121a to 120a for the *main* extension too (not just the fp4* targets below),
338-
# so a CUDA-12.x build with 12.1a in the arch list doesn't fail to compile —
339-
# sm_120a PTX forward-JITs to sm_121 at runtime.
340-
if(CUDAToolkit_VERSION VERSION_LESS 13.0)
341-
string(REPLACE "121a" "120a" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
342-
list(REMOVE_DUPLICATES FASTVIDEO_MAIN_CUDA_ARCHS)
343-
endif()
344355
message(STATUS "[per-arch] main extension archs=${FASTVIDEO_MAIN_CUDA_ARCHS}, fp4* archs set per-target below")
345356
endif()
346357

@@ -526,15 +537,8 @@ if(ENABLE_ATTN_QAT_INFER)
526537
$<$<COMPILE_LANGUAGE:CXX>:-O3 -std=c++17>
527538
$<$<COMPILE_LANGUAGE:CUDA>:${ATTN_QAT_INFER_CUDA_FLAGS}>
528539
)
529-
# FP4 kernels target consumer/workstation Blackwell: sm_120a (RTX 5090 /
530-
# PRO 6000) and sm_121a (DGX Spark GB10). sm_121a needs CUDA 13's ptxas
531-
# (CUDA 12.x lacks it), so it is added only on CUDA >= 13.0 — older
532-
# toolchains keep the original sm_120a-only build unchanged. sm_121a
533-
# GB10-verified (cos ~0.98 vs bf16).
534-
set(FASTVIDEO_FP4_CUDA_ARCHS "120a")
535-
if(NOT CUDAToolkit_VERSION VERSION_LESS 13.0)
536-
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "121a")
537-
endif()
540+
# Compile only the FP4 architectures requested by the caller. The large
541+
# template translation units use 8-12 GiB each, so an extra target matters.
538542
message(STATUS "fp4* CUDA architectures: ${FASTVIDEO_FP4_CUDA_ARCHS}")
539543
set_target_properties(fp4attn_cuda PROPERTIES
540544
CUDA_ARCHITECTURES "${FASTVIDEO_FP4_CUDA_ARCHS}"
@@ -576,6 +580,6 @@ message(STATUS "TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}")
576580
message(STATUS "fastvideo_kernel_ops: ON (turbodiffusion int8-gemm/quant/rmsnorm/layernorm, all listed archs)")
577581
message(STATUS " + TK sta/block_sparse (sm_90a only): ${ENABLE_TK_KERNELS}")
578582
message(STATUS " + Ulysses NCCL-device all-to-all: ${ENABLE_ULYSSES_A2A}")
579-
message(STATUS "fp4attn/fp4quant (sm_120a/sm_121a, CUDA >= 12.8): ${ENABLE_ATTN_QAT_INFER}")
583+
message(STATUS "fp4attn/fp4quant (sm_120a CUDA >= 12.8; sm_121a CUDA >= 13): ${ENABLE_ATTN_QAT_INFER}")
580584
message(STATUS "Triton fallbacks ship in python/fastvideo_kernel/triton_kernels regardless.")
581585
message(STATUS "============================================================")

fastvideo-kernel/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Runtime-JIT kernels (no build step, ship in every wheel/image):
3232
| Docker images `ghcr.io/hao-ai-lab/fastvideo/fastvideo-dev` (`.github/workflows/infra-build-image.yml`) | `docker/Dockerfile` changes on main, or manual dispatch | amd64 cuda12.6.3 + cuda13.0.0 | `9.0a` | ON | AUTO ||
3333
| | | arm64 cuda12.6.3 (GH200) | `9.0a` | — (aarch64) | AUTO ||
3434
| | | arm64 cuda13.0.0 (GB10 / DGX Spark) | `12.1` || AUTO ||
35-
| Local `./build.sh` | manual | probes the visible GPU via torch | detected | ON iff sm_90 (non-aarch64 host) | AUTO | ON iff sm_120 |
35+
| Local `./build.sh` | manual | probes the visible GPU via torch | detected | ON iff sm_90 (non-aarch64 host) | AUTO | ON iff sm_120 or sm_121 |
3636

3737
Notes:
3838

fastvideo-kernel/build.sh

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,34 @@ if [ "${GPU_BACKEND}" = "CUDA" ]; then
137137

138138
# Respect explicit overrides.
139139
if [ -z "${TORCH_CUDA_ARCH_LIST:-}" ]; then
140-
if [ "${cc_major}" = "9" ] && [ "${cc_minor}" = "0" ]; then
141-
export TORCH_CUDA_ARCH_LIST="9.0a"
142-
elif [ "${cc_major}" = "12" ] && [ "${cc_minor}" = "0" ]; then
143-
# Blackwell sm_120 needs the arch-conditional 'a' suffix so CMake's
144-
# AUTO gate (matches 12.0a/120a/sm_120a) builds the attn_qat_infer
145-
# (modified SageAttention3 FP4) kernels instead of silently skipping.
146-
export TORCH_CUDA_ARCH_LIST="12.0a"
147-
else
148-
export TORCH_CUDA_ARCH_LIST="${cc_major}.${cc_minor}"
140+
case "${cc_major}.${cc_minor}" in
141+
9.0|12.0|12.1)
142+
# Architecture-specific instructions require the `a` target.
143+
export TORCH_CUDA_ARCH_LIST="${cc_major}.${cc_minor}a"
144+
;;
145+
*)
146+
export TORCH_CUDA_ARCH_LIST="${cc_major}.${cc_minor}"
147+
;;
148+
esac
149+
fi
150+
151+
if [[ "${TORCH_CUDA_ARCH_LIST}" =~ (^|[\;,[:space:]])(12\.1a|121a|sm_121a)([\;,[:space:]]|$) ]]; then
152+
cuda_compiler="${CUDACXX:-}"
153+
if [ -z "${cuda_compiler}" ] && [ -n "${CUDA_HOME:-}" ] && [ -x "${CUDA_HOME}/bin/nvcc" ]; then
154+
cuda_compiler="${CUDA_HOME}/bin/nvcc"
155+
fi
156+
if [ -z "${cuda_compiler}" ]; then
157+
cuda_compiler="$(command -v nvcc || true)"
158+
fi
159+
if [ -z "${cuda_compiler}" ]; then
160+
echo "ERROR: sm_121a requires CUDA Toolkit 13.0+, but nvcc was not found." >&2
161+
echo " Set CUDACXX or CUDA_HOME to a CUDA 13 toolkit." >&2
162+
exit 1
163+
fi
164+
cuda_major="$("${cuda_compiler}" --version 2>/dev/null | sed -n 's/.*release \([0-9][0-9]*\)\..*/\1/p' | tail -n 1)"
165+
if [[ ! "${cuda_major}" =~ ^[0-9]+$ ]] || (( cuda_major < 13 )); then
166+
echo "ERROR: sm_121a requires CUDA Toolkit 13.0+; ${cuda_compiler} reports CUDA ${cuda_major:-unknown}." >&2
167+
exit 1
149168
fi
150169
fi
151170

fastvideo-kernel/tests/test_attn_qat_infer.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
Compares causal and non-causal outputs against a naive float32 reference to verify
66
that the V-row permutation in scaled_fp4_quant_trans_kernel is correct (or absent).
77
8-
Requires a Blackwell GPU (sm_120a) and fp4attn_cuda / fp4quant_cuda extensions built
8+
Requires a Blackwell GPU (sm_120a or sm_121a) and fp4attn_cuda / fp4quant_cuda extensions built
99
via `cd fastvideo-kernel && ./build.sh`.
1010
1111
Run from the fastvideo-kernel directory:
@@ -24,9 +24,9 @@
2424
import torch.nn.functional as F
2525
from torch.nn.attention import SDPBackend, sdpa_kernel
2626

27-
# The FP4 extensions are only compiled under the sm_120a (Blackwell) arch
27+
# The FP4 extensions are only compiled under the sm_120a/sm_121a arch
2828
# gate; on other GPUs the api import below would die at collection time.
29-
pytest.importorskip("fp4attn_cuda", reason="ATTN_QAT_INFER FP4 kernels require a sm_120a build")
29+
pytest.importorskip("fp4attn_cuda", reason="ATTN_QAT_INFER FP4 kernels require a sm_120a/sm_121a build")
3030

3131
from attn_qat_infer.api import sageattn_blackwell
3232

@@ -129,6 +129,23 @@ def test_accuracy_sdpa(causal: bool, B: int, H: int, L: int, D: int):
129129
assert cos >= 0.97, f"({label}) B={B} H={H} L={L} D={D} cos_sim={cos:.4f} < 0.97"
130130

131131

132+
@pytest.mark.parametrize(
133+
"q_len,kv_len",
134+
[(384, 512), (384, 257)],
135+
ids=["wan_t2v_cross_attention", "wan_i2v_image_cross_attention"],
136+
)
137+
def test_cross_attention_unequal_sequence_lengths(q_len: int, kv_len: int):
138+
"""Wan cross-attention keeps video queries separate from text/image keys."""
139+
torch.manual_seed(42)
140+
q = torch.randn(1, 4, q_len, 128, dtype=torch.bfloat16, device=DEVICE)
141+
k = torch.randn(1, 4, kv_len, 128, dtype=torch.bfloat16, device=DEVICE)
142+
v = torch.randn(1, 4, kv_len, 128, dtype=torch.bfloat16, device=DEVICE)
143+
ref = reference_sdpa(q, k, v, is_causal=False)
144+
out = sageattn_blackwell(q.clone(), k.clone(), v.clone(), is_causal=False)
145+
cos = cosine_similarity(out, ref)
146+
assert cos >= 0.97, f"q_len={q_len} kv_len={kv_len} cos_sim={cos:.4f} < 0.97"
147+
148+
132149
if __name__ == "__main__":
133150
print("=" * 65)
134151
print("sageattn_blackwell (ATTN_QAT_INFER) inference correctness tests")

0 commit comments

Comments
 (0)