33import time
44import pytest
55from lightllm .models .vit .triton_kernel .flashattention_nopad import flash_attention_fwd
6+ from lightllm .models .vit .triton_kernel import flashattention_nopad
7+ from lightllm .common .basemodel .attention_vit .fa3 .fp import Fa3VitAttBackend
8+
9+
10+ _fa3_mark = pytest .mark .skipif (
11+ not flashattention_nopad ._flash_attn_v3_available
12+ or not torch .cuda .is_available ()
13+ or torch .cuda .get_device_capability ()[0 ] < 9 ,
14+ reason = "FA3 requires sgl_kernel and a Hopper GPU" ,
15+ )
16+
17+
18+ def test_fa3_failure_computes_fallback_on_same_invocation (monkeypatch ):
19+ calls = []
20+ q = torch .zeros (2 , 1 , 4 )
21+ out = torch .full_like (q , - 123 )
22+ cu = torch .tensor ([0 , 2 ], dtype = torch .int32 )
23+
24+ def fail_fa3 (q , k , v , o , cu_seqlens , max_seqlen ):
25+ calls .append ("fa3" )
26+ o [0 ].fill_ (99 ) # A backend may fail after partially writing output.
27+ raise RuntimeError ("shape-dependent FA3 failure" )
28+
29+ def fallback (q , k , v , o , cu_seqlens , max_seqlen ):
30+ calls .append ("triton" )
31+ o .fill_ (7 )
32+
33+ monkeypatch .setattr (flashattention_nopad , "_flash_attn_v3_available" , True )
34+ monkeypatch .setattr (flashattention_nopad , "is_hopper" , lambda : True )
35+ monkeypatch .setattr (flashattention_nopad , "flash_attention_v3_fwd" , fail_fa3 , raising = False )
36+ monkeypatch .setattr (flashattention_nopad , "_flash_attention_triton_fwd" , fallback )
37+ flash_attention_fwd (q , q , q , out , cu , 2 )
38+ assert calls == ["fa3" , "triton" ]
39+ assert torch .equal (out , torch .full_like (q , 7 ))
40+ assert not flashattention_nopad ._flash_attn_v3_available
41+
42+ # Later calls should use the disabled-backend path without retrying FA3.
43+ out .fill_ (- 123 )
44+ flash_attention_fwd (q , q , q , out , cu , 2 )
45+ assert calls == ["fa3" , "triton" , "triton" ]
46+ assert torch .equal (out , torch .full_like (q , 7 ))
47+
48+
49+ def test_fa3_success_does_not_run_fallback (monkeypatch ):
50+ q = torch .zeros (2 , 1 , 4 )
51+ out = torch .full_like (q , - 123 )
52+ cu = torch .tensor ([0 , 2 ], dtype = torch .int32 )
53+
54+ def fa3 (q , k , v , o , cu_seqlens , max_seqlen ):
55+ o .fill_ (9 )
56+
57+ monkeypatch .setattr (flashattention_nopad , "_flash_attn_v3_available" , True )
58+ monkeypatch .setattr (flashattention_nopad , "is_hopper" , lambda : True )
59+ monkeypatch .setattr (flashattention_nopad , "flash_attention_v3_fwd" , fa3 , raising = False )
60+ monkeypatch .setattr (
61+ flashattention_nopad ,
62+ "_flash_attention_triton_fwd" ,
63+ lambda * args : pytest .fail ("a successful FA3 call must not run fallback" ),
64+ )
65+ flash_attention_fwd (q , q , q , out , cu , 2 )
66+ assert torch .equal (out , torch .full_like (q , 9 ))
67+ assert flashattention_nopad ._flash_attn_v3_available
668
769
870def reference_attention_varlen (q , k , v , cu ):
@@ -32,7 +94,21 @@ def reference_attention_varlen(q, k, v, cu):
3294
3395
3496@pytest .mark .parametrize ("dtype,atol" , [(torch .float16 , 1e-2 ), (torch .bfloat16 , 2e-2 )])
35- def test_varlen (dtype , atol , batch = 4 , heads = 8 , d = 80 , device = "cuda:0" ):
97+ @pytest .mark .parametrize (
98+ "attention_fn" ,
99+ [
100+ flash_attention_fwd ,
101+ pytest .param (
102+ getattr (flashattention_nopad , "flash_attention_v3_fwd" , None ),
103+ marks = _fa3_mark ,
104+ ),
105+ pytest .param (
106+ Fa3VitAttBackend ._vit_att_fwd ,
107+ marks = _fa3_mark ,
108+ ),
109+ ],
110+ )
111+ def test_varlen (dtype , atol , attention_fn , batch = 4 , heads = 8 , d = 80 , device = "cuda:0" ):
36112 torch .manual_seed (0 )
37113 lengths = torch .randint (1 , 257 , (batch ,))
38114 max_len = int (lengths .max ().item ())
@@ -45,10 +121,10 @@ def test_varlen(dtype, atol, batch=4, heads=8, d=80, device="cuda:0"):
45121 k = torch .randn_like (q )
46122 v = torch .randn_like (q )
47123 out_tri = torch .randn_like (q )
48- flash_attention_fwd (q , k , v , out_tri , cu , max_len )
124+ attention_fn (q , k , v , out_tri , cu , max_len )
49125 a = time .time ()
50126 for _ in range (100 ):
51- flash_attention_fwd (q , k , v , out_tri , cu , max_len )
127+ attention_fn (q , k , v , out_tri , cu , max_len )
52128 b = time .time ()
53129 print (f"flash_attention_fwd time: { (b - a ) / 100 * 1000 :.2f} ms" )
54130 out_ref = reference_attention_varlen (q , k , v , cu )
0 commit comments