Skip to content

Commit 0d03a58

Browse files
SolitaryThinkerMister-Raggs
authored andcommitted
[bugfix]: harden SM121 Attn-QAT support
1 parent 5dc586d commit 0d03a58

11 files changed

Lines changed: 203 additions & 69 deletions

File tree

.github/workflows/publish-kernel.yml

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ jobs:
6767
- os: ubuntu-22.04
6868
arch: x86_64
6969
wheel-plat: manylinux_2_35_x86_64
70-
# aarch64 is Blackwell (GB200 sm_100a + DGX Spark / consumer sm_120a), not
70+
# aarch64 is Blackwell (GB200 sm_100a + sm_120a + DGX Spark sm_121a), not
7171
# Hopper, and Blackwell needs CUDA >= 12.8 — so only the cu130 leg applies.
7272
# Added via include so x86 keeps cu126 + cu130 while aarch64 stays cu130-only.
7373
include:
@@ -163,15 +163,16 @@ jobs:
163163
cd fastvideo-kernel
164164
git submodule update --init --recursive # Ensure ThunderKittens submodule is initialized
165165
# Release builds run on GPU-less runners, so set kernels + arch explicitly:
166-
# * aarch64 = Blackwell (GB200 sm_100a + DGX Spark/consumer sm_120a), NOT
167-
# Hopper, so TK (sm_90a wgmma) is OFF. The C++ FP4 (attn_qat_infer, SM120)
168-
# covers sm_120a; turbodiffusion covers sm_100a+sm_120a. The sm_100 FP4
169-
# forward is the FA4 CuTe DSL path in the fastvideo package (PR #1221),
166+
# * aarch64 = Blackwell (GB200 sm_100a + sm_120a + DGX Spark sm_121a), NOT
167+
# Hopper, so TK (sm_90a wgmma) is OFF. The C++ FP4 (attn_qat_infer)
168+
# covers sm_120a+sm_121a; turbodiffusion covers every listed arch. The
169+
# sm_100 FP4 forward is the FA4 CuTe DSL path in the fastvideo package (PR #1221),
170170
# JIT-compiled at runtime — not built into this wheel.
171171
# * x86_64 cu130 = Hopper TK + consumer Blackwell sm_120a FP4.
172172
# * x86_64 cu126 = Hopper TK only (older drivers; CUDA < 12.8 has no FP4).
173-
# The per-arch split in CMakeLists pins the FP4 targets to sm_120a and builds
174-
# the main extension for the full arch list. CMAKE_BUILD_PARALLEL_LEVEL caps
173+
# The per-arch split in CMakeLists pins the FP4 targets to requested
174+
# sm_120a/sm_121a and builds the main extension for the full arch list.
175+
# CMAKE_BUILD_PARALLEL_LEVEL caps
175176
# Ninja so heavy CUTLASS/TK template TUs don't OOM the 16 GB runner (exit 143).
176177
if [ "${{ matrix.platform.arch }}" = "aarch64" ]; then
177178
export TORCH_CUDA_ARCH_LIST="10.0a;12.0a;12.1a"
@@ -243,7 +244,8 @@ jobs:
243244
- name: Download PyPI wheels
244245
# Publish the cu130 (CUDA 13) wheels to PyPI for both architectures:
245246
# x86_64 — Hopper sm_90a TK + consumer Blackwell sm_120a FP4
246-
# aarch64 — Blackwell: turbodiffusion (sm_100a/sm_120a) + C++ FP4 (sm_120a);
247+
# aarch64 — Blackwell: turbodiffusion (sm_100a/sm_120a/sm_121a)
248+
# + C++ FP4 (sm_120a/sm_121a);
247249
# no TK (Hopper). sm_100 FP4 forward is the FA4 CuTe DSL path in the
248250
# fastvideo package (#1221), shipped/JIT separately.
249251
# 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
@@ -146,20 +146,19 @@ gen.generate_video(prompt="A raccoon in sunflowers", save_video=True)
146146
- Per-call cosine similarity vs BF16: ~0.99 (slight quantization error accumulates over denoising steps)
147147
- Only supports `headdim >= 128`
148148

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

151-
**`ATTN_QAT_INFER`** with **`transformer_quant=nvfp4_qat`**
151+
**`ATTN_QAT_INFER`**
152152

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

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

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

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

173172
```python
174173
import os
175174
os.environ["FASTVIDEO_ATTENTION_BACKEND"] = "ATTN_QAT_INFER"
176175

177176
from fastvideo import VideoGenerator
178-
from fastvideo.layers.quantization import get_quantization_config
179177
gen = VideoGenerator.from_pretrained(
180178
"Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
181179
num_gpus=1,
182-
# Wan-2.1 uses the nvfp4_qat config (NVFP4 is LTX2-specific). Pass an
183-
# instance — the bare string is not resolved on the from_pretrained path.
184-
transformer_quant=get_quantization_config("nvfp4_qat")(),
185-
use_fsdp_inference=False, # FSDP shards invalidate the FP4 tensor pointers
180+
use_fsdp_inference=False,
186181
)
187182
gen.generate(request={"prompt": "A raccoon in sunflowers", "output": {"save_video": True}})
188183
```
189184

190-
Or run the example script:
185+
The full QAD configuration, pairing this attention backend with
186+
`transformer_quant=nvfp4_qat`, is currently limited to sm_120. Its RTX 5090
187+
example is:
191188

192189
```bash
193190
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
@@ -208,13 +221,7 @@ else()
208221
set(_WANTS_ATTN_QAT_INFER ON)
209222
elseif(FASTVIDEO_KERNEL_BUILD_ATTN_QAT_INFER STREQUAL "AUTO")
210223
if(TORCH_CUDA_ARCH_LIST)
211-
# Consumer/workstation Blackwell: sm_120a (RTX 5090 / PRO 6000) and
212-
# sm_121a (DGX Spark GB10). The block-scaled FP4 MMA is valid on both
213-
# (GB10-verified: cos ~0.98 vs bf16 SDPA across shapes).
214-
string(REGEX MATCH
215-
"(^|[; ,])((12\\.0a)|(120a)|(sm_120a)|(12\\.1a)|(121a)|(sm_121a))([; ,]|$)"
216-
_HAS_BLACKWELL_FP4 "${TORCH_CUDA_ARCH_LIST}")
217-
if(_HAS_BLACKWELL_FP4)
224+
if(_FV_HAS_120A OR _FV_HAS_121A)
218225
set(_WANTS_ATTN_QAT_INFER ON)
219226
endif()
220227
else()
@@ -242,6 +249,18 @@ else()
242249
endif()
243250

244251
if(ENABLE_ATTN_QAT_INFER)
252+
set(FASTVIDEO_FP4_CUDA_ARCHS "")
253+
if(_FV_HAS_120A)
254+
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "120a")
255+
endif()
256+
if(_FV_HAS_121A)
257+
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "121a")
258+
endif()
259+
if(NOT FASTVIDEO_FP4_CUDA_ARCHS)
260+
message(FATAL_ERROR
261+
"FASTVIDEO_KERNEL_BUILD_ATTN_QAT_INFER=ON requires 12.0a or "
262+
"12.1a in TORCH_CUDA_ARCH_LIST; got '${TORCH_CUDA_ARCH_LIST}'.")
263+
endif()
245264
message(STATUS "attn_qat_infer kernels: ENABLED")
246265
else()
247266
message(STATUS
@@ -273,14 +292,6 @@ if(ENABLE_ATTN_QAT_INFER AND TORCH_CUDA_ARCH_LIST)
273292
set(FASTVIDEO_MAIN_CUDA_ARCHS "${TORCH_CUDA_ARCH_LIST}")
274293
string(REPLACE "sm_" "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
275294
string(REPLACE "." "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
276-
# sm_121a needs CUDA 13's ptxas. On older toolchains, downgrade any requested
277-
# 121a to 120a for the *main* extension too (not just the fp4* targets below),
278-
# so a CUDA-12.x build with 12.1a in the arch list doesn't fail to compile —
279-
# sm_120a PTX forward-JITs to sm_121 at runtime.
280-
if(CUDAToolkit_VERSION VERSION_LESS 13.0)
281-
string(REPLACE "121a" "120a" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
282-
list(REMOVE_DUPLICATES FASTVIDEO_MAIN_CUDA_ARCHS)
283-
endif()
284295
message(STATUS "[per-arch] main extension archs=${FASTVIDEO_MAIN_CUDA_ARCHS}, fp4* archs set per-target below")
285296
endif()
286297

@@ -421,15 +432,8 @@ if(ENABLE_ATTN_QAT_INFER)
421432
$<$<COMPILE_LANGUAGE:CXX>:-O3 -std=c++17>
422433
$<$<COMPILE_LANGUAGE:CUDA>:${ATTN_QAT_INFER_CUDA_FLAGS}>
423434
)
424-
# FP4 kernels target consumer/workstation Blackwell: sm_120a (RTX 5090 /
425-
# PRO 6000) and sm_121a (DGX Spark GB10). sm_121a needs CUDA 13's ptxas
426-
# (CUDA 12.x lacks it), so it is added only on CUDA >= 13.0 — older
427-
# toolchains keep the original sm_120a-only build unchanged. sm_121a
428-
# GB10-verified (cos ~0.98 vs bf16).
429-
set(FASTVIDEO_FP4_CUDA_ARCHS "120a")
430-
if(NOT CUDAToolkit_VERSION VERSION_LESS 13.0)
431-
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "121a")
432-
endif()
435+
# Compile only the FP4 architectures requested by the caller. The large
436+
# template translation units use 8-12 GiB each, so an extra target matters.
433437
message(STATUS "fp4* CUDA architectures: ${FASTVIDEO_FP4_CUDA_ARCHS}")
434438
set_target_properties(fp4attn_cuda PROPERTIES
435439
CUDA_ARCHITECTURES "${FASTVIDEO_FP4_CUDA_ARCHS}"
@@ -470,6 +474,6 @@ message(STATUS "host / backend: ${CMAKE_SYSTEM_PROCESSOR} / ${GPU_BACKEND}
470474
message(STATUS "TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}")
471475
message(STATUS "fastvideo_kernel_ops: ON (turbodiffusion int8-gemm/quant/rmsnorm/layernorm, all listed archs)")
472476
message(STATUS " + TK sta/block_sparse (sm_90a only): ${ENABLE_TK_KERNELS}")
473-
message(STATUS "fp4attn/fp4quant (sm_120a/sm_121a, CUDA >= 12.8): ${ENABLE_ATTN_QAT_INFER}")
477+
message(STATUS "fp4attn/fp4quant (sm_120a CUDA >= 12.8; sm_121a CUDA >= 13): ${ENABLE_ATTN_QAT_INFER}")
474478
message(STATUS "Triton fallbacks ship in python/fastvideo_kernel/triton_kernels regardless.")
475479
message(STATUS "============================================================")

fastvideo-kernel/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ Runtime-JIT kernels (no build step, ship in every wheel/image):
3030
| 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 ||
3131
| | | arm64 cuda12.6.3 (GH200) | `9.0a` | — (aarch64) ||
3232
| | | arm64 cuda13.0.0 (GB10 / DGX Spark) | `12.1` |||
33-
| Local `./build.sh` | manual | probes the visible GPU via torch | detected | ON iff sm_90 (non-aarch64 host) | ON iff sm_120 |
33+
| Local `./build.sh` | manual | probes the visible GPU via torch | detected | ON iff sm_90 (non-aarch64 host) | ON iff sm_120 or sm_121 |
3434

3535
Notes:
3636

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:
@@ -23,9 +23,9 @@
2323
import torch.nn.functional as F
2424
from torch.nn.attention import SDPBackend, sdpa_kernel
2525

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

3030
from attn_qat_infer.api import sageattn_blackwell
3131

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

128128

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

0 commit comments

Comments
 (0)