Skip to content

Commit b4051c1

Browse files
committed
[kernel] Build + allow attn_qat_infer FP4 attention on sm_121a (DGX Spark)
The modified-SageAttention3 FP4 attention kernels (fp4attn_cuda/fp4quant_cuda) were pinned to sm_120a, and the docs/comments called them "sm_120a-only". They are in fact numerically correct on sm_121a (DGX Spark / GB10) too — verified on a GB10: cos ~0.98 vs bf16 SDPA across shapes/causal, output bit-identical in structure. - CMakeLists: build the FP4 targets for sm_120a AND sm_121a (added only on CUDA >= 12.9, since sm_121a needs CUDA 13's ptxas; pre-13 toolchains keep the sm_120a-only build unchanged). AUTO-gate and local-GPU detect now recognize sm_121a. - Allow ATTN_QAT_INFER in the Wan attention + denoising-stage backend allowlists so it is opt-in-selectable on sm_121 (default stays FA/SDPA). - Docs: correct "sm_120a-only" -> sm_120a/sm_121a, with the quality caveat. Opt-in only (FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER); default behavior is unchanged on every existing GPU.
1 parent 7a592ff commit b4051c1

4 files changed

Lines changed: 42 additions & 18 deletions

File tree

docs/inference/optimizations.md

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ This page describes the various options for speeding up generation times in Fast
2727
- Video Sparse Attention: `FASTVIDEO_ATTENTION_BACKEND=VIDEO_SPARSE_ATTN`
2828
- Sage Attention: `FASTVIDEO_ATTENTION_BACKEND=SAGE_ATTN`
2929
- Sage Attention 3: `FASTVIDEO_ATTENTION_BACKEND=SAGE_ATTN_THREE`
30-
- Attn-QAT inference (modified SageAttention3 FP4, sm_120/RTX 5090): `FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER`
30+
- Attn-QAT inference (modified SageAttention3 FP4, sm_120a/sm_121a — RTX 5090 / DGX Spark): `FASTVIDEO_ATTENTION_BACKEND=ATTN_QAT_INFER`
3131
- Video MoBA Attention: `FASTVIDEO_ATTENTION_BACKEND=VMOBA_ATTN`
3232
- Sparse Linear Attention: `FASTVIDEO_ATTENTION_BACKEND=SLA_ATTN`
3333
- SageSLA Attention: `FASTVIDEO_ATTENTION_BACKEND=SAGE_SLA_ATTN`
@@ -146,7 +146,7 @@ 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_120)
149+
### NVFP4 + Attn-QAT (modified SageAttention3, Blackwell sm_120a/sm_121a)
150150

151151
**`ATTN_QAT_INFER`** with **`transformer_quant=nvfp4_qat`**
152152

@@ -155,9 +155,18 @@ fly) plus the modified SageAttention3 FP4 attention backend. This is the
155155
inference half of the Quantization-Aware Distillation (QAD) recipe and the path
156156
used for the RTX 5090 release.
157157

158-
The `attn_qat_infer` kernel hard-gates on **sm_120 (consumer Blackwell / RTX
159-
5090)**; on other GPUs the backend logs a notice and falls back to Flash
160-
Attention. See the [Attn-QAT paper](https://arxiv.org/abs/2603.00040).
158+
The `attn_qat_infer` kernel targets consumer/workstation Blackwell — **sm_120a
159+
(RTX 5090 / PRO 6000)** and **sm_121a (DGX Spark GB10)**; the block-scaled FP4
160+
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.
163+
164+
> **Quality note (stock weights):** on stock Wan-2.1 the FP4-attention output is
165+
> below bf16 — QAD expects a QAT-distilled checkpoint the model was *trained* to
166+
> tolerate FP4 attention with. Post-hoc on non-QAT weights, expect a quality
167+
> cost; use the QAT checkpoint for release-grade output.
168+
169+
See the [Attn-QAT paper](https://arxiv.org/abs/2603.00040).
161170

162171
Enable both halves — attention via the env var, linear via `transformer_quant`:
163172

fastvideo-kernel/CMakeLists.txt

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ if(NOT GPU_BACKEND STREQUAL "ROCM")
7272
else()
7373
# Best-effort probe of the visible GPU (mirrors build.sh detect_with_torch).
7474
execute_process(
75-
COMMAND "${Python_EXECUTABLE}" -c "import torch; assert torch.cuda.is_available(); mj, mn = torch.cuda.get_device_capability(0); print(f'{mj}.{mn}a' if (mj, mn) in ((9, 0), (12, 0)) else f'{mj}.{mn}')"
75+
COMMAND "${Python_EXECUTABLE}" -c "import torch; assert torch.cuda.is_available(); mj, mn = torch.cuda.get_device_capability(0); print(f'{mj}.{mn}a' if (mj, mn) in ((9, 0), (12, 0), (12, 1)) else f'{mj}.{mn}')"
7676
OUTPUT_VARIABLE _FV_ARCH_LIST
7777
OUTPUT_STRIP_TRAILING_WHITESPACE
7878
RESULT_VARIABLE _fv_detect_rc
@@ -208,10 +208,13 @@ else()
208208
set(_WANTS_ATTN_QAT_INFER ON)
209209
elseif(FASTVIDEO_KERNEL_BUILD_ATTN_QAT_INFER STREQUAL "AUTO")
210210
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).
211214
string(REGEX MATCH
212-
"(^|[; ,])((12\\.0a)|(120a)|(sm_120a))([; ,]|$)"
213-
_HAS_120A "${TORCH_CUDA_ARCH_LIST}")
214-
if(_HAS_120A)
215+
"(^|[; ,])((12\\.0a)|(120a)|(sm_120a)|(12\\.1a)|(121a)|(sm_121a))([; ,]|$)"
216+
_HAS_BLACKWELL_FP4 "${TORCH_CUDA_ARCH_LIST}")
217+
if(_HAS_BLACKWELL_FP4)
215218
set(_WANTS_ATTN_QAT_INFER ON)
216219
endif()
217220
else()
@@ -243,7 +246,7 @@ else()
243246
else()
244247
message(STATUS
245248
"attn_qat_infer kernels: DISABLED "
246-
"(requires CUDA 12.8+ and Blackwell sm_120a)")
249+
"(requires CUDA 12.8+ and Blackwell sm_120a/sm_121a)")
247250
endif()
248251
endif()
249252

@@ -253,12 +256,12 @@ set(BUILD_CXX_KERNELS ON)
253256
# ---------------------------------------------------------------------------
254257
# Per-arch split for the Blackwell FP4 (attn_qat_infer) build
255258
# ---------------------------------------------------------------------------
256-
# The FP4 kernels are sm_120a-only (they emit `cvt.e2m1x2` etc.), while the main
259+
# The FP4 kernels are Blackwell-only (sm_120a/sm_121a; they emit `cvt.e2m1x2` etc.), while the main
257260
# extension (Hopper-only TK + generic turbodiffusion) targets the full arch list.
258261
# find_package(Torch) injects ONE global -gencode list into CMAKE_CUDA_FLAGS that
259262
# forces every target onto every arch, so the FP4 sources also get the sm_90a pass
260263
# and ptxas rejects their Blackwell instructions. Strip that global list and drive
261-
# arch per target via CUDA_ARCHITECTURES instead (the fp4* targets pin 120a below;
264+
# arch per target via CUDA_ARCHITECTURES instead (the fp4* targets pin the Blackwell archs below;
262265
# the main extension gets the full list). Only do this for the FP4 build with an
263266
# explicit arch list, so the cu126 / local autodetect paths stay untouched.
264267
if(ENABLE_ATTN_QAT_INFER AND TORCH_CUDA_ARCH_LIST)
@@ -270,7 +273,7 @@ if(ENABLE_ATTN_QAT_INFER AND TORCH_CUDA_ARCH_LIST)
270273
set(FASTVIDEO_MAIN_CUDA_ARCHS "${TORCH_CUDA_ARCH_LIST}")
271274
string(REPLACE "sm_" "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
272275
string(REPLACE "." "" FASTVIDEO_MAIN_CUDA_ARCHS "${FASTVIDEO_MAIN_CUDA_ARCHS}")
273-
message(STATUS "[per-arch] main extension archs=${FASTVIDEO_MAIN_CUDA_ARCHS}, fp4* archs=120a")
276+
message(STATUS "[per-arch] main extension archs=${FASTVIDEO_MAIN_CUDA_ARCHS}, fp4* archs=sm_120a/sm_121a")
274277
endif()
275278

276279
# Compiler flags
@@ -410,8 +413,18 @@ if(ENABLE_ATTN_QAT_INFER)
410413
$<$<COMPILE_LANGUAGE:CXX>:-O3 -std=c++17>
411414
$<$<COMPILE_LANGUAGE:CUDA>:${ATTN_QAT_INFER_CUDA_FLAGS}>
412415
)
416+
# FP4 kernels target consumer/workstation Blackwell: sm_120a (RTX 5090 /
417+
# PRO 6000) and sm_121a (DGX Spark GB10). sm_121a needs CUDA 13's ptxas
418+
# (12.8 lacks it), so it is added only on CUDA >= 12.9 — pre-13 toolchains
419+
# keep the original sm_120a-only build unchanged. sm_121a GB10-verified
420+
# (cos ~0.98 vs bf16).
421+
set(FASTVIDEO_FP4_CUDA_ARCHS "120a")
422+
if(NOT CUDAToolkit_VERSION VERSION_LESS 12.9)
423+
list(APPEND FASTVIDEO_FP4_CUDA_ARCHS "121a")
424+
endif()
425+
message(STATUS "fp4* CUDA architectures: ${FASTVIDEO_FP4_CUDA_ARCHS}")
413426
set_target_properties(fp4attn_cuda PROPERTIES
414-
CUDA_ARCHITECTURES "120a"
427+
CUDA_ARCHITECTURES "${FASTVIDEO_FP4_CUDA_ARCHS}"
415428
CXX_STANDARD 17
416429
CUDA_STANDARD 17
417430
)
@@ -427,7 +440,7 @@ if(ENABLE_ATTN_QAT_INFER)
427440
$<$<COMPILE_LANGUAGE:CUDA>:${ATTN_QAT_INFER_CUDA_FLAGS}>
428441
)
429442
set_target_properties(fp4quant_cuda PROPERTIES
430-
CUDA_ARCHITECTURES "120a"
443+
CUDA_ARCHITECTURES "${FASTVIDEO_FP4_CUDA_ARCHS}"
431444
CXX_STANDARD 17
432445
CUDA_STANDARD 17
433446
)
@@ -449,6 +462,6 @@ message(STATUS "host / backend: ${CMAKE_SYSTEM_PROCESSOR} / ${GPU_BACKEND}
449462
message(STATUS "TORCH_CUDA_ARCH_LIST: ${TORCH_CUDA_ARCH_LIST}")
450463
message(STATUS "fastvideo_kernel_ops: ON (turbodiffusion int8-gemm/quant/rmsnorm/layernorm, all listed archs)")
451464
message(STATUS " + TK sta/block_sparse (sm_90a only): ${ENABLE_TK_KERNELS}")
452-
message(STATUS "fp4attn/fp4quant (sm_120a only, CUDA >= 12.8): ${ENABLE_ATTN_QAT_INFER}")
465+
message(STATUS "fp4attn/fp4quant (sm_120a/sm_121a, CUDA >= 12.8): ${ENABLE_ATTN_QAT_INFER}")
453466
message(STATUS "Triton fallbacks ship in python/fastvideo_kernel/triton_kernels regardless.")
454467
message(STATUS "============================================================")

fastvideo/models/dits/wanvideo.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ def __init__(self,
185185
softmax_scale=None,
186186
causal=False,
187187
supported_attention_backends=(AttentionBackendEnum.FLASH_ATTN,
188-
AttentionBackendEnum.TORCH_SDPA))
188+
AttentionBackendEnum.TORCH_SDPA,
189+
AttentionBackendEnum.ATTN_QAT_INFER))
189190

190191
def forward(self, x: torch.Tensor, context: torch.Tensor,
191192
context_lens: int):

fastvideo/pipelines/stages/denoising.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ def __init__(self, transformer, scheduler, pipeline=None, transformer_2=None, va
6666
dtype=torch.float16, # TODO(will): hack
6767
supported_attention_backends=(AttentionBackendEnum.VIDEO_SPARSE_ATTN, AttentionBackendEnum.BSA_ATTN,
6868
AttentionBackendEnum.VMOBA_ATTN, AttentionBackendEnum.FLASH_ATTN,
69-
AttentionBackendEnum.TORCH_SDPA, AttentionBackendEnum.SAGE_ATTN_THREE) # hack
69+
AttentionBackendEnum.TORCH_SDPA, AttentionBackendEnum.SAGE_ATTN_THREE,
70+
AttentionBackendEnum.ATTN_QAT_INFER) # hack
7071
)
7172

7273
def forward(

0 commit comments

Comments
 (0)