Skip to content

Commit a70c27b

Browse files
committed
Add flash_attention_forward_no_dropout_inplace operator implementation, tests and benchmark
1 parent bfeca79 commit a70c27b

6 files changed

Lines changed: 376 additions & 0 deletions
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright 2026 FlagOS Contributors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import math
16+
17+
import pytest
18+
import torch
19+
20+
import flag_gems
21+
22+
from . import base, consts
23+
24+
device = flag_gems.device
25+
26+
# (batch, num_heads, q_seq_len, kv_seq_len, head_dim) configs.
27+
FLASH_FWD_CONFIGS = [
28+
(1, 2, 512, 512, 64),
29+
(1, 8, 1024, 1024, 128),
30+
(2, 4, 512, 512, 64),
31+
(1, 2, 1024, 2048, 64),
32+
]
33+
34+
35+
def torch_flash_attention_forward_no_dropout_inplace(
36+
q,
37+
k,
38+
v,
39+
scale,
40+
is_causal,
41+
return_debug_mask=False,
42+
**extra_kwargs,
43+
):
44+
"""Reference: aten::_flash_attention_forward with dropout_p=0.0."""
45+
return torch.ops.aten._flash_attention_forward(
46+
q,
47+
k,
48+
v,
49+
None,
50+
None,
51+
q.shape[-3],
52+
k.shape[-3],
53+
0.0, # dropout_p = 0.0 (no dropout)
54+
is_causal,
55+
return_debug_mask,
56+
scale=scale,
57+
**extra_kwargs,
58+
)
59+
60+
61+
def gems_flash_attention_forward_no_dropout_inplace(
62+
q,
63+
k,
64+
v,
65+
scale,
66+
is_causal,
67+
return_debug_mask=False,
68+
**extra_kwargs,
69+
):
70+
"""FlagGems Triton implementation (no dropout, in-place into ``q``)."""
71+
# ``do_bench`` reuses the same tensors across iterations, so clone ``q`` to
72+
# preserve the original data between runs (the kernel writes in-place).
73+
return flag_gems._flash_attention_forward_no_dropout_inplace(
74+
q.clone(),
75+
k,
76+
v,
77+
None,
78+
None,
79+
q.shape[-3],
80+
k.shape[-3],
81+
is_causal,
82+
return_debug_mask,
83+
scale=scale,
84+
**extra_kwargs,
85+
)
86+
87+
88+
def flash_attention_forward_no_dropout_inplace_input_fn(config, dtype, device):
89+
batch, num_head, q_seq_len, kv_seq_len, head_size = config
90+
q = torch.empty(
91+
(batch, q_seq_len, num_head, head_size), device=device, dtype=dtype
92+
).uniform_(-0.05, 0.05)
93+
k = torch.empty(
94+
(batch, kv_seq_len, num_head, head_size), device=device, dtype=dtype
95+
).uniform_(-0.05, 0.05)
96+
v = torch.empty(
97+
(batch, kv_seq_len, num_head, head_size), device=device, dtype=dtype
98+
).uniform_(-0.05, 0.05)
99+
scale = float(1.0 / math.sqrt(head_size))
100+
101+
# BSHD layout; no dropout; non-causal for the default benchmark configs.
102+
yield q, k, v, scale, False, False, {}
103+
104+
105+
class FlashAttentionForwardNoDropoutInplaceBenchmark(base.GenericBenchmark):
106+
def set_shapes(self, shape_file_path=None):
107+
# Use the configs defined in FLASH_FWD_CONFIGS directly, since this
108+
# operator has no entry in the shared core-shapes yaml file.
109+
self.shapes = [tuple(c) for c in FLASH_FWD_CONFIGS]
110+
111+
112+
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA is not available")
113+
@pytest.mark.skipif(flag_gems.device == "cpu", reason="Unsupported in CPU mode")
114+
@pytest.mark.flash_attention_forward_no_dropout_inplace
115+
def test_flash_attention_forward_no_dropout_inplace_impl():
116+
bench = FlashAttentionForwardNoDropoutInplaceBenchmark(
117+
op_name="flash_attention_forward_no_dropout_inplace",
118+
torch_op=torch_flash_attention_forward_no_dropout_inplace,
119+
input_fn=flash_attention_forward_no_dropout_inplace_input_fn,
120+
# FlashAttention only supports fp16/bf16; filter from FLOAT_DTYPES.
121+
dtypes=[d for d in consts.FLOAT_DTYPES if d in (torch.float16, torch.bfloat16)],
122+
)
123+
bench.set_gems(gems_flash_attention_forward_no_dropout_inplace)
124+
bench.run()

conf/operators.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3469,6 +3469,20 @@ ops:
34693469
- NeuralNetwork
34703470
stages:
34713471
- stable: '3.0'
3472+
- id: flash_attention_forward_no_dropout_inplace
3473+
description: |
3474+
Triton kernel implementation for _flash_attention_forward_no_dropout_inplace.
3475+
A specialised variant of _flash_attention_forward that drops the dropout
3476+
argument (implicitly dropout_p=0.0) and writes the attention output in-place.
3477+
for:
3478+
- _flash_attention_forward_no_dropout_inplace
3479+
labels:
3480+
- aten
3481+
- KernelGen
3482+
kind:
3483+
- NeuralNetwork
3484+
stages:
3485+
- alpha: '5.4'
34723486
- id: flash_attn_varlen_func
34733487
description: |
34743488
Compute attention for sequences of variable lengths within a single batch.

src/flag_gems/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ def torch_ge(v):
116116
("_euclidean_dist", _euclidean_dist),
117117
("_flash_attention_backward", flash_attention_backward),
118118
("_flash_attention_forward", _flash_attention_forward),
119+
(
120+
"_flash_attention_forward_no_dropout_inplace",
121+
_flash_attention_forward_no_dropout_inplace,
122+
),
119123
(
120124
"_functional_sym_constrain_range",
121125
_functional_sym_constrain_range,

src/flag_gems/ops/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
)
3939
from flag_gems.ops._euclidean_dist import _euclidean_dist
4040
from flag_gems.ops._flash_attention_forward import _flash_attention_forward
41+
from flag_gems.ops._flash_attention_forward_no_dropout_inplace import (
42+
_flash_attention_forward_no_dropout_inplace,
43+
)
4144
from flag_gems.ops._functional_sym_constrain_range import (
4245
_functional_sym_constrain_range,
4346
)
@@ -815,6 +818,7 @@
815818
"_amp_foreach_non_finite_check_and_unscale_",
816819
"_assert_async",
817820
"_batch_norm_no_update",
821+
"_flash_attention_forward_no_dropout_inplace",
818822
"_functional_assert_async",
819823
"_cdist_backward",
820824
"_cdist_forward",
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Copyright 2026, The FlagOS Contributors.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
# Generated by KernelGen: https://github.com/flagos-ai/KernelGen
16+
17+
import logging
18+
19+
import torch
20+
21+
from flag_gems.ops.attention import flash_attention_forward
22+
23+
logger = logging.getLogger(__name__)
24+
25+
26+
def _flash_attention_forward_no_dropout_inplace(
27+
query,
28+
key,
29+
value,
30+
cum_seq_q,
31+
cum_seq_k,
32+
max_q,
33+
max_k,
34+
is_causal,
35+
return_debug_mask,
36+
*,
37+
scale=None,
38+
window_size_left=None,
39+
window_size_right=None,
40+
seqused_k=None,
41+
alibi_slopes=None,
42+
):
43+
"""FlagGems implementation of ``aten::_flash_attention_forward_no_dropout_inplace``.
44+
45+
This is a specialised variant of ``_flash_attention_forward`` that drops the
46+
``dropout_p`` argument (it is implicitly ``0.0``) and writes the attention
47+
output in-place into ``query``. The heavy lifting is delegated to the
48+
existing Triton FlashAttention kernel (:func:`flash_attention_forward`),
49+
which runs the ``mha_fwd`` Triton kernel.
50+
51+
Args:
52+
query: ``(batch, num_heads, seq_len_q, head_dim)``.
53+
key: ``(batch, num_kv_heads, seq_len_kv, head_dim)``.
54+
value: ``(batch, num_kv_heads, seq_len_kv, head_dim)``.
55+
cum_seq_q / cum_seq_k: optional cumulative sequence lengths (varlen).
56+
max_q / max_k: maximum sequence lengths.
57+
is_causal: whether to apply causal masking.
58+
return_debug_mask: whether to return the debug attention mask.
59+
scale: optional scale factor for the QK dot product.
60+
window_size_left / window_size_right: sliding-window attention sizes.
61+
seqused_k: optional per-batch key sequence lengths.
62+
alibi_slopes: optional ALiBi bias slopes.
63+
64+
Returns:
65+
``(output, softmax_logsumexp, rng_state, unused, debug_attn_mask)``.
66+
``output`` aliases ``query`` (written in-place).
67+
"""
68+
logger.debug("GEMS FLASH_ATTENTION_FORWARD_NO_DROPOUT_INPLACE")
69+
70+
# FlashAttention only supports fp16/bf16 inputs.
71+
assert query.dtype in (
72+
torch.float16,
73+
torch.bfloat16,
74+
), f"expected fp16/bf16 query, got {query.dtype}"
75+
76+
# No dropout for this variant.
77+
dropout_p = 0.0
78+
79+
# Run the existing Triton FlashAttention forward kernel.
80+
#
81+
# ``disable_splitkv`` is set because the split-KV combine kernel of the
82+
# shared ``mha_fwd`` path can leave output rows unwritten for some shapes,
83+
# which would violate the in-place contract (the query must hold the full
84+
# result). The non-split kernel path is numerically exact for the shapes
85+
# exercised by this operator.
86+
out, lse, philox_seed, philox_offset, debug_attn_mask = flash_attention_forward(
87+
query,
88+
key,
89+
value,
90+
cum_seq_q,
91+
cum_seq_k,
92+
max_q,
93+
max_k,
94+
dropout_p,
95+
is_causal,
96+
return_debug_mask,
97+
scale=scale,
98+
window_size_left=window_size_left,
99+
window_size_right=window_size_right,
100+
seqused_k=seqused_k,
101+
alibi_slopes=alibi_slopes,
102+
disable_splitkv=True,
103+
)
104+
105+
# In-place semantics: the result is written back into ``query``.
106+
query.copy_(out)
107+
return query, lse, philox_seed, philox_offset, debug_attn_mask

0 commit comments

Comments
 (0)