11# SPDX-License-Identifier: MIT
22# Copyright (C) 2024-2025, Advanced Micro Devices, Inc. All rights reserved.
33
4+ import argparse
5+ import itertools
6+
7+ import pandas as pd
8+ import pytest
49import torch
10+
511import aiter
612from aiter import dtypes
13+ from aiter .test_common import benchmark , run_perftest
714from aiter .test_mha_common import (
815 attention_ref ,
916 attn_bias_from_alibi_slopes ,
1017 ck_randval_to_dropout_mask ,
1118 convert_flash_attn_S_to_softmax ,
1219)
13- import pytest
14- import argparse
1520
1621
1722def run_torch (
@@ -86,7 +91,8 @@ def run_ck(
8691 cu_seqlens_q = None ,
8792 cu_seqlens_kv = None ,
8893):
89- out , _ , S_dmask = aiter .flash_attn_func (
94+ (out , _ , S_dmask ), us_fwd = run_perftest (
95+ aiter .flash_attn_func ,
9096 q ,
9197 k ,
9298 v ,
@@ -124,13 +130,27 @@ def run_ck(
124130 dropout_mask = None
125131
126132 if dout is None :
127- return out , dropout_mask
133+ return out , dropout_mask , us_fwd
128134 elif bias is not None :
129- dq , dk , dv , dbias = torch .autograd .grad (out , (q , k , v , bias ), dout )
130- return out , dropout_mask , dq , dk , dv , dbias
135+ (dq , dk , dv , dbias ), us_bwd = run_perftest (
136+ torch .autograd .grad ,
137+ out ,
138+ (q , k , v , bias ),
139+ dout ,
140+ retain_graph = True ,
141+ num_rotate_args = 1 ,
142+ )
143+ return out , dropout_mask , dq , dk , dv , dbias , (us_fwd , us_bwd )
131144 else :
132- dq , dk , dv = torch .autograd .grad (out , (q , k , v ), dout )
133- return out , dropout_mask , dq , dk , dv , None
145+ (dq , dk , dv ), us_bwd = run_perftest (
146+ torch .autograd .grad ,
147+ out ,
148+ (q , k , v ),
149+ dout ,
150+ retain_graph = True ,
151+ num_rotate_args = 1 ,
152+ )
153+ return out , dropout_mask , dq , dk , dv , None , (us_fwd , us_bwd )
134154
135155
136156@pytest .mark .parametrize ("dtype" , [dtypes .fp16 , dtypes .bf16 ])
@@ -173,6 +193,7 @@ def run_ck(
173193 (2048 , 2048 ),
174194 ],
175195)
196+ @benchmark ()
176197def test_flash_attn_output (
177198 batch_size ,
178199 nheads ,
@@ -238,7 +259,7 @@ def test_flash_attn_output(
238259 requires_grad = True ,
239260 )
240261
241- out , dropout_mask , dq , dk , dv , dbias = run_ck (
262+ out , dropout_mask , dq , dk , dv , dbias , ( us_fwd , us_bwd ) = run_ck (
242263 q ,
243264 k ,
244265 v ,
@@ -307,6 +328,28 @@ def test_flash_attn_output(
307328 dbias_tol = max (10 * (dbias_pt - dbias_ref ).abs ().max ().item (), 0.01 )
308329 assert (dbias - dbias_ref ).abs ().max ().item () <= dbias_tol
309330
331+ fwd_flop = nheads * (seqlen_q * seqlen_k * d * 2 + seqlen_q * seqlen_k * d_v * 2 )
332+ dtype_bytes = torch .finfo (dtype ).bits // 8
333+ fwd_num_bytes = (
334+ nheads
335+ * dtype_bytes
336+ * (seqlen_q * d + seqlen_k * d + seqlen_k * d_v + seqlen_q * d_v )
337+ )
338+ bwd_flop = nheads * (
339+ seqlen_q * seqlen_k * d * 2 * 3 + seqlen_q * seqlen_k * d_v * 2 * 2
340+ )
341+ bwd_num_bytes = (
342+ 2 * fwd_num_bytes + nheads * (torch .finfo (torch .float ).bits // 8 ) * seqlen_q
343+ )
344+ ret = {}
345+ ret ["fwd_us" ] = us_fwd
346+ ret ["fwd_tflops" ] = (fwd_flop ) / 1.0e6 / us_fwd
347+ ret ["fwd_gb_per_sec" ] = (fwd_num_bytes ) / 1.0e3 / us_fwd
348+ ret ["bwd_us" ] = us_bwd
349+ ret ["bwd_tflops" ] = (bwd_flop ) / 1.0e6 / us_bwd
350+ ret ["bwd_gb_per_sec" ] = (bwd_num_bytes ) / 1.0e3 / us_bwd
351+ return ret
352+
310353
311354@pytest .mark .parametrize (
312355 "padding_scenario" ,
@@ -450,7 +493,7 @@ def test_flash_attn_seq_padding(
450493 alibi_slopes = torch .rand (batch_size , nheads , device = "cuda" , dtype = dtypes .fp32 )
451494
452495 # 2. Run CK with cu_seqlens (forward pass only)
453- out , _ = run_ck (
496+ out , _ , _ = run_ck (
454497 q ,
455498 k ,
456499 v ,
@@ -552,6 +595,13 @@ def test_flash_attn_seq_padding(
552595 assert diff <= out_tol
553596
554597
598+ l_dtype = ["bf16" , "fp16" ]
599+ l_dim = [32 , 40 , 64 , 111 , 128 , 160 ]
600+ l_mha_type = ["mha" , "mqa" , "gqa" ]
601+ l_causal = [False , True ]
602+ l_local = [False , True ]
603+ l_deterministic = [False , True ]
604+
555605parser = argparse .ArgumentParser (
556606 formatter_class = argparse .RawTextHelpFormatter ,
557607 description = "config input of test" ,
@@ -568,8 +618,8 @@ def test_flash_attn_seq_padding(
568618 "-n" ,
569619 "--nheads" ,
570620 type = int ,
571- default = 5 ,
572- help = """Number of heads. Default is 5 .
621+ default = 6 ,
622+ help = """Number of heads. Default is 6 .
573623 e.g.: -n 8""" ,
574624)
575625parser .add_argument (
@@ -592,8 +642,8 @@ def test_flash_attn_seq_padding(
592642 "-qk" ,
593643 "--d_qk" ,
594644 type = int ,
595- default = 128 ,
596- help = """Dimension of query and key. Default is 128 .
645+ default = None ,
646+ help = """Dimension of query and key. Default is None .
597647 e.g.: -qk 256""" ,
598648)
599649parser .add_argument (
@@ -615,16 +665,20 @@ def test_flash_attn_seq_padding(
615665parser .add_argument (
616666 "-c" ,
617667 "--causal" ,
618- action = "store_true" ,
619- help = """Causal attention. Default is False.
620- -c or --causal # enable causal attention""" ,
668+ action = argparse .BooleanOptionalAction ,
669+ default = None ,
670+ help = """Causal attention. Default is None.
671+ -c or --causal # enable causal attention
672+ --no-causal # disable causal attention""" ,
621673)
622674parser .add_argument (
623675 "-l" ,
624676 "--local" ,
625- action = "store_true" ,
626- help = """Local attention. Default is False.
627- -l or --local # enable local attention""" ,
677+ action = argparse .BooleanOptionalAction ,
678+ default = None ,
679+ help = """Local attention. Default is None.
680+ e.g. -l or --local # enable local attention
681+ --no-local # disable local attention""" ,
628682)
629683parser .add_argument (
630684 "-bt" ,
@@ -637,45 +691,87 @@ def test_flash_attn_seq_padding(
637691parser .add_argument (
638692 "-det" ,
639693 "--deterministic" ,
640- action = "store_true" ,
641- help = """Deterministic attention. Default is False.
642- -det or --deterministic # enable deterministic attention""" ,
694+ action = argparse .BooleanOptionalAction ,
695+ default = None ,
696+ help = """Deterministic attention. Default is None.
697+ -det or --deterministic # enable deterministic attention
698+ --no-deterministic # disable deterministic attention""" ,
643699)
644700parser .add_argument (
645701 "-m" ,
646702 "--mha_type" ,
647703 type = str ,
648- default = "mha" ,
704+ default = None ,
649705 help = """Type of multi-head attention.
650706 e.g.: -m mha""" ,
651707)
652708parser .add_argument (
653709 "-d" ,
654710 "--dtype" ,
655711 type = str ,
656- default = "bf16" ,
712+ default = None ,
657713 help = """Data type.
658714 e.g.: -d bf16""" ,
659715)
660716
661717if __name__ == "__main__" :
662718 args = parser .parse_args ()
663- dtype = dtypes .d_dtypes [args .dtype ]
664- test_flash_attn_output (
665- args .batch_size ,
666- args .nheads ,
667- args .seqlen_q ,
668- args .seqlen_k ,
669- args .d_qk ,
670- args .d_v ,
671- args .dropout_p ,
672- args .causal ,
673- args .local ,
674- args .bias_type ,
675- args .deterministic ,
676- args .mha_type ,
719+ if args .dtype is not None :
720+ l_dtype = [dtypes .d_dtypes [args .dtype ]]
721+ else :
722+ l_dtype = [dtypes .d_dtypes [key ] for key in l_dtype ]
723+ args .dtype = "bf16"
724+
725+ if args .d_qk is not None :
726+ l_dim = [args .d_qk ]
727+ else :
728+ args .d_qk = 128
729+ if args .mha_type is not None :
730+ l_mha_type = [args .mha_type ]
731+ else :
732+ args .mha_type = "mha"
733+ if args .causal is not None :
734+ l_causal = [args .causal ]
735+ else :
736+ args .causal = False
737+ if args .local is not None :
738+ l_local = [args .local ]
739+ else :
740+ args .local = False
741+ if args .deterministic is not None :
742+ l_deterministic = [args .deterministic ]
743+ else :
744+ args .deterministic = False
745+ collected = []
746+ for (
677747 dtype ,
678- )
748+ dim ,
749+ mha_type ,
750+ causal ,
751+ local ,
752+ deterministic ,
753+ ) in itertools .product (
754+ l_dtype , l_dim , l_mha_type , l_causal , l_local , l_deterministic
755+ ):
756+ ret = test_flash_attn_output (
757+ args .batch_size ,
758+ args .nheads ,
759+ args .seqlen_q ,
760+ args .seqlen_k ,
761+ dim ,
762+ dim ,
763+ args .dropout_p ,
764+ causal ,
765+ local ,
766+ args .bias_type ,
767+ deterministic ,
768+ mha_type ,
769+ dtype ,
770+ )
771+ collected .append (ret )
772+
773+ df = pd .DataFrame (collected )
774+ aiter .logger .info (f"mha summary:\n { df } " )
679775
680776 test_flash_attn_seq_padding (
681777 "mixed" ,
0 commit comments