Skip to content

Commit 5966076

Browse files
authored
fix: correct vision FA3 arguments and fallback handling (#1641)
1 parent f62bc81 commit 5966076

3 files changed

Lines changed: 85 additions & 5 deletions

File tree

‎lightllm/common/basemodel/attention_vit/fa3/fp.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def _vit_att_fwd(
5353
pack_gqa=None,
5454
sm_margin=0,
5555
sinks=None,
56+
sparse_mask_fine=None,
57+
only_qv=False,
5658
)
5759

5860
return o

‎lightllm/models/vit/triton_kernel/flashattention_nopad.py‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ def flash_attention_v3_fwd(
203203
pack_gqa=None,
204204
sm_margin=0,
205205
sinks=None,
206+
sparse_mask_fine=None,
207+
only_qv=False,
206208
)
207209
return o
208210

@@ -220,8 +222,8 @@ def flash_attention_fwd(q, k, v, o, cu_seqlens, max_seqlen):
220222
if _flash_attn_v3_available and (is_hopper() or is_4090()):
221223
try:
222224
flash_attention_v3_fwd(q, k, v, o, cu_seqlens, max_seqlen)
225+
return
223226
except Exception as e:
224227
print(f"Failed to use sgl_kernel: {e}")
225228
_flash_attn_v3_available = False
226-
else:
227-
_flash_attention_triton_fwd(q, k, v, o, cu_seqlens, max_seqlen)
229+
_flash_attention_triton_fwd(q, k, v, o, cu_seqlens, max_seqlen)

‎unit_tests/models/vit/test_flash_attention_forward.py‎

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,68 @@
33
import time
44
import pytest
55
from 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

870
def 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

Comments
 (0)