Skip to content

F8 fmha ASM gfx950 - #2911

Merged
JohnNikolay84 merged 8 commits into
mainfrom
f8_fmha_mla_gfx950
May 7, 2026
Merged

F8 fmha ASM gfx950#2911
JohnNikolay84 merged 8 commits into
mainfrom
f8_fmha_mla_gfx950

Conversation

@JohnNikolay84

@JohnNikolay84 JohnNikolay84 commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

Motivation

There is no f8 fmha ASM kernel for gfx950, despite a similar kernel exists for gfx942.
Diffusion model spend 60-70% flops just in attention, so f8 can gives even 25% e2e improvement over bf16.

Technical Details

I used MLA ASM kernel for hd192 and reworked it as a pure fmha with hd128. In the end of the day it is not that hard as it is essentially fmha and mfma is 32x32x64 which is perfectly compatible with hd128 too.

Test Plan

  • Run video generation prompts with xDiT.
  • aiter fmha f8 tests.

Test Result

The improvement is 60->45s. No noticeable artifacts.

python3 op_tests/test_mha_fp8.py -b 1 -n 5 -q 75600 -d 128
    [aiter] import [module_aiter_core] under /workspace/aiter/aiter/jit/module_aiter_core.so
    [aiter] import [module_fmha_v3_fwd] under /workspace/aiter/aiter/jit/module_fmha_v3_fwd.so
    [aiter] type hints mismatch, override to --> fmha_v3_fwd(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, dropout_p: float, softmax_scale: float, is_causal: bool, window_size_left: int, window_size_right: int, return_softmax_lse: bool, return_dropout_randval: bool, how_v3_bf16_cvt: int, out: Optional[torch.Tensor] = None, bias: Optional[torch.Tensor] = None, alibi_slopes: Optional[torch.Tensor] = None, q_descale: Optional[torch.Tensor] = None, k_descale: Optional[torch.Tensor] = None, v_descale: Optional[torch.Tensor] = None, gen: Optional[torch.Generator] = None) -> List[torch.Tensor]
    [aiter] LoadKernel: _ZN5aiter24fmha_fwd_hd128_fp8_gfx950E hsaco: /workspace/aiter/hsa//gfx950/fmha_v3_fwd/fwd_hd128_fp8.co
    [W424 14:32:50.280727107 collection.cpp:1133] Warning: ROCTracer produced duplicate flow start: 4 (function operator())
    [aiter] LoadKernel: _ZN5aiter19fmha_fwd_hd128_bf16E hsaco: /workspace/aiter/hsa//gfx950/fmha_v3_fwd/fwd_hd128_bf16.co
Output nrms: 0.004180908203125
Output max diff: 0.001953125
[aiter] mha summary:
   quant_fwd_us  quant_fwd_tflops  quant_fwd_gb_per_sec        fwd_us   fwd_tflops  fwd_gb_per_sec
0   7082.736211       2065.772488             27.325033  12004.333737  1218.836624       32.244355
wan22_i2v_14b_fp8_aiter_mla-mha_fp8_gemms_hybrid_aiter_2steps_staging.mp4

Submission Checklist

@JohnNikolay84
JohnNikolay84 requested a review from a team April 24, 2026 13:24
@JohnNikolay84 JohnNikolay84 self-assigned this Apr 24, 2026
@JohnNikolay84
JohnNikolay84 requested a review from valarLip April 24, 2026 13:25
@github-actions

Copy link
Copy Markdown
Contributor

🏷️ CI Guide

Runs automatically on every PR:

  • ✅ Pre-checks (submodule verification, code formatting)
  • ✅ Aiter op tests (gfx942 + gfx950)
  • ✅ Triton tests on MI35X (only when aiter/ops/triton/** or related paths are changed)

Extended tests (opt-in via labels):

Label Tests
ci:triton-300x Run an additional Triton test job on MI300X in PRs; main branch always runs both MI35X and MI300X
ci:sglang SGLang integration tests
ci:atom ATOM benchmark (DeepSeek-R1 + GPT-OSS)
ci:vllm vLLM benchmark
ci:all All of the above

Add labels via the sidebar or gh pr edit 2911 --add-label <label>

@jcaraban

jcaraban commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

@JohnNikolay84 while this was developed in the context of Diffusion Inference on xDiT, I think the testing can be limited to AITER. Your change to aiter/ops/mha.py makes it so your ASM gets selected in the MHA fp8 path, right? In that case op_tests/test_mha_fp8.py is now expected to jump from 1200 TFlops to 2000 TFlops on mi355. If you can paste those logs, I'd consider that enough.

@JohnNikolay84

JohnNikolay84 commented Apr 24, 2026

Copy link
Copy Markdown
Contributor Author

@jcaraban

python3 op_tests/test_mha_fp8.py -b 1 -n 5 -q 75600 -d 128
[aiter] import [module_aiter_core] under /workspace/aiter/aiter/jit/module_aiter_core.so
[aiter] import [module_fmha_v3_fwd] under /workspace/aiter/aiter/jit/module_fmha_v3_fwd.so
[aiter] type hints mismatch, override to --> fmha_v3_fwd(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, dropout_p: float, softmax_scale: float, is_causal: bool, window_size_left: int, window_size_right: int, return_softmax_lse: bool, return_dropout_randval: bool, how_v3_bf16_cvt: int, out: Optional[torch.Tensor] = None, bias: Optional[torch.Tensor] = None, alibi_slopes: Optional[torch.Tensor] = None, q_descale: Optional[torch.Tensor] = None, k_descale: Optional[torch.Tensor] = None, v_descale: Optional[torch.Tensor] = None, gen: Optional[torch.Generator] = None) -> List[torch.Tensor]
[aiter] LoadKernel: _ZN5aiter24fmha_fwd_hd128_fp8_gfx950E hsaco: /workspace/aiter/hsa//gfx950/fmha_v3_fwd/fwd_hd128_fp8.co
[W424 14:32:50.280727107 collection.cpp:1133] Warning: ROCTracer produced duplicate flow start: 4 (function operator())
[aiter] LoadKernel: _ZN5aiter19fmha_fwd_hd128_bf16E hsaco: /workspace/aiter/hsa//gfx950/fmha_v3_fwd/fwd_hd128_bf16.co
Output nrms: 0.004180908203125
Output max diff: 0.001953125

[aiter] mha summary:
   quant_fwd_us  quant_fwd_tflops  quant_fwd_gb_per_sec        fwd_us   fwd_tflops  fwd_gb_per_sec
0   7082.736211       2065.772488             27.325033  12004.333737  1218.836624       32.244355

@jcaraban

Copy link
Copy Markdown
Contributor

Thanks! Here is a plot of the long-context TFlops (per head) for @valarLip et al
image

@JohnNikolay84 JohnNikolay84 changed the title F8 fmha mla gfx950 F8 fmha gfx950 Apr 24, 2026
@yichiche

Copy link
Copy Markdown
Contributor

@JohnNikolay84 Thanks for the effort. To clarify: is this kernel limited to MHA with hd128, or might it be extended to other head dimensions in the future?

@jcaraban

Copy link
Copy Markdown
Contributor

@yichiche what other head dimensions do you have in mind? hd192 might be trivial since Sergey derived his MHA from the persistent fp8 MLA. However our focus is diffusion models where hd128 is typically enough

@yichiche

Copy link
Copy Markdown
Contributor

@jcaraban While running Wan 2.2 end-to-end, I hit a segmentation fault. In this model, both self-attention (where Q/K/V share the same sequence length) and cross-attention go through the MHA path. The crash occurs in cross-attention with seq_q = 90000, seq_k = seq_v = 512.

Minimal repro (CLI)

cd /sgl-workspace/aiter_mha_fp8/aiter
python3 op_tests/test_mha_fp8.py -b 1 -n 40 -q 90000 -k 512 -d 128

[aiter] import [module_aiter_core] under /sgl-workspace/aiter_mha_fp8/aiter/aiter/jit/module_aiter_core.so
[aiter] import [module_fmha_v3_fwd] under /sgl-workspace/aiter_mha_fp8/aiter/aiter/jit/module_fmha_v3_fwd.so
[aiter] type hints mismatch, override to --> fmha_v3_fwd(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, dropout_p: float, softmax_scale: float, is_causal: bool, window_size_left: int, window_size_right: int, return_softmax_lse: bool, return_dropout_randval: bool, how_v3_bf16_cvt: int, out: Optional[torch.Tensor] = None, bias: Optional[torch.Tensor] = None, alibi_slopes: Optional[torch.Tensor] = None, q_descale: Optional[torch.Tensor] = None, k_descale: Optional[torch.Tensor] = None, v_descale: Optional[torch.Tensor] = None, gen: Optional[torch.Generator] = None) -> List[torch.Tensor]
[aiter] hipModuleLoad: /sgl-workspace/aiter_mha_fp8/aiter/hsa//gfx950/fmha_v3_fwd/fwd_hd128_fp8.co GetFunction: _ZN5aiter24fmha_fwd_hd128_fp8_gfx950E
[aiter] hipModuleGetFunction: _ZN5aiter24fmha_fwd_hd128_fp8_gfx950E Success
Memory access fault by GPU node-2 (Agent handle: 0x5592cecab380) on address 0x7f1c01000000. Reason: Unknown.

@jcaraban

Copy link
Copy Markdown
Contributor

Thanks for the minimal repro 👍 The crash seems constrained to cross attention (K != Q) and Heads 2, 4, 8, 10, 20, 40... however it doesn't crash for H 1, 3, 5, 6, 7, 9...

@JohnNikolay84

Copy link
Copy Markdown
Contributor Author

@yichiche thank you for the report. Great catch. The issue should be fixed now.

@yichiche

yichiche commented May 4, 2026

Copy link
Copy Markdown
Contributor

@yichiche thank you for the report. Great catch. The issue should be fixed now.

Hi, may I follow up the status of this PR? When will merge it?

@JohnNikolay84

Copy link
Copy Markdown
Contributor Author

@valarLip do you have any ,comments/objections about this PR?

@jcaraban jcaraban changed the title F8 fmha gfx950 F8 fmha ASM gfx950 May 5, 2026
@valarLip
valarLip requested a review from slippedJim May 5, 2026 16:33
@valarLip

valarLip commented May 5, 2026

Copy link
Copy Markdown
Collaborator

looks really good

valarLip
valarLip previously approved these changes May 6, 2026
@JohnNikolay84

JohnNikolay84 commented May 6, 2026

Copy link
Copy Markdown
Contributor Author

I've asked Claude to compare CK vs asm for the causal kernel too

image

Sergey Solo and others added 4 commits May 6, 2026 16:30
Commit c437cca dropped fwd_hd128_fp8_group.co and
fwd_hd128_fp8_causal_group.co from hsa/gfx950/fmha_v3_fwd/fmha_fwd.csv.
The gfx950 FP8 varlen ASM kernels will be re-added later; for now route
those calls through CK by tightening can_impl_fmha_v3_fwd in
flash_attn_varlen_fp8_pertensor_func's path. The condition is gated on
get_gfx() == "gfx950" so gfx942 keeps using its own ASM group kernels.

Without this change, dropping the gfx950 CSV rows alone would cause
RuntimeError: invalid argument for fmha_v3_varlen_fwd: the dispatcher in
module_fmha_v3_varlen_fwd is built with FAV3_ON=1 only (no FAV2_ON), so
the in-module CK fallback in cpp_itfs/mha_fwd.cu is compiled out and a
CSV miss propagates as -1 to the TORCH_CHECK in asm_mha_varlen_fwd.

Verified with op_tests/test_mha_varlen_fp8.py for both causal and
non-causal at b=2,n=32,nk=32,q=k=1024,d=128 on gfx950: no
fp8_*_group.co LoadKernel attempts, output max_diff within tolerance.
Fixed-shape FP8 (mha_fwd) is unaffected -- fwd_hd128_fp8.co and
fwd_hd128_fp8_causal.co remain in the CSV and still picked by ASM.

Co-authored-by: Cursor <cursoragent@cursor.com>
@jcaraban
jcaraban requested a review from valarLip May 7, 2026 08:40
@JohnNikolay84
JohnNikolay84 merged commit 3a52614 into main May 7, 2026
35 checks passed
@JohnNikolay84
JohnNikolay84 deleted the f8_fmha_mla_gfx950 branch May 7, 2026 10:02
Liang-jianhao97 pushed a commit that referenced this pull request May 7, 2026
* mla with hd128

* Fix GPU fault

* Fix register clobbing bug in s_load

* Fix a bug in group causal attention kernel

* Fix GPU fault

* Remove broken varlen variants

* Route FP8 varlen MHA on gfx950 to CK; ASM group will land later

Commit c437cca dropped fwd_hd128_fp8_group.co and
fwd_hd128_fp8_causal_group.co from hsa/gfx950/fmha_v3_fwd/fmha_fwd.csv.
The gfx950 FP8 varlen ASM kernels will be re-added later; for now route
those calls through CK by tightening can_impl_fmha_v3_fwd in
flash_attn_varlen_fp8_pertensor_func's path. The condition is gated on
get_gfx() == "gfx950" so gfx942 keeps using its own ASM group kernels.

Without this change, dropping the gfx950 CSV rows alone would cause
RuntimeError: invalid argument for fmha_v3_varlen_fwd: the dispatcher in
module_fmha_v3_varlen_fwd is built with FAV3_ON=1 only (no FAV2_ON), so
the in-module CK fallback in cpp_itfs/mha_fwd.cu is compiled out and a
CSV miss propagates as -1 to the TORCH_CHECK in asm_mha_varlen_fwd.

Verified with op_tests/test_mha_varlen_fp8.py for both causal and
non-causal at b=2,n=32,nk=32,q=k=1024,d=128 on gfx950: no
fp8_*_group.co LoadKernel attempts, output max_diff within tolerance.
Fixed-shape FP8 (mha_fwd) is unaffected -- fwd_hd128_fp8.co and
fwd_hd128_fp8_causal.co remain in the CSV and still picked by ASM.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Reintroduce group mode kernels

---------

Co-authored-by: Sergey Solo <ssolovye@amd.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@carlushuang

Copy link
Copy Markdown
Collaborator

This PR regession all the MI355 BF16 attention causal masking attention, will increase execution to 2x because of
image

@carlushuang

carlushuang commented May 28, 2026

Copy link
Copy Markdown
Collaborator

#3390 => This PR fixed this regression, we don't globally set tg_div = 1 (hence will have all the bf16 mi355 attn regression), only check the FP8 attention introduced in this PR to set tgdiv = 1

cc @JohnNikolay84 @yichiche @jcaraban
cc @valarLip @danyao12 @shengnxu

carlushuang added a commit that referenced this pull request May 28, 2026
The gfx950 tg_div=1 override (from PR #2911) applied to all dtypes,
but only the FP8 ASM kernel needs it. The bf16 ASM kernels support
causal tile-skipping and should use tg_div=2 for causal masks.

Guard the tg_div=1 override with data_type=="fp8bf16" so bf16 causal
launches half the grid. Also halve the FLOP count for causal in the
C++ benchmark to align with the standard TFLOPS convention.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants