Skip to content

Commit 6c6d83f

Browse files
authored
add mha test case in ci and add perf summary print (ROCm#1115)
* add asm ci test and perf summary * add asm ci test and perf summary * delete seqlen condition * fix
1 parent 732c6c9 commit 6c6d83f

2 files changed

Lines changed: 287 additions & 77 deletions

File tree

op_tests/test_mha.py

Lines changed: 136 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
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
49
import torch
10+
511
import aiter
612
from aiter import dtypes
13+
from aiter.test_common import benchmark, run_perftest
714
from 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

1722
def 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()
176197
def 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+
555605
parser = 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
)
575625
parser.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
)
599649
parser.add_argument(
@@ -615,16 +665,20 @@ def test_flash_attn_seq_padding(
615665
parser.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
)
622674
parser.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
)
629683
parser.add_argument(
630684
"-bt",
@@ -637,45 +691,87 @@ def test_flash_attn_seq_padding(
637691
parser.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
)
644700
parser.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
)
652708
parser.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

661717
if __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

Comments
 (0)