JingzeShi commited on
Commit
a61996a
·
verified ·
1 Parent(s): 28eccca

Uploaded using `kernel-builder`.

Browse files
build/torch-cuda/__init__.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .interface import (
2
+ flash_dense_attn_func,
3
+ flash_dense_attn_with_kvcache_func,
4
+ flash_dense_attn_varlen_func,
5
+ flash_dense_attn_varlen_with_kvcache_func,
6
+ flash_sparse_attn_func,
7
+ flash_sparse_attn_with_kvcache_func,
8
+ flash_sparse_attn_varlen_func,
9
+ flash_sparse_attn_varlen_with_kvcache_func,
10
+ flash_gated_attn_func,
11
+ flash_gated_attn_with_kvcache_func,
12
+ flash_gated_attn_varlen_func,
13
+ flash_gated_attn_varlen_with_kvcache_func,
14
+ )
15
+
16
+ __all__ = [
17
+ "flash_dense_attn_func",
18
+ "flash_dense_attn_with_kvcache_func",
19
+ "flash_dense_attn_varlen_func",
20
+ "flash_dense_attn_varlen_with_kvcache_func",
21
+ "flash_sparse_attn_func",
22
+ "flash_sparse_attn_with_kvcache_func",
23
+ "flash_sparse_attn_varlen_func",
24
+ "flash_sparse_attn_varlen_with_kvcache_func",
25
+ "flash_gated_attn_func",
26
+ "flash_gated_attn_with_kvcache_func",
27
+ "flash_gated_attn_varlen_func",
28
+ "flash_gated_attn_varlen_with_kvcache_func",
29
+ ]
build/torch-cuda/_ops.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ def get_backend() -> str:
4
+ """Detect the backend by inspecting torch."""
5
+ import torch
6
+
7
+ if hasattr(torch, "neuron"):
8
+ # Needs to be sorted before specific Torch builds, since Neuron
9
+ # extension can be loaded into e.g. CUDA Torch builds.
10
+ return "neuron"
11
+ elif torch.version.cuda is not None:
12
+ return "cuda"
13
+ elif torch.version.hip is not None:
14
+ return "rocm"
15
+ elif torch.backends.mps.is_available():
16
+ return "metal"
17
+ elif hasattr(torch.version, "xpu") and torch.version.xpu is not None:
18
+ return "xpu"
19
+ else:
20
+ return "cpu"
21
+
22
+
23
+ def _find_ops_name() -> str:
24
+ kernel_name = "flash_sparse_attention"
25
+ unique_id = "7db628a_dirty"
26
+ backend = get_backend()
27
+ return f"_{kernel_name}_{backend}_{unique_id}"
28
+
29
+
30
+ _OPS_NAME = _find_ops_name()
31
+
32
+ ops = getattr(torch.ops, _OPS_NAME)
33
+
34
+ def add_op_namespace_prefix(op_name: str) -> str:
35
+ """
36
+ Prefix op by namespace.
37
+ """
38
+ return f"{_OPS_NAME}::{op_name}"
build/torch-cuda/activations.py ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+ import triton.language as tl
3
+
4
+
5
+ @triton.jit
6
+ def check_inf(x):
7
+ return tl.maximum(x, -1e6)
8
+
9
+
10
+ @triton.jit
11
+ def exp2(x):
12
+ """
13
+ Compute 2^x, using fast hardware approximation on CUDA and default implementation on other backends.
14
+
15
+ :param x: Input tensor of shape [BLOCK_M, BLOCK_N].
16
+
17
+ :return: Tensor of shape [BLOCK_M, BLOCK_N] containing 2^x values.
18
+ """
19
+ if tl.target_info.is_cuda():
20
+ return tl.inline_asm_elementwise(
21
+ "ex2.approx.ftz.f32 $0, $1;",
22
+ "=r, r",
23
+ [x],
24
+ dtype=tl.float32,
25
+ is_pure=True,
26
+ pack=1,
27
+ )
28
+ else:
29
+ return tl.exp2(x)
30
+
31
+
32
+ @triton.jit
33
+ def exp(x):
34
+ """
35
+ Compute e^x, using fast hardware approximation on CUDA and default implementation on other backends.
36
+
37
+ :param x: Input tensor of shape [BLOCK_M, BLOCK_N].
38
+
39
+ :return: Tensor of shape [BLOCK_M, BLOCK_N] containing e^x values.
40
+ """
41
+ log2_e: tl.constexpr = 1.4426950408889634
42
+ x *= log2_e
43
+ return exp2(x)
44
+
45
+
46
+ @triton.jit
47
+ def online_softmax(
48
+ acc_s,
49
+ row_max,
50
+ row_sum,
51
+ scale_log2,
52
+ CHECK_INF: tl.constexpr,
53
+ RESCALE_THRESHOLD: tl.constexpr,
54
+ ):
55
+ """
56
+ Apply online softmax to acc_s, and update row_max and row_sum.
57
+
58
+ :param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
59
+ :param row_max: Current maximum values per row of shape [BLOCK_M], init to -inf.
60
+ :param row_sum: Current sum values per row of shape [BLOCK_M], init to 0.
61
+ :param scale_log2: Log2 of the scaling factor to be applied to acc_s.
62
+ :param CHECK_INF: Boolean flag indicating if -inf row_max should be clamped to 0.
63
+ :param RESCALE_THRESHOLD: Threshold for rescaling to avoid underflow. If <= 0, rescaling is disabled.
64
+
65
+ :return p: Softmax probabilities tensor of shape [BLOCK_M, BLOCK_N].
66
+ :return row_max_new: Updated maximum values per row of shape [BLOCK_M].
67
+ :return row_sum_new: Updated sum values per row of shape [BLOCK_M].
68
+ :return row_scale: Scaling factors per row of shape [BLOCK_M].
69
+ """
70
+ # Compute current row max
71
+ row_max_curr = tl.max(acc_s, axis=1)
72
+
73
+ # Update row max
74
+ row_max_new = tl.maximum(row_max_curr, row_max)
75
+
76
+ # Avoid exp(-inf - (-inf)) = nan by clamping -inf to 0
77
+ if CHECK_INF:
78
+ row_max_new = check_inf(row_max_new)
79
+
80
+ # Compute scaled differences to new row max
81
+ acc_scale_log2 = (row_max - row_max_new) * scale_log2
82
+
83
+ # Compute row scale
84
+ if RESCALE_THRESHOLD > 0.0:
85
+ # Triton can only skip computation at block granularity
86
+ if tl.min(acc_scale_log2) < -RESCALE_THRESHOLD:
87
+ row_scale = exp2(acc_scale_log2)
88
+ else:
89
+ row_max_new = row_max
90
+ row_scale = acc_scale_log2 * 0.0 + 1.0
91
+ else:
92
+ row_scale = exp2(acc_scale_log2)
93
+
94
+ # Compute attention weights
95
+ p = exp2(acc_s * scale_log2 - row_max_new[:, None] * scale_log2)
96
+
97
+ # Update row sum
98
+ row_sum_cur = tl.sum(p, axis=1)
99
+ row_sum_new = row_sum * row_scale + row_sum_cur
100
+
101
+ return p, row_max_new, row_sum_new, row_scale
102
+
103
+
104
+ @triton.jit
105
+ def online_sparse_softmax(
106
+ acc_s,
107
+ row_max,
108
+ row_sum,
109
+ scale_log2,
110
+ softmax_threshold_log2,
111
+ CHECK_INF: tl.constexpr,
112
+ ):
113
+ """
114
+ Apply online sparse softmax to acc_s, and update row_max and row_sum.
115
+
116
+ :param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
117
+ :param row_max: Current maximum values per row of shape [BLOCK_M], init to -inf.
118
+ :param row_sum: Current sum values per row of shape [BLOCK_M], init to 0.
119
+ :param scale_log2: Log2 of the scaling factor to be applied to acc_s.
120
+ :param softmax_threshold_log2: Threshold in log2-domain for block-level skip.
121
+ :param CHECK_INF: Boolean flag indicating if -inf row_max should be clamped to 0.
122
+
123
+ :return p: Softmax probabilities tensor of shape [BLOCK_M, BLOCK_N].
124
+ :return row_max_new: Updated maximum values per row of shape [BLOCK_M].
125
+ :return row_sum_new: Updated sum values per row of shape [BLOCK_M].
126
+ :return row_scale: Scaling factors per row of shape [BLOCK_M].
127
+ :return skip_softmax: Boolean indicating whether this block was skipped.
128
+ """
129
+ # Compute current row max
130
+ row_max_curr = tl.max(acc_s, axis=1)
131
+
132
+ # Update skip condition based on threshold
133
+ row_max_diff_log2 = (row_max_curr - row_max) * scale_log2
134
+ skip_softmax = tl.max(row_max_diff_log2 - softmax_threshold_log2) < 0.0
135
+
136
+ # Return zero attention weights
137
+ if skip_softmax:
138
+ p = acc_s * 0.0
139
+ row_max_new = row_max
140
+ row_sum_new = row_sum
141
+ row_scale = row_max * 0.0 + 1.0
142
+ else:
143
+ # Update row max
144
+ row_max_new = tl.maximum(row_max_curr, row_max)
145
+
146
+ # Avoid exp(-inf - (-inf)) = nan by clamping -inf to 0
147
+ if CHECK_INF:
148
+ row_max_new = check_inf(row_max_new)
149
+
150
+ # Compute scaled differences to new row max
151
+ acc_scale_log2 = (row_max - row_max_new) * scale_log2
152
+
153
+ # Compute row scale
154
+ row_scale = exp2(acc_scale_log2)
155
+
156
+ # Compute attention weights
157
+ p = exp2(acc_s * scale_log2 - row_max_new[:, None] * scale_log2)
158
+
159
+ # Update row sum
160
+ row_sum_cur = tl.sum(p, axis=1)
161
+ row_sum_new = row_sum * row_scale + row_sum_cur
162
+
163
+ return p, row_max_new, row_sum_new, row_scale, skip_softmax
164
+
165
+
166
+ @triton.jit
167
+ def finalize(
168
+ row_max,
169
+ row_sum,
170
+ scale_log2,
171
+ final_scale,
172
+ IS_LOG2: tl.constexpr,
173
+ CHECK_NAN: tl.constexpr,
174
+ ):
175
+ """
176
+ Finalize online softmax by computing output scale and logsumexp.
177
+
178
+ :param row_max: Final maximum values per row of shape [BLOCK_M].
179
+ :param row_sum: Final sum values per row of shape [BLOCK_M].
180
+ :param final_scale: Scaling factor to be applied to the output.
181
+ :param IS_LOG2: Boolean flag indicating if the returned logsumexp should be in log2-space.
182
+ :param CHECK_NAN: Boolean flag indicating if nan values in row_sum should be checked and set to 1 to avoid returning nan.
183
+
184
+ :return row_scale: Final scaling factors per row of shape [BLOCK_M].
185
+ :return lse: Logsumexp values per row of shape [BLOCK_M].
186
+ """
187
+ # if row_sum is zero or nan, set it to 1 to avoid division by zero
188
+ if CHECK_NAN:
189
+ acc_o_is_zero_or_nan = (row_sum == 0.0) | (row_sum != row_sum)
190
+ row_scale = tl.where(acc_o_is_zero_or_nan, 1.0, final_scale / row_sum)
191
+ lse = tl.where(
192
+ acc_o_is_zero_or_nan,
193
+ float("-inf"),
194
+ row_max * scale_log2 + tl.log2(row_sum),
195
+ )
196
+ else:
197
+ row_scale = final_scale / row_sum
198
+ lse = row_max * scale_log2 + tl.log2(row_sum)
199
+ if not IS_LOG2:
200
+ # ln2 = math.log(2.0)
201
+ ln2: tl.constexpr = 0.6931471805599453
202
+ lse *= ln2
203
+ return row_scale, lse
204
+
205
+
206
+ @triton.jit
207
+ def rescale_o(
208
+ acc_o,
209
+ row_scale,
210
+ LAZY_RESCALE: tl.constexpr,
211
+ ):
212
+ """
213
+ Rescale output accumulator by row_scale.
214
+
215
+ :param acc_o: Output accumulator tensor of shape [BLOCK_M, BLOCK_N].
216
+ :param row_scale: Scaling factors per row of shape [BLOCK_M].
217
+ :param LAZY_RESCALE: Boolean flag indicating if rescaling should be applied lazily.
218
+
219
+ :return: Rescaled output accumulator tensor of shape [BLOCK_M, BLOCK_N].
220
+ """
221
+ if LAZY_RESCALE:
222
+ # On some hardware, the check can cause severe performance regression
223
+ if tl.min(row_scale) != 1.0:
224
+ acc_o = acc_o * row_scale[:, None]
225
+ else:
226
+ acc_o = acc_o * row_scale[:, None]
227
+ return acc_o
228
+
229
+
230
+ @triton.jit
231
+ def sigmoid(x):
232
+ """
233
+ Compute sigmoid of x.
234
+
235
+ :param x: Input tensor of shape [BLOCK_M, BLOCK_N].
236
+
237
+ :return: Tensor of shape [BLOCK_M, BLOCK_N] containing sigmoid values.
238
+ """
239
+ return 1.0 / (1.0 + exp(-x))
240
+
241
+
242
+ @triton.jit
243
+ def log_sigmoid(x, FASTMATH: tl.constexpr):
244
+ """
245
+ Compute log-sigmoid of x.
246
+
247
+ :param x: Input tensor of shape [BLOCK_M, BLOCK_N].
248
+ :param FASTMATH: Boolean flag indicating if the fast approximation should be used.
249
+
250
+ :return: Tensor of shape [BLOCK_M, BLOCK_N] containing log-sigmoid values.
251
+ """
252
+ if FASTMATH:
253
+ xc = tl.minimum(tl.abs(x), 4.0)
254
+ xc2 = xc * xc
255
+ out = tl.minimum(x, 0.0) - 0.05674870 * xc2 + 0.37664706 * xc - 0.65169323
256
+ return out
257
+ else:
258
+ out = tl.minimum(x, 0.0) - tl.log(1.0 + exp(-tl.abs(x)))
259
+ return out
260
+
261
+
262
+ @triton.jit
263
+ def online_gate(
264
+ a_max,
265
+ a_min,
266
+ d_max,
267
+ d_min,
268
+ gate_max,
269
+ scale_log2,
270
+ gate_threshold_log2,
271
+ ):
272
+ """
273
+ Determine whether to skip gate computation for the current tile based on the maximum possible gate value.
274
+
275
+ :param a_max: Maximum value of the Alpha tile.
276
+ :param a_min: Minimum value of the Alpha tile.
277
+ :param d_max: Maximum value of the Delta tile.
278
+ :param d_min: Minimum value of the Delta tile.
279
+ :param gate_max: Maximum value of the gate.
280
+ :param scale_log2: Log2 of the scaling factor applied to the gate max.
281
+ :param gate_threshold_log2: Threshold in log2-domain for gate-level skip.
282
+
283
+ :return gate_max_new: Updated gate max value after considering current tile.
284
+ :return skip_gate: Boolean indicating whether to skip the gate computation for this tile.
285
+ """
286
+ gate_max_curr = tl.maximum(
287
+ tl.maximum(a_max * d_max, a_max * d_min),
288
+ tl.maximum(a_min * d_max, a_min * d_min),
289
+ )
290
+ gate_max_diff_log2 = (gate_max_curr - gate_max) * scale_log2
291
+ skip_gate = gate_max_diff_log2 < gate_threshold_log2
292
+ if skip_gate:
293
+ gate_max_new = gate_max
294
+ else:
295
+ gate_max_new = tl.maximum(gate_max_curr, gate_max)
296
+ return gate_max_new, skip_gate
build/torch-cuda/assert_inputs.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+
5
+
6
+ def assert_fwd_inputs(
7
+ query: torch.Tensor,
8
+ key: torch.Tensor,
9
+ value: torch.Tensor,
10
+ query_scale: Optional[torch.Tensor] = None,
11
+ key_scale: Optional[torch.Tensor] = None,
12
+ value_scale: Optional[torch.Tensor] = None,
13
+ window_sizes: Optional[torch.Tensor] = None,
14
+ alpha: Optional[torch.Tensor] = None,
15
+ delta: Optional[torch.Tensor] = None,
16
+ cu_seqlens_q: Optional[torch.Tensor] = None,
17
+ cu_seqlens_k: Optional[torch.Tensor] = None,
18
+ seqused_q: Optional[torch.Tensor] = None,
19
+ seqused_k: Optional[torch.Tensor] = None,
20
+ num_heads_q: int = None,
21
+ num_heads_kv: int = None,
22
+ head_dim: int = None,
23
+ is_quant: bool = False,
24
+ device: torch.device = None,
25
+ ):
26
+ """
27
+ Assert the validity of inputs for the forward kernel.
28
+
29
+ :param query: Query tensor
30
+ :param key: Key tensor
31
+ :param value: Value tensor
32
+ :param query_scale: Optional query scale tensor for quantized inputs
33
+ :param key_scale: Optional key scale tensor for quantized inputs
34
+ :param value_scale: Optional value scale tensor for quantized inputs
35
+ :param window_sizes: Optional window sizes tensor for local attention
36
+ :param alpha: Alpha tensor for gated attention
37
+ :param delta: Delta tensor for gated attention
38
+ :param cu_seqlens_q: Cumulative sequence lengths for queries
39
+ :param cu_seqlens_k: Cumulative sequence lengths for keys
40
+ :param seqused_q: Sequence used for queries
41
+ :param seqused_k: Sequence used for keys
42
+ :param num_heads_q: Number of query heads
43
+ :param num_heads_kv: Number of key/value heads
44
+ :param head_dim: Head dimension
45
+ :param is_quant: Whether the inputs are quantized
46
+ :param device: Device of the tensors
47
+
48
+ :raises AssertionError: If any of the assertions fail
49
+ """
50
+ assert device == query.device == key.device == value.device, (
51
+ "All inputs must be on the same device"
52
+ )
53
+ if is_quant:
54
+ assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
55
+ "Input dtype must be float8_e5m2 or float8_e4m3fn"
56
+ )
57
+ assert (
58
+ query_scale is not None
59
+ and key_scale is not None
60
+ and value_scale is not None
61
+ ), "Scale tensors must be provided for quantized inputs"
62
+ assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
63
+ "Scale tensors must be float16, bfloat16, or float32"
64
+ )
65
+ assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
66
+ "All scale tensors must have the same dtype"
67
+ )
68
+ assert query_scale.device == key_scale.device == value_scale.device == device, (
69
+ "All scale tensors must be on the same device as query/key/value"
70
+ )
71
+ else:
72
+ assert query.dtype in [torch.float16, torch.bfloat16], (
73
+ "Input dtype must be float16 or bfloat16"
74
+ )
75
+ assert query.dtype == key.dtype == value.dtype, (
76
+ "All inputs must have the same dtype"
77
+ )
78
+ assert num_heads_q % num_heads_kv == 0, (
79
+ "num_heads_q must be divisible by num_heads_kv"
80
+ )
81
+ assert head_dim % 16 == 0, (
82
+ "head_dim must be a multiple of 16 for efficient memory access"
83
+ )
84
+ assert head_dim <= 512, (
85
+ "head_dim must be less than or equal to 512 for efficient memory access"
86
+ )
87
+ if alpha is not None and delta is not None:
88
+ assert device == alpha.device == delta.device, (
89
+ "All inputs must be on the same device"
90
+ )
91
+ if cu_seqlens_q is not None and cu_seqlens_k is not None:
92
+ assert device == cu_seqlens_q.device == cu_seqlens_k.device, (
93
+ "All inputs must be on the same device"
94
+ )
95
+ assert cu_seqlens_q.dtype == cu_seqlens_k.dtype == torch.int32, (
96
+ "cu_seqlens_q and cu_seqlens_k must be int32"
97
+ )
98
+ if seqused_q is not None and seqused_k is not None:
99
+ assert device == seqused_q.device == seqused_k.device, (
100
+ "All inputs must be on the same device"
101
+ )
102
+ assert seqused_q.dtype == seqused_k.dtype == torch.int32, (
103
+ "seqused_q and seqused_k must be int32"
104
+ )
105
+ if window_sizes is not None:
106
+ assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
107
+ assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
108
+ "window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
109
+ )
110
+
111
+
112
+ def assert_bwd_inputs(
113
+ query: torch.Tensor,
114
+ key: torch.Tensor,
115
+ value: torch.Tensor,
116
+ out: torch.Tensor,
117
+ dout: torch.Tensor,
118
+ lse: torch.Tensor,
119
+ query_scale: Optional[torch.Tensor] = None,
120
+ key_scale: Optional[torch.Tensor] = None,
121
+ value_scale: Optional[torch.Tensor] = None,
122
+ window_sizes: Optional[torch.Tensor] = None,
123
+ alpha: Optional[torch.Tensor] = None,
124
+ delta: Optional[torch.Tensor] = None,
125
+ cu_seqlens_q: Optional[torch.Tensor] = None,
126
+ cu_seqlens_k: Optional[torch.Tensor] = None,
127
+ seqused_q: Optional[torch.Tensor] = None,
128
+ seqused_k: Optional[torch.Tensor] = None,
129
+ num_heads_q: int = None,
130
+ num_heads_kv: int = None,
131
+ head_dim: int = None,
132
+ is_quant: bool = False,
133
+ device: torch.device = None,
134
+ ):
135
+ """
136
+ Assert the validity of inputs for the backward base kernel.
137
+
138
+ :param query: Query tensor
139
+ :param key: Key tensor
140
+ :param value: Value tensor
141
+ :param out: Output tensor
142
+ :param dout: Gradient of the output tensor
143
+ :param lse: Log-sum-exp tensor
144
+ :param query_scale: Optional query scale tensor for quantized inputs
145
+ :param key_scale: Optional key scale tensor for quantized inputs
146
+ :param value_scale: Optional value scale tensor for quantized inputs
147
+ :param window_sizes: Optional window sizes tensor for local attention
148
+ :param alpha: Alpha tensor for gated attention
149
+ :param delta: Delta tensor for gated attention
150
+ :param cu_seqlens_q: Cumulative sequence lengths for queries
151
+ :param cu_seqlens_k: Cumulative sequence lengths for keys
152
+ :param seqused_q: Sequence used for queries
153
+ :param seqused_k: Sequence used for keys
154
+ :param num_heads_q: Number of query heads
155
+ :param num_heads_kv: Number of key/value heads
156
+ :param head_dim: Head dimension
157
+ :param is_quant: Whether the inputs are quantized
158
+ :param device: Device of the tensors
159
+
160
+ :raises AssertionError: If any of the assertions fail
161
+ """
162
+ assert (
163
+ device
164
+ == query.device
165
+ == key.device
166
+ == value.device
167
+ == out.device
168
+ == dout.device
169
+ == lse.device
170
+ ), "All inputs must be on the same device"
171
+ if is_quant:
172
+ assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
173
+ "Input dtype must be float8_e5m2 or float8_e4m3fn"
174
+ )
175
+ assert (
176
+ query_scale is not None
177
+ and key_scale is not None
178
+ and value_scale is not None
179
+ ), "Scale tensors must be provided for quantized inputs"
180
+ assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
181
+ "Scale tensors must be float16, bfloat16, or float32"
182
+ )
183
+ assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
184
+ "All scale tensors must have the same dtype"
185
+ )
186
+ assert query_scale.device == key_scale.device == value_scale.device == device, (
187
+ "All scale tensors must be on the same device as query/key/value"
188
+ )
189
+ assert query.dtype == key.dtype == value.dtype, (
190
+ "All inputs must have the same dtype"
191
+ )
192
+ else:
193
+ assert query.dtype in [torch.float16, torch.bfloat16], (
194
+ "Input dtype must be float16 or bfloat16"
195
+ )
196
+ assert query.dtype == key.dtype == value.dtype == out.dtype == dout.dtype, (
197
+ "All inputs must have the same dtype"
198
+ )
199
+ assert lse.dtype == torch.float32, (
200
+ "lse must be float32 for numerical stability in backward pass"
201
+ )
202
+ assert num_heads_q % num_heads_kv == 0, (
203
+ "num_heads_q must be divisible by num_heads_kv"
204
+ )
205
+ assert head_dim % 16 == 0, (
206
+ "head_dim must be a multiple of 16 for efficient memory access"
207
+ )
208
+ assert head_dim <= 512, (
209
+ "head_dim must be less than or equal to 512 for efficient memory access"
210
+ )
211
+ if alpha is not None and delta is not None:
212
+ assert device == alpha.device == delta.device, (
213
+ "All inputs must be on the same device"
214
+ )
215
+ if cu_seqlens_q is not None and cu_seqlens_k is not None:
216
+ assert device == cu_seqlens_q.device == cu_seqlens_k.device, (
217
+ "All inputs must be on the same device"
218
+ )
219
+ assert cu_seqlens_q.dtype == cu_seqlens_k.dtype == torch.int32, (
220
+ "cu_seqlens_q and cu_seqlens_k must be int32"
221
+ )
222
+ if seqused_q is not None and seqused_k is not None:
223
+ assert device == seqused_q.device == seqused_k.device, (
224
+ "All inputs must be on the same device"
225
+ )
226
+ assert seqused_q.dtype == seqused_k.dtype == torch.int32, (
227
+ "seqused_q and seqused_k must be int32"
228
+ )
229
+ if window_sizes is not None:
230
+ assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
231
+ assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
232
+ "window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
233
+ )
234
+
235
+
236
+ def assert_dec_inputs(
237
+ query: torch.Tensor,
238
+ key: torch.Tensor,
239
+ value: torch.Tensor,
240
+ query_scale: Optional[torch.Tensor] = None,
241
+ key_scale: Optional[torch.Tensor] = None,
242
+ value_scale: Optional[torch.Tensor] = None,
243
+ window_sizes: Optional[torch.Tensor] = None,
244
+ alpha: Optional[torch.Tensor] = None,
245
+ delta: Optional[torch.Tensor] = None,
246
+ cu_seqlens_k: Optional[torch.Tensor] = None,
247
+ seqused_k: Optional[torch.Tensor] = None,
248
+ num_heads_q: int = None,
249
+ num_heads_kv: int = None,
250
+ head_dim: int = None,
251
+ is_quant: bool = False,
252
+ device: torch.device = None,
253
+ ):
254
+ """
255
+ Assert the validity of inputs for the decode kernel.
256
+
257
+ :param query: Query tensor
258
+ :param key: Key tensor
259
+ :param value: Value tensor
260
+ :param query_scale: Optional query scale tensor for quantized inputs
261
+ :param key_scale: Optional key scale tensor for quantized inputs
262
+ :param value_scale: Optional value scale tensor for quantized inputs
263
+ :param window_sizes: Optional window sizes tensor for local attention
264
+ :param alpha: Alpha tensor for gated attention
265
+ :param delta: Delta tensor for gated attention
266
+ :param cu_seqlens_k: Cumulative sequence lengths for keys
267
+ :param seqused_k: Sequence used for keys
268
+ :param num_heads_q: Number of query heads
269
+ :param num_heads_kv: Number of key/value heads
270
+ :param head_dim: Head dimension
271
+ :param is_quant: Whether the inputs are quantized
272
+ :param device: Device of the tensors
273
+
274
+ :raises AssertionError: If any of the assertions fail
275
+ """
276
+ assert device == query.device == key.device == value.device, (
277
+ "All inputs must be on the same device"
278
+ )
279
+ if is_quant:
280
+ assert query.dtype in [torch.float8_e5m2, torch.float8_e4m3fn], (
281
+ "Input dtype must be float8_e5m2 or float8_e4m3fn"
282
+ )
283
+ assert (
284
+ query_scale is not None
285
+ and key_scale is not None
286
+ and value_scale is not None
287
+ ), "Scale tensors must be provided for quantized inputs"
288
+ assert query_scale.dtype in [torch.float16, torch.bfloat16, torch.float32], (
289
+ "Scale tensors must be float16, bfloat16, or float32"
290
+ )
291
+ assert query_scale.dtype == key_scale.dtype == value_scale.dtype, (
292
+ "All scale tensors must have the same dtype"
293
+ )
294
+ assert query_scale.device == key_scale.device == value_scale.device == device, (
295
+ "All scale tensors must be on the same device as query/key/value"
296
+ )
297
+ else:
298
+ assert query.dtype in [torch.float16, torch.bfloat16], (
299
+ "Input dtype must be float16 or bfloat16"
300
+ )
301
+ assert query.dtype == key.dtype == value.dtype, (
302
+ "All inputs must have the same dtype"
303
+ )
304
+ assert num_heads_q % num_heads_kv == 0, (
305
+ "num_heads_q must be divisible by num_heads_kv"
306
+ )
307
+ assert head_dim % 16 == 0, (
308
+ "head_dim must be a multiple of 16 for efficient memory access"
309
+ )
310
+ assert head_dim <= 512, (
311
+ "head_dim must be less than or equal to 512 for efficient memory access"
312
+ )
313
+ if alpha is not None and delta is not None:
314
+ assert device == alpha.device == delta.device, (
315
+ "All inputs must be on the same device"
316
+ )
317
+ if cu_seqlens_k is not None:
318
+ assert device == cu_seqlens_k.device, "All inputs must be on the same device"
319
+ assert cu_seqlens_k.dtype == torch.int32, "cu_seqlens_k must be int32"
320
+ if seqused_k is not None:
321
+ assert device == seqused_k.device, "All inputs must be on the same device"
322
+ assert seqused_k.dtype == torch.int32, "seqused_k must be int32"
323
+ if window_sizes is not None:
324
+ assert window_sizes.dtype == torch.int32, "window_sizes must be int32"
325
+ assert window_sizes.ndim == 2 and window_sizes.shape[1] == 4, (
326
+ "window_sizes must have shape [num_kv_heads, 4] with columns [window_sink, window_left, window_right, window_dist]"
327
+ )
build/torch-cuda/autotuner.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+
4
+
5
+ def _get_max_shared_mem():
6
+ props = torch.cuda.get_device_properties(torch.cuda.current_device())
7
+ return getattr(
8
+ props, "shared_memory_per_block_optin", props.shared_memory_per_block
9
+ )
10
+
11
+
12
+ def _smem_bytes_fwd(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
13
+ resident = tile_m * tile_k * dtype_bytes
14
+ pipelined = 2 * tile_n * tile_k * dtype_bytes * num_stages
15
+ return resident + pipelined
16
+
17
+
18
+ def _smem_bytes_bwd(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
19
+ resident = 2 * tile_n * tile_k * dtype_bytes
20
+ pipelined = 2 * tile_m * tile_k * dtype_bytes * num_stages
21
+ return resident + pipelined
22
+
23
+
24
+ def _smem_bytes_dec(tile_m, tile_n, tile_k, num_stages, dtype_bytes):
25
+ resident = tile_m * tile_k * dtype_bytes
26
+ pipelined = 2 * tile_n * tile_k * dtype_bytes * num_stages
27
+ return resident + pipelined
28
+
29
+
30
+ def _prune_fwd_configs(configs, named_args, **kwargs):
31
+ tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
32
+ dtype_bytes = named_args["Q"].element_size()
33
+ max_smem = _get_max_shared_mem() - 4 * 1024
34
+ pruned = []
35
+ for cfg in configs:
36
+ tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
37
+ if _smem_bytes_fwd(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
38
+ pruned.append(cfg)
39
+ if not pruned:
40
+ pruned = [
41
+ min(
42
+ configs,
43
+ key=lambda c: _smem_bytes_fwd(
44
+ c.kwargs["TILE_M"],
45
+ c.kwargs["TILE_N"],
46
+ tile_k,
47
+ c.num_stages,
48
+ dtype_bytes,
49
+ ),
50
+ )
51
+ ]
52
+ return pruned
53
+
54
+
55
+ def _prune_bwd_configs(configs, named_args, **kwargs):
56
+ tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
57
+ dtype_bytes = named_args["Q"].element_size()
58
+ max_smem = _get_max_shared_mem() - 4 * 1024
59
+ pruned = []
60
+ for cfg in configs:
61
+ tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
62
+ if _smem_bytes_bwd(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
63
+ pruned.append(cfg)
64
+ if not pruned:
65
+ pruned = [
66
+ min(
67
+ configs,
68
+ key=lambda c: _smem_bytes_bwd(
69
+ c.kwargs["TILE_M"],
70
+ c.kwargs["TILE_N"],
71
+ tile_k,
72
+ c.num_stages,
73
+ dtype_bytes,
74
+ ),
75
+ )
76
+ ]
77
+ return pruned
78
+
79
+
80
+ def _prune_dec_configs(configs, named_args, **kwargs):
81
+ tile_k = kwargs.get("TILE_K", named_args.get("TILE_K", 128))
82
+ seqlen_q = kwargs.get("seqlen_q", named_args.get("seqlen_q", None))
83
+ dtype_bytes = named_args["Q"].element_size()
84
+ max_smem = _get_max_shared_mem() - 4 * 1024
85
+ pruned = []
86
+ for cfg in configs:
87
+ tm, tn, ns = cfg.kwargs["TILE_M"], cfg.kwargs["TILE_N"], cfg.num_stages
88
+ if seqlen_q is not None and tm > seqlen_q:
89
+ continue
90
+ if _smem_bytes_dec(tm, tn, tile_k, ns, dtype_bytes) <= max_smem:
91
+ pruned.append(cfg)
92
+ if not pruned:
93
+ pruned = [
94
+ min(
95
+ configs,
96
+ key=lambda c: _smem_bytes_dec(
97
+ c.kwargs["TILE_M"],
98
+ c.kwargs["TILE_N"],
99
+ tile_k,
100
+ c.num_stages,
101
+ dtype_bytes,
102
+ ),
103
+ )
104
+ ]
105
+ return pruned
106
+
107
+
108
+ def get_fwd_dense_autotune_configs():
109
+ configs = []
110
+ for tile_m in [64, 128, 256]:
111
+ for tile_n in [32, 64, 128]:
112
+ for num_warps in [4, 8]:
113
+ for num_stages in [1, 2, 3]:
114
+ configs.append(
115
+ triton.Config(
116
+ {"TILE_M": tile_m, "TILE_N": tile_n},
117
+ num_warps=num_warps,
118
+ num_stages=num_stages,
119
+ num_ctas=1,
120
+ )
121
+ )
122
+ return configs
123
+
124
+
125
+ def get_fwd_sparse_autotune_configs():
126
+ configs = []
127
+ for tile_m in [64, 128, 256]:
128
+ for tile_n in [32, 64, 128]:
129
+ for num_warps in [4, 8]:
130
+ for num_stages in [1, 2, 3]:
131
+ configs.append(
132
+ triton.Config(
133
+ {"TILE_M": tile_m, "TILE_N": tile_n},
134
+ num_warps=num_warps,
135
+ num_stages=num_stages,
136
+ num_ctas=1,
137
+ )
138
+ )
139
+ return configs
140
+
141
+
142
+ def get_fwd_gated_autotune_configs():
143
+ configs = []
144
+ for tile_m in [64, 128, 256]:
145
+ for tile_n in [32, 64, 128]:
146
+ for num_warps in [4, 8]:
147
+ for num_stages in [1, 2, 3]:
148
+ configs.append(
149
+ triton.Config(
150
+ {"TILE_M": tile_m, "TILE_N": tile_n},
151
+ num_warps=num_warps,
152
+ num_stages=num_stages,
153
+ num_ctas=1,
154
+ )
155
+ )
156
+ return configs
157
+
158
+
159
+ def get_bwd_dense_autotune_configs():
160
+ configs = []
161
+ for tile_m in [32, 64, 128]:
162
+ for tile_n in [64, 128, 256]:
163
+ for num_warps in [4, 8]:
164
+ for num_stages in [1, 2, 3]:
165
+ configs.append(
166
+ triton.Config(
167
+ {"TILE_M": tile_m, "TILE_N": tile_n},
168
+ num_warps=num_warps,
169
+ num_stages=num_stages,
170
+ num_ctas=1,
171
+ )
172
+ )
173
+ return configs
174
+
175
+
176
+ def get_bwd_sparse_autotune_configs():
177
+ configs = []
178
+ for tile_m in [32, 64, 128]:
179
+ for tile_n in [64, 128, 256]:
180
+ for num_warps in [4, 8]:
181
+ for num_stages in [1, 2, 3]:
182
+ configs.append(
183
+ triton.Config(
184
+ {"TILE_M": tile_m, "TILE_N": tile_n},
185
+ num_warps=num_warps,
186
+ num_stages=num_stages,
187
+ num_ctas=1,
188
+ )
189
+ )
190
+ return configs
191
+
192
+
193
+ def get_bwd_gated_autotune_configs():
194
+ configs = []
195
+ for tile_m in [32, 64, 128]:
196
+ for tile_n in [64, 128, 256]:
197
+ for num_warps in [4, 8]:
198
+ for num_stages in [1, 2, 3]:
199
+ configs.append(
200
+ triton.Config(
201
+ {"TILE_M": tile_m, "TILE_N": tile_n},
202
+ num_warps=num_warps,
203
+ num_stages=num_stages,
204
+ num_ctas=1,
205
+ )
206
+ )
207
+ return configs
208
+
209
+
210
+ def get_dec_dense_autotune_configs():
211
+ configs = []
212
+ for tile_m in [16, 32, 64]:
213
+ for tile_n in [64, 128, 256]:
214
+ for num_warps in [4, 8]:
215
+ for num_stages in [1, 2, 3]:
216
+ configs.append(
217
+ triton.Config(
218
+ {"TILE_M": tile_m, "TILE_N": tile_n},
219
+ num_warps=num_warps,
220
+ num_stages=num_stages,
221
+ num_ctas=1,
222
+ )
223
+ )
224
+ return configs
225
+
226
+
227
+ def get_dec_sparse_autotune_configs():
228
+ configs = []
229
+ for tile_m in [16, 32, 64]:
230
+ for tile_n in [64, 128, 256]:
231
+ for num_warps in [4, 8]:
232
+ for num_stages in [1, 2, 3]:
233
+ configs.append(
234
+ triton.Config(
235
+ {"TILE_M": tile_m, "TILE_N": tile_n},
236
+ num_warps=num_warps,
237
+ num_stages=num_stages,
238
+ num_ctas=1,
239
+ )
240
+ )
241
+ return configs
242
+
243
+
244
+ def get_dec_gated_autotune_configs():
245
+ configs = []
246
+ for tile_m in [16, 32, 64]:
247
+ for tile_n in [64, 128, 256]:
248
+ for num_warps in [4, 8]:
249
+ for num_stages in [1, 2, 3]:
250
+ configs.append(
251
+ triton.Config(
252
+ {"TILE_M": tile_m, "TILE_N": tile_n},
253
+ num_warps=num_warps,
254
+ num_stages=num_stages,
255
+ num_ctas=1,
256
+ )
257
+ )
258
+ return configs
259
+
260
+
261
+ def make_fwd_dense_autotuned_kernel(jit_kernel):
262
+ configs = get_fwd_dense_autotune_configs()
263
+ return triton.autotune(
264
+ configs=configs,
265
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
266
+ prune_configs_by={"early_config_prune": _prune_fwd_configs},
267
+ )(jit_kernel)
268
+
269
+
270
+ def make_fwd_sparse_autotuned_kernel(jit_kernel):
271
+ configs = get_fwd_sparse_autotune_configs()
272
+ return triton.autotune(
273
+ configs=configs,
274
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
275
+ prune_configs_by={"early_config_prune": _prune_fwd_configs},
276
+ )(jit_kernel)
277
+
278
+
279
+ def make_fwd_gated_autotuned_kernel(jit_kernel):
280
+ configs = get_fwd_gated_autotune_configs()
281
+ return triton.autotune(
282
+ configs=configs,
283
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
284
+ prune_configs_by={"early_config_prune": _prune_fwd_configs},
285
+ )(jit_kernel)
286
+
287
+
288
+ def make_bwd_dense_autotuned_kernel(jit_kernel):
289
+ configs = get_bwd_dense_autotune_configs()
290
+ return triton.autotune(
291
+ configs=configs,
292
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
293
+ prune_configs_by={"early_config_prune": _prune_bwd_configs},
294
+ )(jit_kernel)
295
+
296
+
297
+ def make_bwd_sparse_autotuned_kernel(jit_kernel):
298
+ configs = get_bwd_sparse_autotune_configs()
299
+ return triton.autotune(
300
+ configs=configs,
301
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
302
+ prune_configs_by={"early_config_prune": _prune_bwd_configs},
303
+ )(jit_kernel)
304
+
305
+
306
+ def make_bwd_gated_autotuned_kernel(jit_kernel):
307
+ configs = get_bwd_gated_autotune_configs()
308
+ return triton.autotune(
309
+ configs=configs,
310
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_CAUSAL", "IS_LOCAL"],
311
+ prune_configs_by={"early_config_prune": _prune_bwd_configs},
312
+ )(jit_kernel)
313
+
314
+
315
+ def make_dec_dense_autotuned_kernel(jit_kernel):
316
+ configs = get_dec_dense_autotune_configs()
317
+ return triton.autotune(
318
+ configs=configs,
319
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
320
+ prune_configs_by={"early_config_prune": _prune_dec_configs},
321
+ )(jit_kernel)
322
+
323
+
324
+ def make_dec_sparse_autotuned_kernel(jit_kernel):
325
+ configs = get_dec_sparse_autotune_configs()
326
+ return triton.autotune(
327
+ configs=configs,
328
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
329
+ prune_configs_by={"early_config_prune": _prune_dec_configs},
330
+ )(jit_kernel)
331
+
332
+
333
+ def make_dec_gated_autotuned_kernel(jit_kernel):
334
+ configs = get_dec_gated_autotune_configs()
335
+ return triton.autotune(
336
+ configs=configs,
337
+ key=["SEQLEN_Q_CACHE", "SEQLEN_K_CACHE", "TILE_K", "IS_LOCAL"],
338
+ prune_configs_by={"early_config_prune": _prune_dec_configs},
339
+ )(jit_kernel)
340
+
341
+
342
+ class AutotunedKernel:
343
+ STRIP_KWARGS = {"TILE_M", "TILE_N", "num_warps", "num_stages", "num_ctas"}
344
+
345
+ def __init__(self, autotuned_kernel):
346
+ self._autotuned = autotuned_kernel
347
+
348
+ def __getitem__(self, grid):
349
+ autotuned = self._autotuned
350
+
351
+ class _Launcher:
352
+ def __call__(_, *args, **kwargs):
353
+ for key in AutotunedKernel.STRIP_KWARGS:
354
+ kwargs.pop(key, None)
355
+ return autotuned[grid](*args, **kwargs)
356
+
357
+ return _Launcher()
358
+
359
+ def __getattr__(self, name):
360
+ return getattr(self._autotuned, name)
build/torch-cuda/block_info.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+ import triton.language as tl
3
+
4
+
5
+ @triton.jit
6
+ def get_n_block_min_max(
7
+ seqlen_q,
8
+ seqlen_k,
9
+ m_block,
10
+ split_idx,
11
+ num_splits,
12
+ window_size_sink,
13
+ window_size_left,
14
+ window_size_right,
15
+ window_size_dist,
16
+ TILE_N: tl.constexpr,
17
+ TILE_M: tl.constexpr,
18
+ IS_CAUSAL: tl.constexpr,
19
+ IS_LOCAL: tl.constexpr,
20
+ IS_SPLIT_KV: tl.constexpr,
21
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
22
+ ):
23
+ n_block_max = tl.cdiv(seqlen_k, TILE_N)
24
+ n_block_min = 0
25
+ n_block_window_max = n_block_max
26
+ n_block_window_min = n_block_min
27
+ n_block_sink_min = 0
28
+ n_block_sink_max = 0
29
+ if IS_CAUSAL or IS_LOCAL:
30
+ m_idx_max = (m_block + 1) * TILE_M
31
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
32
+ m_idx_max = tl.cdiv(m_idx_max, QHEAD_PER_KVHEAD_PACKGQA)
33
+ m_idx_max = tl.minimum(m_idx_max, seqlen_q)
34
+ n_idx = m_idx_max + seqlen_k - seqlen_q
35
+ n_block_max = tl.minimum(n_block_max, tl.cdiv(n_idx, TILE_N))
36
+ if IS_LOCAL:
37
+ n_idx_right = n_idx - window_size_dist - window_size_right
38
+ n_block_window_max = tl.minimum(
39
+ n_block_window_max, tl.maximum(tl.cdiv(n_idx_right, TILE_N), 0)
40
+ )
41
+ if IS_LOCAL:
42
+ n_block_sink_max = tl.minimum(
43
+ tl.cdiv(window_size_sink, TILE_N),
44
+ tl.maximum(tl.cdiv(n_idx, TILE_N), 0),
45
+ )
46
+ n_block_sink_exclude_max = tl.cdiv(window_size_sink, TILE_N)
47
+ m_idx_min = m_block * TILE_M
48
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
49
+ m_idx_min = m_idx_min // QHEAD_PER_KVHEAD_PACKGQA
50
+ n_idx = m_idx_min + seqlen_k - seqlen_q
51
+ n_idx_dist = n_idx - window_size_dist
52
+ n_block_min = tl.maximum(n_idx_dist // TILE_N, 0)
53
+ n_block_min = tl.maximum(n_block_min, n_block_sink_exclude_max)
54
+ n_idx_left = n_idx - window_size_dist - window_size_right - window_size_left
55
+ n_block_window_min = tl.maximum(n_idx_left // TILE_N, 0)
56
+ n_block_window_min = tl.maximum(n_block_window_min, n_block_sink_exclude_max)
57
+ if IS_SPLIT_KV:
58
+ if IS_LOCAL:
59
+ n_block_min = n_block_window_min
60
+ n_block_max_with_diag = n_block_max
61
+ n_block_max = tl.maximum(n_block_window_max, n_block_min)
62
+ total_n_blocks = tl.maximum(n_block_max - n_block_min, 0)
63
+ base = total_n_blocks // num_splits
64
+ extra = total_n_blocks % num_splits
65
+ n_block_min_new = n_block_min + tl.where(
66
+ split_idx < extra,
67
+ split_idx * (base + 1),
68
+ extra * (base + 1) + (split_idx - extra) * base,
69
+ )
70
+ n_block_count = tl.where(split_idx < extra, base + 1, base)
71
+ n_block_max = tl.minimum(n_block_min_new + n_block_count, n_block_max)
72
+ n_block_min = n_block_min_new
73
+ if IS_LOCAL:
74
+ n_block_max = tl.where(
75
+ split_idx >= num_splits - 1,
76
+ n_block_max_with_diag,
77
+ n_block_max,
78
+ )
79
+ if IS_SPLIT_KV:
80
+ n_block_sink_max = tl.where(split_idx == 0, n_block_sink_max, 0)
81
+ return (
82
+ n_block_min,
83
+ n_block_max,
84
+ n_block_window_min,
85
+ n_block_window_max,
86
+ n_block_sink_min,
87
+ n_block_sink_max,
88
+ )
89
+
90
+
91
+ @triton.jit
92
+ def get_m_block_min_max(
93
+ seqlen_q,
94
+ seqlen_k,
95
+ n_block,
96
+ split_idx,
97
+ num_splits,
98
+ window_size_sink,
99
+ window_size_left,
100
+ window_size_right,
101
+ window_size_dist,
102
+ TILE_N: tl.constexpr,
103
+ TILE_M: tl.constexpr,
104
+ IS_CAUSAL: tl.constexpr,
105
+ IS_LOCAL: tl.constexpr,
106
+ IS_SPLIT_QO: tl.constexpr,
107
+ ):
108
+ m_block_max = tl.cdiv(seqlen_q, TILE_M)
109
+ m_block_min = 0
110
+ m_block_window_max = m_block_max
111
+ m_block_window_min = m_block_min
112
+ m_block_sink_min = 0
113
+ m_block_sink_max = 0
114
+ if IS_CAUSAL or IS_LOCAL:
115
+ n_idx_min = n_block * TILE_N
116
+ m_idx = n_idx_min + seqlen_q - seqlen_k
117
+ m_block_min = tl.maximum(m_block_min, m_idx // TILE_M)
118
+ if IS_LOCAL:
119
+ m_idx_right = m_idx + window_size_dist + window_size_right
120
+ m_block_window_min = tl.maximum(m_block_window_min, m_idx_right // TILE_M)
121
+ if IS_LOCAL:
122
+ n_block_sink_exclude_max = tl.cdiv(window_size_sink, TILE_N)
123
+ is_sink_block = n_block < n_block_sink_exclude_max
124
+ n_idx_min = n_block * TILE_N
125
+ m_idx_sink = n_idx_min + seqlen_q - seqlen_k
126
+ m_block_sink_min = tl.maximum(m_idx_sink // TILE_M, 0)
127
+ m_block_sink_max = tl.where(is_sink_block, tl.cdiv(seqlen_q, TILE_M), 0)
128
+ m_block_sink_min = tl.where(is_sink_block, m_block_sink_min, 0)
129
+ n_idx_max = (n_block + 1) * TILE_N
130
+ m_idx = n_idx_max + seqlen_q - seqlen_k
131
+ m_idx_dist = m_idx + window_size_dist
132
+ m_block_max = tl.minimum(m_block_max, tl.cdiv(m_idx_dist, TILE_M))
133
+ m_idx_left = m_idx + window_size_dist + window_size_right + window_size_left
134
+ m_block_window_max = tl.minimum(m_block_window_max, tl.cdiv(m_idx_left, TILE_M))
135
+ m_block_min = tl.where(is_sink_block, 0, m_block_min)
136
+ m_block_max = tl.where(is_sink_block, 0, m_block_max)
137
+ m_block_window_min = tl.where(is_sink_block, 0, m_block_window_min)
138
+ m_block_window_max = tl.where(is_sink_block, 0, m_block_window_max)
139
+ if IS_SPLIT_QO:
140
+ if IS_LOCAL:
141
+ n_idx_max = (n_block + 1) * TILE_N
142
+ m_idx = n_idx_max + seqlen_q - seqlen_k
143
+ m_block_min_no_mask = tl.maximum(m_block_min, tl.cdiv(m_idx, TILE_M))
144
+ m_block_window_min = tl.maximum(m_block_window_min, m_block_min_no_mask)
145
+ win_blocks = tl.maximum(m_block_window_max - m_block_window_min, 0)
146
+ base = win_blocks // num_splits
147
+ extra = win_blocks % num_splits
148
+ win_off = tl.where(
149
+ split_idx < extra,
150
+ split_idx * (base + 1),
151
+ extra * (base + 1) + (split_idx - extra) * base,
152
+ )
153
+ win_cnt = tl.where(split_idx < extra, base + 1, base)
154
+ m_block_window_min = m_block_window_min + win_off
155
+ m_block_window_max = tl.minimum(
156
+ m_block_window_min + win_cnt, m_block_window_max
157
+ )
158
+ # First split keeps diagonal, others skip it
159
+ m_block_min = tl.where(split_idx == 0, m_block_min, m_block_max)
160
+ m_block_sink_max = tl.where(split_idx == 0, m_block_sink_max, 0)
161
+ m_block_sink_min = tl.where(split_idx == 0, m_block_sink_min, 0)
162
+ else:
163
+ total_m_blocks = tl.maximum(m_block_max - m_block_min, 0)
164
+ base = total_m_blocks // num_splits
165
+ extra = total_m_blocks % num_splits
166
+ m_block_min_new = m_block_min + tl.where(
167
+ split_idx < extra,
168
+ split_idx * (base + 1),
169
+ extra * (base + 1) + (split_idx - extra) * base,
170
+ )
171
+ m_block_count = tl.where(split_idx < extra, base + 1, base)
172
+ m_block_max = tl.minimum(m_block_min_new + m_block_count, m_block_max)
173
+ m_block_min = m_block_min_new
174
+ return (
175
+ m_block_min,
176
+ m_block_max,
177
+ m_block_window_min,
178
+ m_block_window_max,
179
+ m_block_sink_min,
180
+ m_block_sink_max,
181
+ )
182
+
183
+
184
+ @triton.jit
185
+ def get_n_block_min_causal_local_mask(
186
+ seqlen_q,
187
+ seqlen_k,
188
+ m_block,
189
+ n_block_min,
190
+ window_size_right,
191
+ window_size_dist,
192
+ TILE_N: tl.constexpr,
193
+ TILE_M: tl.constexpr,
194
+ IS_LOCAL: tl.constexpr,
195
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
196
+ ):
197
+ m_idx_min = m_block * TILE_M
198
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
199
+ m_idx_min = m_idx_min // QHEAD_PER_KVHEAD_PACKGQA
200
+ n_idx = m_idx_min + seqlen_k - seqlen_q
201
+ n_idx_right = (
202
+ n_idx if not IS_LOCAL else n_idx - window_size_dist - window_size_right
203
+ )
204
+ return tl.maximum(n_block_min, n_idx_right // TILE_N)
205
+
206
+
207
+ @triton.jit
208
+ def get_n_block_min_before_local_mask(
209
+ seqlen_q,
210
+ seqlen_k,
211
+ m_block,
212
+ n_block_min,
213
+ window_size_left,
214
+ window_size_right,
215
+ window_size_dist,
216
+ TILE_N: tl.constexpr,
217
+ TILE_M: tl.constexpr,
218
+ IS_LOCAL: tl.constexpr,
219
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
220
+ ):
221
+ if not IS_LOCAL:
222
+ return n_block_min
223
+ else:
224
+ m_idx_max = (m_block + 1) * TILE_M
225
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
226
+ m_idx_max = tl.cdiv(m_idx_max, QHEAD_PER_KVHEAD_PACKGQA)
227
+ m_idx_max = tl.minimum(m_idx_max, seqlen_q)
228
+ n_idx = m_idx_max + seqlen_k - seqlen_q
229
+ n_idx_left = n_idx - window_size_dist - window_size_right - window_size_left
230
+ return tl.maximum(n_block_min, tl.cdiv(n_idx_left, TILE_N))
231
+
232
+
233
+ @triton.jit
234
+ def get_m_block_min_causal_local_mask(
235
+ seqlen_q,
236
+ seqlen_k,
237
+ n_block,
238
+ m_block_min,
239
+ window_size_right,
240
+ window_size_dist,
241
+ TILE_N: tl.constexpr,
242
+ TILE_M: tl.constexpr,
243
+ IS_CAUSAL: tl.constexpr,
244
+ IS_LOCAL: tl.constexpr,
245
+ ):
246
+ if not IS_CAUSAL and not IS_LOCAL:
247
+ return m_block_min
248
+ else:
249
+ n_idx_max = (n_block + 1) * TILE_N
250
+ m_idx = n_idx_max + seqlen_q - seqlen_k
251
+ if IS_LOCAL:
252
+ m_idx_right = m_idx + window_size_dist + window_size_right
253
+ else:
254
+ m_idx_right = m_idx
255
+ return tl.maximum(m_block_min, tl.cdiv(m_idx_right, TILE_M))
256
+
257
+
258
+ @triton.jit
259
+ def get_m_block_max_before_local_mask(
260
+ seqlen_q,
261
+ seqlen_k,
262
+ n_block,
263
+ m_block_max,
264
+ window_size_left,
265
+ window_size_right,
266
+ window_size_dist,
267
+ TILE_N: tl.constexpr,
268
+ TILE_M: tl.constexpr,
269
+ IS_LOCAL: tl.constexpr,
270
+ ):
271
+ if not IS_LOCAL:
272
+ return m_block_max
273
+ else:
274
+ n_idx_min = n_block * TILE_N
275
+ m_idx = n_idx_min + seqlen_q - seqlen_k
276
+ m_idx_left = m_idx + window_size_dist + window_size_right + window_size_left
277
+ return tl.minimum(m_block_max, m_idx_left // TILE_M)
build/torch-cuda/cache_utils.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import inspect
3
+ from collections import OrderedDict
4
+
5
+ import torch
6
+
7
+
8
+ @functools.lru_cache(maxsize=8)
9
+ def get_device_num_sms(device: torch.device) -> int:
10
+ """
11
+ Get the SM count for a given device.
12
+
13
+ :param device: Torch device.
14
+
15
+ :return num_sms: Number of streaming multiprocessors.
16
+ """
17
+ return torch.cuda.get_device_properties(device).multi_processor_count
18
+
19
+
20
+ @functools.lru_cache(maxsize=8)
21
+ def get_device_arch(device: torch.device) -> int:
22
+ """
23
+ Get the architecture for a given device.
24
+
25
+ :param device: Torch device.
26
+
27
+ :return arch: Architecture model as a number.
28
+ """
29
+ if device.type == "cuda":
30
+ major, minor = torch.cuda.get_device_capability(device)
31
+ sm = major * 10 + minor
32
+ return sm if sm >= 80 else -1
33
+ if device.type in {"xpu", "mps", "cpu"}:
34
+ return -1
35
+ raise ValueError(f"Unsupported device: {device}")
36
+
37
+
38
+ def cache_launch_config(fn):
39
+ """
40
+ Cache launch-config lookups by (arch, *args, **kwargs).
41
+
42
+ :param fn: Launch-config function to wrap.
43
+
44
+ :return wrapper: Wrapped function with cache.
45
+ """
46
+ params = list(inspect.signature(fn).parameters.keys())
47
+ device_idx = params.index("device") if "device" in params else None
48
+ arch_idx = params.index("arch") if "arch" in params else None
49
+ cached = functools.lru_cache(maxsize=None)(
50
+ lambda _arch, *args, **kwargs: fn(*args, **kwargs)
51
+ )
52
+
53
+ @functools.wraps(fn)
54
+ def wrapper(*args, **kwargs):
55
+ device = (
56
+ args[device_idx]
57
+ if (device_idx is not None and device_idx < len(args))
58
+ else kwargs.get("device")
59
+ )
60
+ arch = (
61
+ args[arch_idx]
62
+ if (arch_idx is not None and arch_idx < len(args))
63
+ else kwargs.get("arch")
64
+ )
65
+ return cached((device.type, arch), *args, **kwargs)
66
+
67
+ return wrapper
68
+
69
+
70
+ def cache_launch_grid(fn, maxsize: int = 512):
71
+ """
72
+ Bounded cache for Triton grid-factory closures.
73
+
74
+ :param fn: Grid-factory function to wrap.
75
+ :param maxsize: Maximum number of cached entries.
76
+
77
+ :return wrapper: Wrapped function.
78
+ """
79
+ return functools.lru_cache(maxsize=maxsize)(fn)
80
+
81
+
82
+ class CachedKernel:
83
+ """
84
+ Wrap a `triton.jit`-decorated function so that each launch reuses a
85
+ previously-compiled `CompiledKernel` when the specialization matches.
86
+ """
87
+
88
+ __slots__ = ("_kernel", "_cache")
89
+
90
+ def __init__(self, kernel):
91
+ self._kernel = kernel
92
+ self._cache = OrderedDict()
93
+
94
+ def __getitem__(self, grid):
95
+ kernel = self._kernel
96
+ kernel_warmup = kernel.warmup
97
+ kernel_arg_names = kernel.arg_names
98
+ cache = self._cache
99
+ maxsize = 1024
100
+ tensor_indices = None
101
+ static_grid = None
102
+
103
+ if not callable(grid):
104
+ static_grid = tuple(grid)
105
+ if len(static_grid) < 3:
106
+ static_grid = static_grid + (1,) * (3 - len(static_grid))
107
+
108
+ def launcher(
109
+ *args,
110
+ num_warps: int = 4,
111
+ num_stages: int = 1,
112
+ num_ctas: int = 1,
113
+ **constexprs,
114
+ ):
115
+ nonlocal tensor_indices
116
+
117
+ if static_grid is not None:
118
+ launch_grid = static_grid
119
+ else:
120
+ launch_grid = tuple(grid(constexprs))
121
+ if len(launch_grid) < 3:
122
+ launch_grid = launch_grid + (1,) * (3 - len(launch_grid))
123
+
124
+ if tensor_indices is None:
125
+ tensor_indices = tuple(
126
+ i for i, a in enumerate(args) if torch.is_tensor(a)
127
+ )
128
+
129
+ key = (
130
+ tuple((args[i].dtype, args[i].device.type) for i in tensor_indices),
131
+ tuple(constexprs.values()) if constexprs else (),
132
+ num_warps,
133
+ num_stages,
134
+ num_ctas,
135
+ )
136
+
137
+ compiled = cache.get(key)
138
+ if compiled is None:
139
+ compiled = kernel_warmup(
140
+ *args,
141
+ grid=launch_grid,
142
+ num_warps=num_warps,
143
+ num_stages=num_stages,
144
+ num_ctas=num_ctas,
145
+ **constexprs,
146
+ )
147
+ cache[key] = compiled
148
+ if len(cache) > maxsize:
149
+ cache.popitem(last=False)
150
+ else:
151
+ cache.move_to_end(key)
152
+
153
+ runner = compiled[launch_grid]
154
+
155
+ if not constexprs:
156
+ runner(*args)
157
+ return
158
+
159
+ args_iter = iter(args)
160
+ full_args = [
161
+ constexprs[name] if name in constexprs else next(args_iter)
162
+ for name in kernel_arg_names
163
+ ]
164
+ runner(*full_args)
165
+
166
+ return launcher
167
+
168
+ def __getattr__(self, name):
169
+ return getattr(self._kernel, name)
170
+
171
+
172
+ def wrap_kernel(kernel):
173
+ """
174
+ Wrap a `triton.jit` kernel for compiled-kernel caching.
175
+
176
+ :param kernel: Triton JIT kernel.
177
+
178
+ :return wrapped: Wrapped kernel preserving the `kernel[grid](...)` syntax.
179
+ """
180
+ return CachedKernel(kernel)
build/torch-cuda/flash_bwd_postprocess.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ utils,
9
+ cache_utils,
10
+ launch_grid,
11
+ seqlen_info,
12
+ kernel_repr,
13
+ )
14
+
15
+
16
+ @triton.jit(repr=kernel_repr.bwd_postprocess_repr)
17
+ def _bwd_postprocess_kernel(
18
+ dQaccum,
19
+ dQ,
20
+ dAaccum,
21
+ dA,
22
+ stride_dqab,
23
+ stride_dqah,
24
+ stride_dqam,
25
+ stride_dqb,
26
+ stride_dqh,
27
+ stride_dqm,
28
+ stride_daab,
29
+ stride_daah,
30
+ stride_daam,
31
+ stride_dab,
32
+ stride_dah,
33
+ stride_dam,
34
+ cu_seqlens_q,
35
+ seqused_q,
36
+ seqlen_q,
37
+ head_dim,
38
+ head_dim_rounded,
39
+ scale,
40
+ HAS_CU_SEQLENS_Q: tl.constexpr,
41
+ HAS_SEQUSED_Q: tl.constexpr,
42
+ HAS_DA: tl.constexpr,
43
+ TILE_M: tl.constexpr,
44
+ TILE_K: tl.constexpr,
45
+ ):
46
+ m_block = tl.program_id(0)
47
+ head_idx = tl.program_id(1)
48
+ batch_idx = tl.program_id(2)
49
+
50
+ # Get seqlen info for this batch
51
+ offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
52
+ batch_idx=batch_idx,
53
+ seqlen_static=seqlen_q,
54
+ cu_seqlens=cu_seqlens_q,
55
+ seqused=seqused_q,
56
+ HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
57
+ HAS_SEQUSED=HAS_SEQUSED_Q,
58
+ )
59
+ padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
60
+
61
+ # Initialize base pointers
62
+ dq_accum_base = seqlen_info.offset_batch_Q(
63
+ dQaccum + head_idx * stride_dqah,
64
+ batch_idx,
65
+ offset_q,
66
+ padded_offset_q,
67
+ stride_dqab,
68
+ stride_dqam,
69
+ HAS_CU_SEQLENS_Q,
70
+ USE_PADDED=True,
71
+ )
72
+ dq_base = seqlen_info.offset_batch_Q(
73
+ dQ + head_idx * stride_dqh,
74
+ batch_idx,
75
+ offset_q,
76
+ padded_offset_q,
77
+ stride_dqb,
78
+ stride_dqm,
79
+ HAS_CU_SEQLENS_Q,
80
+ USE_PADDED=False,
81
+ )
82
+
83
+ # Create descriptors
84
+ dq_accum_desc = tl.make_tensor_descriptor(
85
+ base=dq_accum_base,
86
+ shape=[actual_seqlen_q, head_dim_rounded],
87
+ strides=[stride_dqam, 1],
88
+ block_shape=[TILE_M, TILE_K],
89
+ )
90
+ dq_desc = tl.make_tensor_descriptor(
91
+ base=dq_base,
92
+ shape=[actual_seqlen_q, head_dim],
93
+ strides=[stride_dqm, 1],
94
+ block_shape=[TILE_M, TILE_K],
95
+ )
96
+
97
+ # Load accumulators
98
+ acc_dq = dq_accum_desc.load([m_block * TILE_M, 0])
99
+
100
+ # Scale dq
101
+ dq = (acc_dq * scale).to(dQ.dtype.element_ty)
102
+
103
+ # Store dq
104
+ dq_desc.store([m_block * TILE_M, 0], dq)
105
+
106
+ if HAS_DA:
107
+ da_accum_base = seqlen_info.offset_batch_Q(
108
+ dAaccum + head_idx * stride_daah,
109
+ batch_idx,
110
+ offset_q,
111
+ padded_offset_q,
112
+ stride_daab,
113
+ stride_daam,
114
+ HAS_CU_SEQLENS_Q,
115
+ USE_PADDED=True,
116
+ )
117
+ da_base = seqlen_info.offset_batch_Q(
118
+ dA + head_idx * stride_dah,
119
+ batch_idx,
120
+ offset_q,
121
+ padded_offset_q,
122
+ stride_dab,
123
+ stride_dam,
124
+ HAS_CU_SEQLENS_Q,
125
+ USE_PADDED=False,
126
+ )
127
+
128
+ da_accum_desc = tl.make_tensor_descriptor(
129
+ base=da_accum_base,
130
+ shape=[actual_seqlen_q],
131
+ strides=[stride_daam],
132
+ block_shape=[TILE_M],
133
+ )
134
+ da_desc = tl.make_tensor_descriptor(
135
+ base=da_base,
136
+ shape=[actual_seqlen_q],
137
+ strides=[stride_dam],
138
+ block_shape=[TILE_M],
139
+ )
140
+
141
+ # Load da accumulators
142
+ acc_da = da_accum_desc.load([m_block * TILE_M])
143
+
144
+ # Scale da
145
+ da = (acc_da * scale).to(dA.dtype.element_ty)
146
+
147
+ # Store da
148
+ da_desc.store([m_block * TILE_M], da)
149
+
150
+
151
+ _bwd_postprocess_kernel = cache_utils.wrap_kernel(_bwd_postprocess_kernel)
152
+
153
+
154
+ def _flash_attn_bwd_postprocess(
155
+ dq_accum: torch.Tensor,
156
+ dq: torch.Tensor,
157
+ scale: float,
158
+ head_dim_rounded: int,
159
+ da_accum: Optional[torch.Tensor] = None,
160
+ da: Optional[torch.Tensor] = None,
161
+ cu_seqlens_q: Optional[torch.Tensor] = None,
162
+ seqused_q: Optional[torch.Tensor] = None,
163
+ max_seqlen_q: Optional[int] = None,
164
+ tile_m: int = 128,
165
+ tile_k: int = 128,
166
+ ) -> torch.Tensor:
167
+ is_varlen = cu_seqlens_q is not None
168
+ if not is_varlen:
169
+ batch_size, seqlen_q, num_heads_q, head_dim = dq.shape
170
+ else:
171
+ _, num_heads_q, head_dim = dq.shape
172
+ batch_size = cu_seqlens_q.shape[0] - 1
173
+ seqlen_q = max_seqlen_q
174
+ has_da = da_accum is not None and da is not None
175
+
176
+ grid = launch_grid.get_bwd_postprocess_grid(batch_size, seqlen_q, num_heads_q)
177
+
178
+ triton.set_allocator(utils.alloc_fn)
179
+
180
+ _bwd_postprocess_kernel[grid](
181
+ dq_accum,
182
+ dq,
183
+ da_accum if has_da else dq_accum,
184
+ da if has_da else dq,
185
+ dq_accum.stride(0) if not is_varlen else 0,
186
+ dq_accum.stride(1) if not is_varlen else dq_accum.stride(0),
187
+ head_dim_rounded,
188
+ dq.stride(0) if not is_varlen else 0,
189
+ dq.stride(-2),
190
+ dq.stride(-3) if not is_varlen else dq.stride(0),
191
+ da_accum.stride(0) if (has_da and not is_varlen) else 0,
192
+ da_accum.stride(1)
193
+ if (has_da and not is_varlen)
194
+ else (da_accum.stride(0) if has_da else 0),
195
+ da_accum.stride(-1) if has_da else 0,
196
+ da.stride(0) if (has_da and not is_varlen) else 0,
197
+ da.stride(-2) if has_da else 0,
198
+ da.stride(-1) if has_da else 0,
199
+ cu_seqlens_q,
200
+ seqused_q,
201
+ seqlen_q,
202
+ head_dim,
203
+ head_dim_rounded,
204
+ scale,
205
+ HAS_CU_SEQLENS_Q=is_varlen,
206
+ HAS_SEQUSED_Q=seqused_q is not None,
207
+ HAS_DA=has_da,
208
+ TILE_M=tile_m,
209
+ TILE_K=tile_k,
210
+ num_warps=4,
211
+ num_stages=1,
212
+ num_ctas=1,
213
+ )
214
+
215
+ return dq
build/torch-cuda/flash_bwd_preprocess.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ import math
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from . import (
9
+ utils,
10
+ cache_utils,
11
+ launch_grid,
12
+ seqlen_info,
13
+ activations,
14
+ kernel_repr,
15
+ )
16
+
17
+
18
+ @triton.jit(repr=kernel_repr.bwd_preprocess_repr)
19
+ def _bwd_preprocess_kernel(
20
+ Out,
21
+ dO,
22
+ dPsum,
23
+ LSE,
24
+ LSELog2,
25
+ dQaccum,
26
+ stride_ob,
27
+ stride_oh,
28
+ stride_om,
29
+ stride_dob,
30
+ stride_doh,
31
+ stride_dom,
32
+ stride_pb,
33
+ stride_ph,
34
+ stride_pm,
35
+ stride_lb,
36
+ stride_lh,
37
+ stride_lm,
38
+ stride_l2b,
39
+ stride_l2h,
40
+ stride_l2m,
41
+ stride_dqab,
42
+ stride_dqah,
43
+ stride_dqam,
44
+ cu_seqlens_q,
45
+ seqused_q,
46
+ seqlen_q,
47
+ head_dim,
48
+ head_dim_rounded,
49
+ HAS_CU_SEQLENS_Q: tl.constexpr,
50
+ HAS_SEQUSED_Q: tl.constexpr,
51
+ TILE_M: tl.constexpr,
52
+ TILE_K: tl.constexpr,
53
+ ):
54
+ m_block = tl.program_id(0)
55
+ head_idx = tl.program_id(1)
56
+ batch_idx = tl.program_id(2)
57
+
58
+ # Get seqlen info for this batch
59
+ offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
60
+ batch_idx=batch_idx,
61
+ seqlen_static=seqlen_q,
62
+ cu_seqlens=cu_seqlens_q,
63
+ seqused=seqused_q,
64
+ HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
65
+ HAS_SEQUSED=HAS_SEQUSED_Q,
66
+ )
67
+ padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
68
+
69
+ # Initialize base pointers
70
+ o_base = seqlen_info.offset_batch_Q(
71
+ Out + head_idx * stride_oh,
72
+ batch_idx,
73
+ offset_q,
74
+ padded_offset_q,
75
+ stride_ob,
76
+ stride_om,
77
+ HAS_CU_SEQLENS_Q,
78
+ USE_PADDED=False,
79
+ )
80
+ do_base = seqlen_info.offset_batch_Q(
81
+ dO + head_idx * stride_doh,
82
+ batch_idx,
83
+ offset_q,
84
+ padded_offset_q,
85
+ stride_dob,
86
+ stride_dom,
87
+ HAS_CU_SEQLENS_Q,
88
+ USE_PADDED=False,
89
+ )
90
+ dpsum_base = seqlen_info.offset_batch_Q(
91
+ dPsum + head_idx * stride_ph,
92
+ batch_idx,
93
+ offset_q,
94
+ padded_offset_q,
95
+ stride_pb,
96
+ stride_pm,
97
+ HAS_CU_SEQLENS_Q,
98
+ USE_PADDED=True,
99
+ )
100
+ lse_base = seqlen_info.offset_batch_Q(
101
+ LSE + head_idx * stride_lh,
102
+ batch_idx,
103
+ offset_q,
104
+ padded_offset_q,
105
+ stride_lb,
106
+ stride_lm,
107
+ HAS_CU_SEQLENS_Q,
108
+ USE_PADDED=False,
109
+ )
110
+ lse_log2_base = seqlen_info.offset_batch_Q(
111
+ LSELog2 + head_idx * stride_l2h,
112
+ batch_idx,
113
+ offset_q,
114
+ padded_offset_q,
115
+ stride_l2b,
116
+ stride_l2m,
117
+ HAS_CU_SEQLENS_Q,
118
+ USE_PADDED=True,
119
+ )
120
+ dq_accum_base = seqlen_info.offset_batch_Q(
121
+ dQaccum + head_idx * stride_dqah,
122
+ batch_idx,
123
+ offset_q,
124
+ padded_offset_q,
125
+ stride_dqab,
126
+ stride_dqam,
127
+ HAS_CU_SEQLENS_Q,
128
+ USE_PADDED=True,
129
+ )
130
+
131
+ # Create descriptors
132
+ o_desc = tl.make_tensor_descriptor(
133
+ base=o_base,
134
+ shape=[actual_seqlen_q, head_dim],
135
+ strides=[stride_om, 1],
136
+ block_shape=[TILE_M, TILE_K],
137
+ )
138
+ do_desc = tl.make_tensor_descriptor(
139
+ base=do_base,
140
+ shape=[actual_seqlen_q, head_dim],
141
+ strides=[stride_dom, 1],
142
+ block_shape=[TILE_M, TILE_K],
143
+ )
144
+ dpsum_desc = tl.make_tensor_descriptor(
145
+ base=dpsum_base,
146
+ shape=[actual_seqlen_q],
147
+ strides=[stride_pm],
148
+ block_shape=[TILE_M],
149
+ )
150
+ lse_desc = tl.make_tensor_descriptor(
151
+ base=lse_base,
152
+ shape=[actual_seqlen_q],
153
+ strides=[stride_lm],
154
+ block_shape=[TILE_M],
155
+ )
156
+ lse_log2_desc = tl.make_tensor_descriptor(
157
+ base=lse_log2_base,
158
+ shape=[actual_seqlen_q],
159
+ strides=[stride_l2m],
160
+ block_shape=[TILE_M],
161
+ )
162
+ dq_accum_desc = tl.make_tensor_descriptor(
163
+ base=dq_accum_base,
164
+ shape=[actual_seqlen_q, head_dim_rounded],
165
+ strides=[stride_dqam, 1],
166
+ block_shape=[TILE_M, TILE_K],
167
+ )
168
+
169
+ # Initialize accumulators
170
+ acc_dq = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
171
+
172
+ # Load o tile
173
+ o_tile = o_desc.load([m_block * TILE_M, 0]).to(tl.float32)
174
+
175
+ # Load do tile
176
+ do_tile = do_desc.load([m_block * TILE_M, 0]).to(tl.float32)
177
+
178
+ # Compute dpsum
179
+ dpsum = tl.sum(o_tile * do_tile, axis=1)
180
+
181
+ # Store dpsum
182
+ dpsum_desc.store([m_block * TILE_M], dpsum)
183
+
184
+ # Load lse tile
185
+ lse_tile = lse_desc.load([m_block * TILE_M])
186
+
187
+ # Compute lse_log2
188
+ lse_log2 = tl.where(lse_tile == lse_tile, lse_tile * math.log2(math.e), 1e6)
189
+ lse_log2 = activations.check_inf(lse_log2)
190
+
191
+ # Store dq_accum
192
+ dq_accum_desc.store([m_block * TILE_M, 0], acc_dq)
193
+
194
+ # Store lse_log2
195
+ lse_log2_desc.store([m_block * TILE_M], lse_log2)
196
+
197
+
198
+ _bwd_preprocess_kernel = cache_utils.wrap_kernel(_bwd_preprocess_kernel)
199
+
200
+
201
+ def _flash_attn_bwd_preprocess(
202
+ out: torch.Tensor,
203
+ dout: torch.Tensor,
204
+ dpsum: torch.Tensor,
205
+ lse: torch.Tensor,
206
+ lse_log2: torch.Tensor,
207
+ dq_accum: torch.Tensor,
208
+ head_dim_rounded: int,
209
+ cu_seqlens_q: Optional[torch.Tensor] = None,
210
+ seqused_q: Optional[torch.Tensor] = None,
211
+ max_seqlen_q: Optional[int] = None,
212
+ tile_m: int = 128,
213
+ tile_k: int = 128,
214
+ ) -> torch.Tensor:
215
+ is_varlen = cu_seqlens_q is not None
216
+ if not is_varlen:
217
+ batch_size, seqlen_q, num_heads_q, head_dim = out.shape
218
+ else:
219
+ _, num_heads_q, head_dim = out.shape
220
+ batch_size = cu_seqlens_q.shape[0] - 1
221
+ seqlen_q = max_seqlen_q
222
+
223
+ grid = launch_grid.get_bwd_preprocess_grid(batch_size, seqlen_q, num_heads_q)
224
+
225
+ triton.set_allocator(utils.alloc_fn)
226
+
227
+ _bwd_preprocess_kernel[grid](
228
+ out,
229
+ dout,
230
+ dpsum,
231
+ lse,
232
+ lse_log2,
233
+ dq_accum,
234
+ out.stride(0) if not is_varlen else 0,
235
+ out.stride(-2),
236
+ out.stride(-3) if not is_varlen else out.stride(0),
237
+ dout.stride(0) if not is_varlen else 0,
238
+ dout.stride(-2),
239
+ dout.stride(-3) if not is_varlen else dout.stride(0),
240
+ dpsum.stride(0) if not is_varlen else 0,
241
+ dpsum.stride(1) if not is_varlen else dpsum.stride(0),
242
+ dpsum.stride(-1),
243
+ lse.stride(0) if not is_varlen else 0,
244
+ lse.stride(1) if not is_varlen else lse.stride(0),
245
+ lse.stride(-1),
246
+ lse_log2.stride(0) if not is_varlen else 0,
247
+ lse_log2.stride(1) if not is_varlen else lse_log2.stride(0),
248
+ lse_log2.stride(-1),
249
+ dq_accum.stride(0) if not is_varlen else 0,
250
+ dq_accum.stride(1) if not is_varlen else dq_accum.stride(0),
251
+ head_dim_rounded,
252
+ cu_seqlens_q,
253
+ seqused_q,
254
+ seqlen_q,
255
+ head_dim,
256
+ head_dim_rounded,
257
+ HAS_CU_SEQLENS_Q=is_varlen,
258
+ HAS_SEQUSED_Q=seqused_q is not None,
259
+ TILE_M=tile_m,
260
+ TILE_K=tile_k,
261
+ num_warps=4,
262
+ num_stages=1,
263
+ num_ctas=1,
264
+ )
265
+
266
+ return dpsum
build/torch-cuda/flash_dec_combine.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+ from . import (
6
+ utils,
7
+ cache_utils,
8
+ launch_template,
9
+ launch_grid,
10
+ seqlen_info,
11
+ kernel_repr,
12
+ )
13
+
14
+
15
+ @triton.jit(repr=kernel_repr.dec_combine_repr)
16
+ def _dec_combine_kernel(
17
+ Out_partial,
18
+ Lse_partial,
19
+ Out,
20
+ Lse,
21
+ stride_ops,
22
+ stride_opb,
23
+ stride_oph,
24
+ stride_opm,
25
+ stride_lps,
26
+ stride_lpb,
27
+ stride_lph,
28
+ stride_lpm,
29
+ stride_ob,
30
+ stride_oh,
31
+ stride_om,
32
+ stride_lb,
33
+ stride_lh,
34
+ stride_lm,
35
+ cu_seqlens_q,
36
+ seqused_q,
37
+ num_splits,
38
+ num_heads_q,
39
+ head_dim,
40
+ TILE_K: tl.constexpr,
41
+ HAS_CU_SEQLENS_Q: tl.constexpr,
42
+ HAS_SEQUSED_Q: tl.constexpr,
43
+ ):
44
+ bh_idx = tl.program_id(0)
45
+ batch_idx = bh_idx // num_heads_q
46
+ head_idx = bh_idx - batch_idx * num_heads_q
47
+
48
+ # Get seqlen info for this batch
49
+ offset_q, _ = seqlen_info.get_seqlen_info(
50
+ batch_idx=batch_idx,
51
+ seqlen_static=1,
52
+ cu_seqlens=cu_seqlens_q,
53
+ seqused=seqused_q,
54
+ HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
55
+ HAS_SEQUSED=HAS_SEQUSED_Q,
56
+ )
57
+
58
+ # Initialize base pointers
59
+ out_part_base = seqlen_info.offset_batch_Q(
60
+ Out_partial + head_idx * stride_oph,
61
+ batch_idx,
62
+ offset_q,
63
+ 0,
64
+ stride_opb,
65
+ stride_opm,
66
+ HAS_CU_SEQLENS_Q,
67
+ USE_PADDED=False,
68
+ )
69
+ lse_part_base = seqlen_info.offset_batch_Q(
70
+ Lse_partial + head_idx * stride_lph,
71
+ batch_idx,
72
+ offset_q,
73
+ 0,
74
+ stride_lpb,
75
+ stride_lpm,
76
+ HAS_CU_SEQLENS_Q,
77
+ USE_PADDED=False,
78
+ )
79
+ out_base = seqlen_info.offset_batch_Q(
80
+ Out + head_idx * stride_oh,
81
+ batch_idx,
82
+ offset_q,
83
+ 0,
84
+ stride_ob,
85
+ stride_om,
86
+ HAS_CU_SEQLENS_Q,
87
+ USE_PADDED=False,
88
+ )
89
+ lse_base = seqlen_info.offset_batch_Q(
90
+ Lse + head_idx * stride_lh,
91
+ batch_idx,
92
+ offset_q,
93
+ 0,
94
+ stride_lb,
95
+ stride_lm,
96
+ HAS_CU_SEQLENS_Q,
97
+ USE_PADDED=False,
98
+ )
99
+
100
+ # Create descriptors
101
+ out_part_desc = tl.make_tensor_descriptor(
102
+ base=out_part_base,
103
+ shape=[num_splits, head_dim],
104
+ strides=[stride_ops, 1],
105
+ block_shape=[1, TILE_K],
106
+ )
107
+ out_desc = tl.make_tensor_descriptor(
108
+ base=out_base,
109
+ shape=[head_dim],
110
+ strides=[1],
111
+ block_shape=[TILE_K],
112
+ )
113
+
114
+ # Initialize accumulators
115
+ e_sum = tl.full((), 0.0, dtype=tl.float32)
116
+ e_max = tl.full((), float("-inf"), dtype=tl.float32)
117
+ acc_o = tl.zeros((TILE_K,), dtype=tl.float32)
118
+
119
+ # Combine split outputs
120
+ for s in tl.range(0, num_splits):
121
+ # Load partial LSE
122
+ lse_s = tl.load(lse_part_base + s * stride_lps).to(tl.float32)
123
+
124
+ # Compute normalized exponentials
125
+ new_e_max = tl.maximum(lse_s, e_max)
126
+ old_scale = tl.where(e_max == float("-inf"), 0.0, tl.exp2(e_max - new_e_max))
127
+ exp_logic = tl.where(lse_s == float("-inf"), 0.0, tl.exp2(lse_s - new_e_max))
128
+
129
+ # Load partial outputs
130
+ o_s = tl.sum(out_part_desc.load([s, 0]), axis=0)
131
+
132
+ # Compute scaled outputs
133
+ acc_o *= old_scale
134
+ acc_o += exp_logic * o_s
135
+
136
+ # Update e_sum and e_max
137
+ e_sum = e_sum * old_scale + exp_logic
138
+ e_max = new_e_max
139
+
140
+ # Normalize output
141
+ inv_sum = tl.where((e_sum == 0.0) | (e_sum != e_sum), 0.0, 1.0 / e_sum)
142
+ acc_o *= inv_sum
143
+
144
+ # Store output
145
+ out_desc.store([0], acc_o.to(Out.dtype.element_ty))
146
+
147
+ # Compute LSE
148
+ # ln2 = math.log(2.0)
149
+ ln2 = 0.6931471805599453
150
+ lse = tl.where(e_sum > 0.0, (e_max + tl.log2(e_sum)) * ln2, float("-inf"))
151
+
152
+ # Store LSE
153
+ tl.store(lse_base, lse)
154
+
155
+
156
+ _dec_combine_kernel = cache_utils.wrap_kernel(_dec_combine_kernel)
157
+
158
+
159
+ def _flash_attn_dec_combine(
160
+ out_partial: torch.Tensor,
161
+ lse_partial: torch.Tensor,
162
+ out: torch.Tensor,
163
+ lse: torch.Tensor,
164
+ cu_seqlens_q: torch.Tensor = None,
165
+ seqused_q: torch.Tensor = None,
166
+ ):
167
+ device = out.device
168
+ arch = cache_utils.get_device_arch(device)
169
+ is_varlen = cu_seqlens_q is not None
170
+ num_splits = out_partial.shape[0]
171
+ if not is_varlen:
172
+ batch_size, num_heads_q, head_dim = out_partial.shape[1:]
173
+ else:
174
+ _, num_heads_q, head_dim = out_partial.shape[1:]
175
+ batch_size = cu_seqlens_q.shape[0] - 1
176
+
177
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
178
+
179
+ num_warps, num_stages, num_ctas = launch_template.get_dec_combine_launch_config(
180
+ tile_k=TILE_K,
181
+ device=device,
182
+ arch=arch,
183
+ )
184
+
185
+ grid = launch_grid.get_dec_combine_grid(
186
+ batch_size=batch_size,
187
+ num_heads_q=num_heads_q,
188
+ )
189
+
190
+ triton.set_allocator(utils.alloc_fn)
191
+
192
+ _dec_combine_kernel[grid](
193
+ out_partial,
194
+ lse_partial,
195
+ out,
196
+ lse,
197
+ out_partial.stride(0),
198
+ out_partial.stride(1) if not is_varlen else 0,
199
+ out_partial.stride(-2),
200
+ 1 if not is_varlen else out_partial.stride(1),
201
+ lse_partial.stride(0),
202
+ lse_partial.stride(1) if not is_varlen else 0,
203
+ lse_partial.stride(-1),
204
+ 1 if not is_varlen else lse_partial.stride(1),
205
+ out.stride(0) if not is_varlen else 0,
206
+ out.stride(-2),
207
+ 1 if not is_varlen else out.stride(0),
208
+ lse.stride(0) if not is_varlen else 0,
209
+ lse.stride(-1),
210
+ 1 if not is_varlen else lse.stride(0),
211
+ cu_seqlens_q,
212
+ seqused_q,
213
+ num_splits,
214
+ num_heads_q,
215
+ head_dim,
216
+ TILE_K=TILE_K,
217
+ HAS_CU_SEQLENS_Q=is_varlen,
218
+ HAS_SEQUSED_Q=seqused_q is not None,
219
+ num_warps=num_warps,
220
+ num_stages=num_stages,
221
+ num_ctas=num_ctas,
222
+ )
build/torch-cuda/flash_dense_bwd.py ADDED
@@ -0,0 +1,1019 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple
2
+
3
+ import math
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from . import (
9
+ assert_inputs,
10
+ utils,
11
+ cache_utils,
12
+ launch_template,
13
+ launch_grid,
14
+ activations,
15
+ flash_bwd_preprocess,
16
+ flash_bwd_postprocess,
17
+ kernel_repr,
18
+ autotuner,
19
+ )
20
+ from .scheduler import (
21
+ AttnBwdGridIndex,
22
+ AttnBwdConfig,
23
+ AttnBwdBlockScheduler,
24
+ AttnBwdPointerScheduler,
25
+ AttnMaskScheduler,
26
+ )
27
+
28
+
29
+ @triton.jit
30
+ def _bwd_inner_dense_kernel(
31
+ config,
32
+ ptrs_sched,
33
+ mask_sched,
34
+ acc_dk,
35
+ acc_dv,
36
+ k_tile,
37
+ v_tile,
38
+ q_ptrs,
39
+ do_ptrs,
40
+ dq_accum_ptrs,
41
+ lse_ptrs,
42
+ dpsum_ptrs,
43
+ m_block,
44
+ IS_MASK: tl.constexpr,
45
+ MASK_CAUSAL: tl.constexpr,
46
+ MASK_LOCAL: tl.constexpr,
47
+ MASK_SINK: tl.constexpr,
48
+ ):
49
+ # Load query tile
50
+ q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
51
+
52
+ # Compute attention scores
53
+ acc_s = tl.dot(k_tile, q_tile.T)
54
+
55
+ # Load LSE
56
+ lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
57
+
58
+ if IS_MASK:
59
+ # Apply mask to attention scores
60
+ acc_s = mask_sched.apply_mask(
61
+ acc_s=acc_s,
62
+ iter_block=m_block,
63
+ MASK_CAUSAL=MASK_CAUSAL,
64
+ MASK_LOCAL=MASK_LOCAL,
65
+ MASK_SINK=MASK_SINK,
66
+ )
67
+
68
+ # Compute attention weights
69
+ p = activations.exp2(acc_s * config.softmax_scale_log2 - lse_log2[None, :]).to(
70
+ q_tile.dtype
71
+ )
72
+
73
+ # Load output gradients tile
74
+ do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
75
+
76
+ # Compute value gradients
77
+ acc_dv += tl.dot(p, do_tile)
78
+
79
+ # Compute attention weight gradients
80
+ acc_dp = tl.dot(v_tile, tl.trans(do_tile))
81
+
82
+ # Load dpsum
83
+ dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
84
+
85
+ # Compute attention score gradients
86
+ ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
87
+
88
+ # Compute query gradients
89
+ dq = tl.dot(tl.trans(ds), k_tile)
90
+
91
+ # Store query gradients
92
+ ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
93
+
94
+ # Compute key gradients
95
+ acc_dk += tl.dot(ds, q_tile)
96
+
97
+ return acc_dk, acc_dv
98
+
99
+
100
+ @triton.jit(repr=kernel_repr.bwd_dense_repr)
101
+ def _bwd_dense_kernel(
102
+ Q,
103
+ K,
104
+ V,
105
+ dO,
106
+ LSELog2,
107
+ dPsum,
108
+ dQaccum,
109
+ dK,
110
+ dV,
111
+ softmax_scale,
112
+ query_scale,
113
+ key_scale,
114
+ value_scale,
115
+ window_sizes,
116
+ stride_qb,
117
+ stride_qh,
118
+ stride_qm,
119
+ stride_kb,
120
+ stride_kh,
121
+ stride_kn,
122
+ stride_vb,
123
+ stride_vh,
124
+ stride_vn,
125
+ stride_dob,
126
+ stride_doh,
127
+ stride_dom,
128
+ stride_lb,
129
+ stride_lh,
130
+ stride_pb,
131
+ stride_ph,
132
+ stride_dqab,
133
+ stride_dqah,
134
+ stride_dqam,
135
+ stride_dkb,
136
+ stride_dkh,
137
+ stride_dkn,
138
+ stride_dks,
139
+ stride_dvb,
140
+ stride_dvh,
141
+ stride_dvn,
142
+ stride_dvs,
143
+ stride_wh,
144
+ cu_seqlens_q,
145
+ cu_seqlens_k,
146
+ seqused_q,
147
+ seqused_k,
148
+ num_splits,
149
+ seqlen_q,
150
+ seqlen_k,
151
+ head_dim,
152
+ SEQLEN_Q_CACHE: tl.constexpr,
153
+ SEQLEN_K_CACHE: tl.constexpr,
154
+ QHEAD_PER_KVHEAD: tl.constexpr,
155
+ TILE_M: tl.constexpr,
156
+ TILE_N: tl.constexpr,
157
+ TILE_K: tl.constexpr,
158
+ IS_CAUSAL: tl.constexpr,
159
+ IS_LOCAL: tl.constexpr,
160
+ IS_SPLIT_QO: tl.constexpr,
161
+ HAS_CU_SEQLENS_Q: tl.constexpr,
162
+ HAS_CU_SEQLENS_K: tl.constexpr,
163
+ HAS_SEQUSED_Q: tl.constexpr,
164
+ HAS_SEQUSED_K: tl.constexpr,
165
+ ):
166
+ # Create grid index
167
+ grid_idx = AttnBwdGridIndex.create(
168
+ num_splits=num_splits,
169
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
170
+ IS_SPLIT_QO=IS_SPLIT_QO,
171
+ )
172
+
173
+ # Load window sizes
174
+ (
175
+ window_size_sink,
176
+ window_size_left,
177
+ window_size_right,
178
+ window_size_dist,
179
+ ) = grid_idx.load_window_sizes(
180
+ window_sizes=window_sizes,
181
+ stride_wh=stride_wh,
182
+ IS_LOCAL=IS_LOCAL,
183
+ )
184
+
185
+ # Create config
186
+ config = AttnBwdConfig.create(
187
+ softmax_scale=softmax_scale,
188
+ query_scale=query_scale,
189
+ key_scale=key_scale,
190
+ value_scale=value_scale,
191
+ n_block=grid_idx.n_block,
192
+ batch_idx=grid_idx.batch_idx,
193
+ window_size_sink=window_size_sink,
194
+ window_size_left=window_size_left,
195
+ window_size_right=window_size_right,
196
+ window_size_dist=window_size_dist,
197
+ head_dim=head_dim,
198
+ cu_seqlens_q=cu_seqlens_q,
199
+ cu_seqlens_k=cu_seqlens_k,
200
+ seqused_q=seqused_q,
201
+ seqused_k=seqused_k,
202
+ seqlen_q=seqlen_q,
203
+ seqlen_k=seqlen_k,
204
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
205
+ TILE_M=TILE_M,
206
+ TILE_N=TILE_N,
207
+ TILE_K=TILE_K,
208
+ IS_CAUSAL=IS_CAUSAL,
209
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
210
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
211
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
212
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
213
+ )
214
+
215
+ # Create pointer scheduler
216
+ ptrs_sched = AttnBwdPointerScheduler.create(
217
+ config=config,
218
+ Q=Q,
219
+ K=K,
220
+ V=V,
221
+ dO=dO,
222
+ LSELog2=LSELog2,
223
+ dPsum=dPsum,
224
+ dQaccum=dQaccum,
225
+ dK=dK,
226
+ dV=dV,
227
+ batch_idx=grid_idx.batch_idx,
228
+ head_idx=grid_idx.head_idx,
229
+ head_kv_idx=grid_idx.head_kv_idx,
230
+ split_idx=grid_idx.split_idx,
231
+ stride_qb=stride_qb,
232
+ stride_qh=stride_qh,
233
+ stride_qm=stride_qm,
234
+ stride_kb=stride_kb,
235
+ stride_kh=stride_kh,
236
+ stride_kn=stride_kn,
237
+ stride_vb=stride_vb,
238
+ stride_vh=stride_vh,
239
+ stride_vn=stride_vn,
240
+ stride_dob=stride_dob,
241
+ stride_doh=stride_doh,
242
+ stride_dom=stride_dom,
243
+ stride_lb=stride_lb,
244
+ stride_lh=stride_lh,
245
+ stride_pb=stride_pb,
246
+ stride_ph=stride_ph,
247
+ stride_dqab=stride_dqab,
248
+ stride_dqah=stride_dqah,
249
+ stride_dqam=stride_dqam,
250
+ stride_dkb=stride_dkb,
251
+ stride_dkh=stride_dkh,
252
+ stride_dkn=stride_dkn,
253
+ stride_dks=stride_dks,
254
+ stride_dvb=stride_dvb,
255
+ stride_dvh=stride_dvh,
256
+ stride_dvn=stride_dvn,
257
+ stride_dvs=stride_dvs,
258
+ IS_SPLIT_QO=IS_SPLIT_QO,
259
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
260
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
261
+ )
262
+
263
+ # Create block scheduler
264
+ block_sched = AttnBwdBlockScheduler.create(
265
+ config=config,
266
+ split_idx=grid_idx.split_idx,
267
+ num_splits=num_splits,
268
+ IS_CAUSAL=IS_CAUSAL,
269
+ IS_LOCAL=IS_LOCAL,
270
+ IS_SPLIT_QO=IS_SPLIT_QO,
271
+ )
272
+
273
+ # Create mask scheduler
274
+ mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
275
+
276
+ # Early exit if no n_blocks to process
277
+ if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
278
+ return
279
+
280
+ # Create pointers
281
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
282
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
283
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
284
+ do_ptrs = ptrs_sched.make_do_ptrs(config)
285
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
286
+ dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
287
+ dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
288
+ dk_ptrs = ptrs_sched.make_dk_ptrs(config)
289
+ dv_ptrs = ptrs_sched.make_dv_ptrs(config)
290
+
291
+ # Initialize accumulators
292
+ acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
293
+ acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
294
+
295
+ # Load key tile
296
+ k_tile = ptrs_sched.load_k(config, k_ptrs)
297
+
298
+ # Load value tile
299
+ v_tile = ptrs_sched.load_v(config, v_ptrs)
300
+
301
+ # Process m_blocks with causal masking
302
+ if IS_CAUSAL or IS_LOCAL:
303
+ for m_block in tl.range(
304
+ block_sched.m_block_min, block_sched.m_block_min_no_mask
305
+ ):
306
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
307
+ config=config,
308
+ ptrs_sched=ptrs_sched,
309
+ mask_sched=mask_sched,
310
+ acc_dk=acc_dk,
311
+ acc_dv=acc_dv,
312
+ k_tile=k_tile,
313
+ v_tile=v_tile,
314
+ q_ptrs=q_ptrs,
315
+ do_ptrs=do_ptrs,
316
+ dq_accum_ptrs=dq_accum_ptrs,
317
+ lse_ptrs=lse_ptrs,
318
+ dpsum_ptrs=dpsum_ptrs,
319
+ m_block=m_block,
320
+ IS_MASK=True,
321
+ MASK_CAUSAL=IS_CAUSAL,
322
+ MASK_LOCAL=True if IS_LOCAL else False,
323
+ MASK_SINK=False,
324
+ )
325
+
326
+ # Process m_blocks without masking
327
+ if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
328
+ for m_block in tl.range(
329
+ block_sched.m_block_min_no_mask, block_sched.m_block_max
330
+ ):
331
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
332
+ config=config,
333
+ ptrs_sched=ptrs_sched,
334
+ mask_sched=mask_sched,
335
+ acc_dk=acc_dk,
336
+ acc_dv=acc_dv,
337
+ k_tile=k_tile,
338
+ v_tile=v_tile,
339
+ q_ptrs=q_ptrs,
340
+ do_ptrs=do_ptrs,
341
+ dq_accum_ptrs=dq_accum_ptrs,
342
+ lse_ptrs=lse_ptrs,
343
+ dpsum_ptrs=dpsum_ptrs,
344
+ m_block=m_block,
345
+ IS_MASK=False,
346
+ MASK_CAUSAL=False,
347
+ MASK_LOCAL=False,
348
+ MASK_SINK=False,
349
+ )
350
+
351
+ if IS_LOCAL:
352
+ # Process m_blocks with local right masking
353
+ if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
354
+ for m_block in tl.range(
355
+ block_sched.m_block_window_min,
356
+ block_sched.m_block_window_min_no_mask,
357
+ ):
358
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
359
+ config=config,
360
+ ptrs_sched=ptrs_sched,
361
+ mask_sched=mask_sched,
362
+ acc_dk=acc_dk,
363
+ acc_dv=acc_dv,
364
+ k_tile=k_tile,
365
+ v_tile=v_tile,
366
+ q_ptrs=q_ptrs,
367
+ do_ptrs=do_ptrs,
368
+ dq_accum_ptrs=dq_accum_ptrs,
369
+ lse_ptrs=lse_ptrs,
370
+ dpsum_ptrs=dpsum_ptrs,
371
+ m_block=m_block,
372
+ IS_MASK=True,
373
+ MASK_CAUSAL=False,
374
+ MASK_LOCAL=True,
375
+ MASK_SINK=False,
376
+ )
377
+
378
+ # Process m_blocks without masking
379
+ if (
380
+ block_sched.m_block_window_min_no_mask
381
+ < block_sched.m_block_window_max_no_mask
382
+ ):
383
+ for m_block in tl.range(
384
+ block_sched.m_block_window_min_no_mask,
385
+ block_sched.m_block_window_max_no_mask,
386
+ ):
387
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
388
+ config=config,
389
+ ptrs_sched=ptrs_sched,
390
+ mask_sched=mask_sched,
391
+ acc_dk=acc_dk,
392
+ acc_dv=acc_dv,
393
+ k_tile=k_tile,
394
+ v_tile=v_tile,
395
+ q_ptrs=q_ptrs,
396
+ do_ptrs=do_ptrs,
397
+ dq_accum_ptrs=dq_accum_ptrs,
398
+ lse_ptrs=lse_ptrs,
399
+ dpsum_ptrs=dpsum_ptrs,
400
+ m_block=m_block,
401
+ IS_MASK=False,
402
+ MASK_CAUSAL=False,
403
+ MASK_LOCAL=False,
404
+ MASK_SINK=False,
405
+ )
406
+
407
+ # Process m_blocks with local left masking
408
+ if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
409
+ for m_block in tl.range(
410
+ block_sched.m_block_window_max_no_mask,
411
+ block_sched.m_block_window_max,
412
+ ):
413
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
414
+ config=config,
415
+ ptrs_sched=ptrs_sched,
416
+ mask_sched=mask_sched,
417
+ acc_dk=acc_dk,
418
+ acc_dv=acc_dv,
419
+ k_tile=k_tile,
420
+ v_tile=v_tile,
421
+ q_ptrs=q_ptrs,
422
+ do_ptrs=do_ptrs,
423
+ dq_accum_ptrs=dq_accum_ptrs,
424
+ lse_ptrs=lse_ptrs,
425
+ dpsum_ptrs=dpsum_ptrs,
426
+ m_block=m_block,
427
+ IS_MASK=True,
428
+ MASK_CAUSAL=False,
429
+ MASK_LOCAL=True,
430
+ MASK_SINK=False,
431
+ )
432
+
433
+ # Process m_blocks with local sink masking
434
+ if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
435
+ for m_block in tl.range(
436
+ block_sched.m_block_sink_min,
437
+ block_sched.m_block_sink_max,
438
+ ):
439
+ acc_dk, acc_dv = _bwd_inner_dense_kernel(
440
+ config=config,
441
+ ptrs_sched=ptrs_sched,
442
+ mask_sched=mask_sched,
443
+ acc_dk=acc_dk,
444
+ acc_dv=acc_dv,
445
+ k_tile=k_tile,
446
+ v_tile=v_tile,
447
+ q_ptrs=q_ptrs,
448
+ do_ptrs=do_ptrs,
449
+ dq_accum_ptrs=dq_accum_ptrs,
450
+ lse_ptrs=lse_ptrs,
451
+ dpsum_ptrs=dpsum_ptrs,
452
+ m_block=m_block,
453
+ IS_MASK=True,
454
+ MASK_CAUSAL=False,
455
+ MASK_LOCAL=True,
456
+ MASK_SINK=True,
457
+ )
458
+
459
+ # Store value gradients
460
+ ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
461
+
462
+ # Scale key gradients
463
+ acc_dk = acc_dk * softmax_scale
464
+
465
+ # Store key gradients
466
+ ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
467
+
468
+
469
+ _bwd_dense_kernel = cache_utils.wrap_kernel(_bwd_dense_kernel)
470
+
471
+
472
+ _bwd_dense_kernel_autotuned = None
473
+
474
+
475
+ def _get_autotuned_kernel():
476
+ global _bwd_dense_kernel_autotuned
477
+ if _bwd_dense_kernel_autotuned is None:
478
+ jit_kernel = _bwd_dense_kernel._kernel
479
+ autotuned = autotuner.make_bwd_dense_autotuned_kernel(jit_kernel)
480
+ _bwd_dense_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
481
+ return _bwd_dense_kernel_autotuned
482
+
483
+
484
+ def _flash_dense_attn_backward(
485
+ query: torch.Tensor,
486
+ key: torch.Tensor,
487
+ value: torch.Tensor,
488
+ out: torch.Tensor,
489
+ dout: torch.Tensor,
490
+ lse: torch.Tensor,
491
+ is_causal: bool = False,
492
+ softmax_scale: float = None,
493
+ query_scale: Optional[torch.Tensor] = None,
494
+ key_scale: Optional[torch.Tensor] = None,
495
+ value_scale: Optional[torch.Tensor] = None,
496
+ window_sizes: Optional[torch.Tensor] = None,
497
+ is_local: bool = False,
498
+ is_quant: bool = False,
499
+ is_split_qo: bool = False,
500
+ is_autotune: bool = False,
501
+ skip_checks: bool = False,
502
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
503
+ device = query.device
504
+ num_SMs = cache_utils.get_device_num_sms(device)
505
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
506
+ _, seqlen_k, num_heads_kv, _ = key.shape
507
+ softmax_scale = (
508
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
509
+ )
510
+ qhead_per_kvhead = num_heads_q // num_heads_kv
511
+ if is_local and window_sizes is None:
512
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
513
+ elif not is_local:
514
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
515
+
516
+ if not skip_checks:
517
+ assert_inputs.assert_bwd_inputs(
518
+ query,
519
+ key,
520
+ value,
521
+ out,
522
+ dout,
523
+ lse,
524
+ query_scale=query_scale,
525
+ key_scale=key_scale,
526
+ value_scale=value_scale,
527
+ window_sizes=window_sizes,
528
+ cu_seqlens_q=None,
529
+ cu_seqlens_k=None,
530
+ seqused_q=None,
531
+ seqused_k=None,
532
+ num_heads_q=num_heads_q,
533
+ num_heads_kv=num_heads_kv,
534
+ head_dim=head_dim,
535
+ is_quant=is_quant,
536
+ device=device,
537
+ )
538
+
539
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
540
+
541
+ launch_config = launch_template.load_launch_config(
542
+ device=device,
543
+ kernel_name="bwd_dense",
544
+ seqlen_q=seqlen_q,
545
+ seqlen_k=seqlen_k,
546
+ tile_k=TILE_K,
547
+ is_local=is_local,
548
+ qhead_per_kvhead=qhead_per_kvhead,
549
+ is_causal=is_causal,
550
+ is_quant=is_quant,
551
+ )
552
+ if launch_config is not None and not is_autotune:
553
+ kernel = _bwd_dense_kernel
554
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
555
+ else:
556
+ kernel = _get_autotuned_kernel()
557
+ # Placeholder for pre-launch computations
558
+ TILE_M = TILE_N = 64
559
+ num_warps = num_stages = num_ctas = None
560
+
561
+ num_splits = (
562
+ utils.num_splits_heuristic(
563
+ seqlen_q=seqlen_k,
564
+ seqlen_k=seqlen_q,
565
+ num_SMs=num_SMs,
566
+ TILE_M=TILE_N,
567
+ TILE_N=TILE_M,
568
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
569
+ window_sizes, TILE_M
570
+ )
571
+ if is_local
572
+ else None,
573
+ )
574
+ if is_split_qo
575
+ else 1
576
+ )
577
+
578
+ seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
579
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
580
+
581
+ if not is_quant:
582
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
583
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
584
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
585
+
586
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
587
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
588
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
589
+ lse_log2 = torch.empty(
590
+ (batch_size, num_heads_q, seqlen_q_rounded),
591
+ dtype=torch.float32,
592
+ device=query.device,
593
+ )
594
+ dpsum = torch.empty(
595
+ (batch_size, num_heads_q, seqlen_q_rounded),
596
+ dtype=torch.float32,
597
+ device=query.device,
598
+ )
599
+ dq_accum = torch.empty(
600
+ (batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
601
+ dtype=torch.float32,
602
+ device=query.device,
603
+ )
604
+ dk_accum = torch.zeros(
605
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
606
+ if is_split_qo and num_splits > 1
607
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
608
+ dtype=torch.float32,
609
+ device=query.device,
610
+ )
611
+ dv_accum = torch.zeros(
612
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
613
+ if is_split_qo and num_splits > 1
614
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
615
+ dtype=torch.float32,
616
+ device=query.device,
617
+ )
618
+
619
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
620
+ out=out,
621
+ dout=dout,
622
+ dpsum=dpsum,
623
+ lse=lse,
624
+ lse_log2=lse_log2,
625
+ dq_accum=dq_accum,
626
+ head_dim_rounded=head_dim_rounded,
627
+ tile_m=TILE_M,
628
+ tile_k=TILE_K,
629
+ )
630
+
631
+ grid = launch_grid.get_bwd_grid(
632
+ seqlen_k=seqlen_k,
633
+ num_heads_q=num_heads_q,
634
+ batch_size=batch_size,
635
+ num_splits=num_splits,
636
+ )
637
+
638
+ triton.set_allocator(utils.alloc_fn)
639
+
640
+ kernel[grid](
641
+ query,
642
+ key,
643
+ value,
644
+ dout,
645
+ lse_log2,
646
+ dpsum,
647
+ dq_accum,
648
+ dk_accum,
649
+ dv_accum,
650
+ softmax_scale,
651
+ query_scale,
652
+ key_scale,
653
+ value_scale,
654
+ window_sizes,
655
+ query.stride(0),
656
+ query.stride(-2),
657
+ query.stride(-3),
658
+ key.stride(0),
659
+ key.stride(-2),
660
+ key.stride(-3),
661
+ value.stride(0),
662
+ value.stride(-2),
663
+ value.stride(-3),
664
+ dout.stride(0),
665
+ dout.stride(-2),
666
+ dout.stride(-3),
667
+ lse_log2.stride(0),
668
+ lse_log2.stride(-2),
669
+ dpsum.stride(0),
670
+ dpsum.stride(-2),
671
+ dq_accum.stride(0),
672
+ dq_accum.stride(1),
673
+ head_dim_rounded,
674
+ dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
675
+ dk_accum.stride(-2),
676
+ dk_accum.stride(-3),
677
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
678
+ dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
679
+ dv_accum.stride(-2),
680
+ dv_accum.stride(-3),
681
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
682
+ window_sizes.stride(0),
683
+ None,
684
+ None,
685
+ None,
686
+ None,
687
+ num_splits,
688
+ seqlen_q=seqlen_q,
689
+ seqlen_k=seqlen_k,
690
+ head_dim=head_dim,
691
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
692
+ SEQLEN_K_CACHE=seqlen_k // 1024,
693
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
694
+ TILE_M=TILE_M,
695
+ TILE_N=TILE_N,
696
+ TILE_K=TILE_K,
697
+ IS_CAUSAL=is_causal,
698
+ IS_LOCAL=is_local,
699
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
700
+ HAS_CU_SEQLENS_Q=False,
701
+ HAS_CU_SEQLENS_K=False,
702
+ HAS_SEQUSED_Q=False,
703
+ HAS_SEQUSED_K=False,
704
+ num_warps=num_warps,
705
+ num_stages=num_stages,
706
+ num_ctas=num_ctas,
707
+ )
708
+
709
+ if launch_config is None or is_autotune:
710
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
711
+ if best is not None:
712
+ launch_template.store_launch_config(
713
+ device=device,
714
+ kernel_name="bwd_dense",
715
+ seqlen_q=seqlen_q,
716
+ seqlen_k=seqlen_k,
717
+ tile_k=TILE_K,
718
+ config=best,
719
+ is_local=is_local,
720
+ qhead_per_kvhead=qhead_per_kvhead,
721
+ is_causal=is_causal,
722
+ is_quant=is_quant,
723
+ )
724
+
725
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
726
+ dq_accum=dq_accum,
727
+ dq=dq,
728
+ scale=softmax_scale,
729
+ head_dim_rounded=head_dim_rounded,
730
+ tile_m=TILE_M,
731
+ tile_k=TILE_K,
732
+ )
733
+
734
+ if is_split_qo and num_splits > 1:
735
+ dk_accum = dk_accum.sum(dim=0)
736
+ dv_accum = dv_accum.sum(dim=0)
737
+
738
+ dk.copy_(dk_accum)
739
+ dv.copy_(dv_accum)
740
+
741
+ return dq, dk, dv
742
+
743
+
744
+ def _flash_dense_attn_varlen_backward(
745
+ query: torch.Tensor,
746
+ key: torch.Tensor,
747
+ value: torch.Tensor,
748
+ out: torch.Tensor,
749
+ dout: torch.Tensor,
750
+ lse: torch.Tensor,
751
+ cu_seqlens_q: torch.Tensor,
752
+ cu_seqlens_k: torch.Tensor,
753
+ max_seqlen_q: Optional[int] = None,
754
+ max_seqlen_k: Optional[int] = None,
755
+ is_causal: bool = False,
756
+ softmax_scale: float = None,
757
+ query_scale: Optional[torch.Tensor] = None,
758
+ key_scale: Optional[torch.Tensor] = None,
759
+ value_scale: Optional[torch.Tensor] = None,
760
+ window_sizes: Optional[torch.Tensor] = None,
761
+ is_local: bool = False,
762
+ is_quant: bool = False,
763
+ is_split_qo: bool = False,
764
+ seqused_q: Optional[torch.Tensor] = None,
765
+ seqused_k: Optional[torch.Tensor] = None,
766
+ is_autotune: bool = False,
767
+ skip_checks: bool = False,
768
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
769
+ device = query.device
770
+ num_SMs = cache_utils.get_device_num_sms(device)
771
+ total_q, num_heads_q, head_dim = query.shape
772
+ total_k, num_heads_kv, _ = key.shape
773
+ batch_size = cu_seqlens_q.shape[0] - 1
774
+ seqlen_q = max_seqlen_q
775
+ seqlen_k = max_seqlen_k
776
+ softmax_scale = (
777
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
778
+ )
779
+ qhead_per_kvhead = num_heads_q // num_heads_kv
780
+ if is_local and window_sizes is None:
781
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
782
+ elif not is_local:
783
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
784
+
785
+ if not skip_checks:
786
+ assert_inputs.assert_bwd_inputs(
787
+ query,
788
+ key,
789
+ value,
790
+ out,
791
+ dout,
792
+ lse,
793
+ query_scale=query_scale,
794
+ key_scale=key_scale,
795
+ value_scale=value_scale,
796
+ window_sizes=window_sizes,
797
+ cu_seqlens_q=cu_seqlens_q,
798
+ cu_seqlens_k=cu_seqlens_k,
799
+ seqused_q=seqused_q,
800
+ seqused_k=seqused_k,
801
+ num_heads_q=num_heads_q,
802
+ num_heads_kv=num_heads_kv,
803
+ head_dim=head_dim,
804
+ is_quant=is_quant,
805
+ device=device,
806
+ )
807
+
808
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
809
+
810
+ launch_config = launch_template.load_launch_config(
811
+ device=device,
812
+ kernel_name="bwd_dense",
813
+ seqlen_q=seqlen_q,
814
+ seqlen_k=seqlen_k,
815
+ tile_k=TILE_K,
816
+ is_local=is_local,
817
+ qhead_per_kvhead=qhead_per_kvhead,
818
+ is_causal=is_causal,
819
+ is_quant=is_quant,
820
+ )
821
+ if launch_config is not None and not is_autotune:
822
+ kernel = _bwd_dense_kernel
823
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
824
+ else:
825
+ kernel = _get_autotuned_kernel()
826
+ # Placeholder for pre-launch computations
827
+ TILE_M = TILE_N = 64
828
+ num_warps = num_stages = num_ctas = None
829
+
830
+ num_splits = (
831
+ utils.num_splits_heuristic(
832
+ seqlen_q=seqlen_k,
833
+ seqlen_k=seqlen_q,
834
+ num_SMs=num_SMs,
835
+ TILE_M=TILE_N,
836
+ TILE_N=TILE_M,
837
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
838
+ window_sizes, TILE_M
839
+ )
840
+ if is_local
841
+ else None,
842
+ )
843
+ if is_split_qo
844
+ else 1
845
+ )
846
+
847
+ total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
848
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
849
+
850
+ if not is_quant:
851
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
852
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
853
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
854
+
855
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
856
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
857
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
858
+ lse_log2 = torch.empty(
859
+ num_heads_q,
860
+ total_q_rounded_padded,
861
+ dtype=torch.float32,
862
+ device=query.device,
863
+ )
864
+ dpsum = torch.empty(
865
+ num_heads_q,
866
+ total_q_rounded_padded,
867
+ dtype=torch.float32,
868
+ device=query.device,
869
+ )
870
+ dq_accum = torch.empty(
871
+ num_heads_q,
872
+ total_q_rounded_padded * head_dim_rounded,
873
+ dtype=torch.float32,
874
+ device=query.device,
875
+ )
876
+ dk_accum = torch.zeros(
877
+ (num_splits, total_k, num_heads_kv, head_dim)
878
+ if is_split_qo and num_splits > 1
879
+ else (total_k, num_heads_kv, head_dim),
880
+ dtype=torch.float32,
881
+ device=query.device,
882
+ )
883
+ dv_accum = torch.zeros(
884
+ (num_splits, total_k, num_heads_kv, head_dim)
885
+ if is_split_qo and num_splits > 1
886
+ else (total_k, num_heads_kv, head_dim),
887
+ dtype=torch.float32,
888
+ device=query.device,
889
+ )
890
+
891
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
892
+ out=out,
893
+ dout=dout,
894
+ dpsum=dpsum,
895
+ lse=lse,
896
+ lse_log2=lse_log2,
897
+ dq_accum=dq_accum,
898
+ head_dim_rounded=head_dim_rounded,
899
+ cu_seqlens_q=cu_seqlens_q,
900
+ seqused_q=seqused_q,
901
+ max_seqlen_q=max_seqlen_q,
902
+ tile_m=TILE_M,
903
+ tile_k=TILE_K,
904
+ )
905
+
906
+ grid = launch_grid.get_bwd_grid(
907
+ seqlen_k=seqlen_k,
908
+ num_heads_q=num_heads_q,
909
+ batch_size=batch_size,
910
+ num_splits=num_splits,
911
+ )
912
+
913
+ triton.set_allocator(utils.alloc_fn)
914
+
915
+ kernel[grid](
916
+ query,
917
+ key,
918
+ value,
919
+ dout,
920
+ lse_log2,
921
+ dpsum,
922
+ dq_accum,
923
+ dk_accum,
924
+ dv_accum,
925
+ softmax_scale,
926
+ query_scale,
927
+ key_scale,
928
+ value_scale,
929
+ window_sizes,
930
+ 0,
931
+ query.stride(-2),
932
+ query.stride(0),
933
+ 0,
934
+ key.stride(-2),
935
+ key.stride(0),
936
+ 0,
937
+ value.stride(-2),
938
+ value.stride(0),
939
+ 0,
940
+ dout.stride(-2),
941
+ dout.stride(0),
942
+ 0,
943
+ lse_log2.stride(0),
944
+ 0,
945
+ dpsum.stride(0),
946
+ 0,
947
+ dq_accum.stride(0),
948
+ head_dim_rounded,
949
+ 0,
950
+ dk_accum.stride(-2),
951
+ dk_accum.stride(-3),
952
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
953
+ 0,
954
+ dv_accum.stride(-2),
955
+ dv_accum.stride(-3),
956
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
957
+ window_sizes.stride(0),
958
+ cu_seqlens_q,
959
+ cu_seqlens_k,
960
+ seqused_q,
961
+ seqused_k,
962
+ num_splits,
963
+ seqlen_q=seqlen_q,
964
+ seqlen_k=seqlen_k,
965
+ head_dim=head_dim,
966
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
967
+ SEQLEN_K_CACHE=seqlen_k // 1024,
968
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
969
+ TILE_M=TILE_M,
970
+ TILE_N=TILE_N,
971
+ TILE_K=TILE_K,
972
+ IS_CAUSAL=is_causal,
973
+ IS_LOCAL=is_local,
974
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
975
+ HAS_CU_SEQLENS_Q=True,
976
+ HAS_CU_SEQLENS_K=True,
977
+ HAS_SEQUSED_Q=seqused_q is not None,
978
+ HAS_SEQUSED_K=seqused_k is not None,
979
+ num_warps=num_warps,
980
+ num_stages=num_stages,
981
+ num_ctas=num_ctas,
982
+ )
983
+
984
+ if launch_config is None or is_autotune:
985
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
986
+ if best is not None:
987
+ launch_template.store_launch_config(
988
+ device=device,
989
+ kernel_name="bwd_dense",
990
+ seqlen_q=seqlen_q,
991
+ seqlen_k=seqlen_k,
992
+ tile_k=TILE_K,
993
+ config=best,
994
+ is_local=is_local,
995
+ qhead_per_kvhead=qhead_per_kvhead,
996
+ is_causal=is_causal,
997
+ is_quant=is_quant,
998
+ )
999
+
1000
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
1001
+ dq_accum=dq_accum,
1002
+ dq=dq,
1003
+ scale=softmax_scale,
1004
+ head_dim_rounded=head_dim_rounded,
1005
+ cu_seqlens_q=cu_seqlens_q,
1006
+ seqused_q=seqused_q,
1007
+ max_seqlen_q=max_seqlen_q,
1008
+ tile_m=TILE_M,
1009
+ tile_k=TILE_K,
1010
+ )
1011
+
1012
+ if is_split_qo and num_splits > 1:
1013
+ dk_accum = dk_accum.sum(dim=0)
1014
+ dv_accum = dv_accum.sum(dim=0)
1015
+
1016
+ dk.copy_(dk_accum)
1017
+ dv.copy_(dv_accum)
1018
+
1019
+ return dq, dk, dv
build/torch-cuda/flash_dense_dec.py ADDED
@@ -0,0 +1,857 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_dec_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnDecGridIndex,
19
+ AttnDecConfig,
20
+ AttnDecBlockScheduler,
21
+ AttnDecPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _dec_inner_dense_kernel(
29
+ config: AttnDecConfig,
30
+ ptrs_sched: AttnDecPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ q_tile,
34
+ k_tile,
35
+ k_ptrs,
36
+ v_ptrs,
37
+ acc_o,
38
+ row_max,
39
+ row_sum,
40
+ n_block,
41
+ n_block_min,
42
+ IS_MASK: tl.constexpr,
43
+ MASK_LOCAL: tl.constexpr,
44
+ MASK_SINK: tl.constexpr,
45
+ CHECK_INF: tl.constexpr,
46
+ ):
47
+ # Compute attention scores
48
+ acc_s = tl.dot(q_tile, k_tile.T)
49
+
50
+ # Prefetch next key tile
51
+ if n_block > n_block_min:
52
+ # Load next key tile
53
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
54
+
55
+ if IS_MASK:
56
+ # Apply mask to attention scores
57
+ acc_s = mask_sched.apply_mask(
58
+ acc_s=acc_s,
59
+ iter_block=n_block,
60
+ MASK_LOCAL=MASK_LOCAL,
61
+ MASK_SINK=MASK_SINK,
62
+ )
63
+
64
+ # Apply online softmax
65
+ p, row_max, row_sum, row_scale = softmax_sched.online_softmax(
66
+ acc_s=acc_s,
67
+ row_max=row_max,
68
+ row_sum=row_sum,
69
+ CHECK_INF=CHECK_INF,
70
+ )
71
+
72
+ # Load value tile
73
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
74
+
75
+ # Rescale output accumulator
76
+ acc_o = softmax_sched.rescale_o(
77
+ acc_o=acc_o,
78
+ row_scale=row_scale,
79
+ )
80
+
81
+ # Update output accumulator
82
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
83
+
84
+ return k_tile, acc_o, row_max, row_sum
85
+
86
+
87
+ @triton.jit(repr=kernel_repr.dec_dense_repr)
88
+ def _dec_dense_kernel(
89
+ Q,
90
+ K,
91
+ V,
92
+ Out,
93
+ Lse,
94
+ softmax_scale,
95
+ query_scale,
96
+ key_scale,
97
+ value_scale,
98
+ window_sizes,
99
+ stride_qb,
100
+ stride_qh,
101
+ stride_qm,
102
+ stride_kb,
103
+ stride_kh,
104
+ stride_kn,
105
+ stride_vb,
106
+ stride_vh,
107
+ stride_vn,
108
+ stride_ob,
109
+ stride_oh,
110
+ stride_om,
111
+ stride_os,
112
+ stride_lb,
113
+ stride_lh,
114
+ stride_lm,
115
+ stride_ls,
116
+ stride_wh,
117
+ cu_seqlens_q,
118
+ cu_seqlens_k,
119
+ seqused_q,
120
+ seqused_k,
121
+ num_splits,
122
+ seqlen_q,
123
+ seqlen_k,
124
+ head_dim,
125
+ SEQLEN_Q_CACHE: tl.constexpr,
126
+ SEQLEN_K_CACHE: tl.constexpr,
127
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
128
+ TILE_M: tl.constexpr,
129
+ TILE_N: tl.constexpr,
130
+ TILE_K: tl.constexpr,
131
+ IS_LOCAL: tl.constexpr,
132
+ HAS_CU_SEQLENS_Q: tl.constexpr,
133
+ HAS_CU_SEQLENS_K: tl.constexpr,
134
+ HAS_SEQUSED_Q: tl.constexpr,
135
+ HAS_SEQUSED_K: tl.constexpr,
136
+ ):
137
+ # Create grid index
138
+ grid_idx = AttnDecGridIndex.create(
139
+ num_splits=num_splits,
140
+ )
141
+
142
+ # Load window sizes
143
+ (
144
+ window_size_sink,
145
+ window_size_left,
146
+ window_size_right,
147
+ window_size_dist,
148
+ ) = grid_idx.load_window_sizes(
149
+ window_sizes=window_sizes,
150
+ stride_wh=stride_wh,
151
+ IS_LOCAL=IS_LOCAL,
152
+ )
153
+
154
+ # Create config
155
+ config = AttnDecConfig.create(
156
+ softmax_scale=softmax_scale,
157
+ query_scale=query_scale,
158
+ key_scale=key_scale,
159
+ value_scale=value_scale,
160
+ batch_idx=grid_idx.batch_idx,
161
+ window_size_sink=window_size_sink,
162
+ window_size_left=window_size_left,
163
+ window_size_right=window_size_right,
164
+ window_size_dist=window_size_dist,
165
+ head_dim=head_dim,
166
+ cu_seqlens_q=cu_seqlens_q,
167
+ cu_seqlens_k=cu_seqlens_k,
168
+ seqused_q=seqused_q,
169
+ seqused_k=seqused_k,
170
+ seqlen_q=seqlen_q,
171
+ seqlen_k=seqlen_k,
172
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
173
+ TILE_M=TILE_M,
174
+ TILE_N=TILE_N,
175
+ TILE_K=TILE_K,
176
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
177
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
178
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
179
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
180
+ )
181
+
182
+ # Create pointer scheduler
183
+ ptrs_sched = AttnDecPointerScheduler.create(
184
+ config=config,
185
+ Q=Q,
186
+ K=K,
187
+ V=V,
188
+ Out=Out,
189
+ Lse=Lse,
190
+ batch_idx=grid_idx.batch_idx,
191
+ head_idx=grid_idx.head_idx,
192
+ head_kv_idx=grid_idx.head_kv_idx,
193
+ split_idx=grid_idx.split_idx,
194
+ stride_qb=stride_qb,
195
+ stride_qh=stride_qh,
196
+ stride_qm=stride_qm,
197
+ stride_kb=stride_kb,
198
+ stride_kh=stride_kh,
199
+ stride_kn=stride_kn,
200
+ stride_vb=stride_vb,
201
+ stride_vh=stride_vh,
202
+ stride_vn=stride_vn,
203
+ stride_ob=stride_ob,
204
+ stride_oh=stride_oh,
205
+ stride_om=stride_om,
206
+ stride_os=stride_os,
207
+ stride_lb=stride_lb,
208
+ stride_lh=stride_lh,
209
+ stride_lm=stride_lm,
210
+ stride_ls=stride_ls,
211
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
212
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
213
+ )
214
+
215
+ # Create block scheduler
216
+ block_sched = AttnDecBlockScheduler.create(
217
+ config=config,
218
+ split_idx=grid_idx.split_idx,
219
+ num_splits=num_splits,
220
+ IS_LOCAL=IS_LOCAL,
221
+ )
222
+
223
+ # Create mask scheduler
224
+ mask_sched = AttnMaskScheduler.create(config)
225
+
226
+ # Create softmax scheduler
227
+ softmax_sched = SoftmaxScheduler.create(config)
228
+
229
+ # Create pointers
230
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
231
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
232
+
233
+ # Early exit if no n_blocks to process
234
+ if block_sched.is_empty():
235
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
236
+ return
237
+
238
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
239
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
240
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
241
+
242
+ # Initialize accumulators
243
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
244
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
245
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
246
+
247
+ # Load query tile
248
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
249
+
250
+ # Load key tile
251
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
252
+
253
+ # Process n_blocks with seqlen masking
254
+ for n_block in tl.range(
255
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
256
+ ):
257
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
258
+ config=config,
259
+ ptrs_sched=ptrs_sched,
260
+ mask_sched=mask_sched,
261
+ softmax_sched=softmax_sched,
262
+ q_tile=q_tile,
263
+ k_tile=k_tile,
264
+ k_ptrs=k_ptrs,
265
+ v_ptrs=v_ptrs,
266
+ acc_o=acc_o,
267
+ row_max=row_max,
268
+ row_sum=row_sum,
269
+ n_block=n_block,
270
+ n_block_min=block_sched.n_block_max_no_mask,
271
+ IS_MASK=True,
272
+ MASK_LOCAL=True if IS_LOCAL else False,
273
+ MASK_SINK=False,
274
+ CHECK_INF=True,
275
+ )
276
+
277
+ # Process n_blocks without masking
278
+ if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
279
+ # Load key tile
280
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
281
+
282
+ for n_block in tl.range(
283
+ block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
284
+ ):
285
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
286
+ config=config,
287
+ ptrs_sched=ptrs_sched,
288
+ mask_sched=mask_sched,
289
+ softmax_sched=softmax_sched,
290
+ q_tile=q_tile,
291
+ k_tile=k_tile,
292
+ k_ptrs=k_ptrs,
293
+ v_ptrs=v_ptrs,
294
+ acc_o=acc_o,
295
+ row_max=row_max,
296
+ row_sum=row_sum,
297
+ n_block=n_block,
298
+ n_block_min=block_sched.n_block_min,
299
+ IS_MASK=False,
300
+ MASK_LOCAL=False,
301
+ MASK_SINK=False,
302
+ CHECK_INF=False,
303
+ )
304
+
305
+ if IS_LOCAL:
306
+ # Process n_blocks with local right masking
307
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
308
+ # Load key tile
309
+ k_tile = ptrs_sched.load_k(
310
+ config, k_ptrs, block_sched.n_block_window_max - 1
311
+ )
312
+
313
+ for n_block in tl.range(
314
+ block_sched.n_block_window_max - 1,
315
+ block_sched.n_block_window_max_no_mask - 1,
316
+ -1,
317
+ ):
318
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
319
+ config=config,
320
+ ptrs_sched=ptrs_sched,
321
+ mask_sched=mask_sched,
322
+ softmax_sched=softmax_sched,
323
+ q_tile=q_tile,
324
+ k_tile=k_tile,
325
+ k_ptrs=k_ptrs,
326
+ v_ptrs=v_ptrs,
327
+ acc_o=acc_o,
328
+ row_max=row_max,
329
+ row_sum=row_sum,
330
+ n_block=n_block,
331
+ n_block_min=block_sched.n_block_window_max_no_mask,
332
+ IS_MASK=True,
333
+ MASK_LOCAL=True,
334
+ MASK_SINK=False,
335
+ CHECK_INF=True,
336
+ )
337
+
338
+ # Process n_blocks without masking
339
+ if (
340
+ block_sched.n_block_window_max_no_mask
341
+ > block_sched.n_block_window_min_no_mask
342
+ ):
343
+ # Load key tile
344
+ k_tile = ptrs_sched.load_k(
345
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
346
+ )
347
+
348
+ for n_block in tl.range(
349
+ block_sched.n_block_window_max_no_mask - 1,
350
+ block_sched.n_block_window_min_no_mask - 1,
351
+ -1,
352
+ ):
353
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
354
+ config=config,
355
+ ptrs_sched=ptrs_sched,
356
+ mask_sched=mask_sched,
357
+ softmax_sched=softmax_sched,
358
+ q_tile=q_tile,
359
+ k_tile=k_tile,
360
+ k_ptrs=k_ptrs,
361
+ v_ptrs=v_ptrs,
362
+ acc_o=acc_o,
363
+ row_max=row_max,
364
+ row_sum=row_sum,
365
+ n_block=n_block,
366
+ n_block_min=block_sched.n_block_window_min_no_mask,
367
+ IS_MASK=False,
368
+ MASK_LOCAL=False,
369
+ MASK_SINK=False,
370
+ CHECK_INF=False,
371
+ )
372
+
373
+ # Process n_blocks with local left masking
374
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
375
+ # Load key tile
376
+ k_tile = ptrs_sched.load_k(
377
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
378
+ )
379
+
380
+ for n_block in tl.range(
381
+ block_sched.n_block_window_min_no_mask - 1,
382
+ block_sched.n_block_window_min - 1,
383
+ -1,
384
+ ):
385
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
386
+ config=config,
387
+ ptrs_sched=ptrs_sched,
388
+ mask_sched=mask_sched,
389
+ softmax_sched=softmax_sched,
390
+ q_tile=q_tile,
391
+ k_tile=k_tile,
392
+ k_ptrs=k_ptrs,
393
+ v_ptrs=v_ptrs,
394
+ acc_o=acc_o,
395
+ row_max=row_max,
396
+ row_sum=row_sum,
397
+ n_block=n_block,
398
+ n_block_min=block_sched.n_block_window_min,
399
+ IS_MASK=True,
400
+ MASK_LOCAL=True,
401
+ MASK_SINK=False,
402
+ CHECK_INF=True,
403
+ )
404
+
405
+ # Process n_blocks with local sink masking
406
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
407
+ # Load key tile
408
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
409
+
410
+ for n_block in tl.range(
411
+ block_sched.n_block_sink_max - 1,
412
+ block_sched.n_block_sink_min - 1,
413
+ -1,
414
+ ):
415
+ k_tile, acc_o, row_max, row_sum = _dec_inner_dense_kernel(
416
+ config=config,
417
+ ptrs_sched=ptrs_sched,
418
+ mask_sched=mask_sched,
419
+ softmax_sched=softmax_sched,
420
+ q_tile=q_tile,
421
+ k_tile=k_tile,
422
+ k_ptrs=k_ptrs,
423
+ v_ptrs=v_ptrs,
424
+ acc_o=acc_o,
425
+ row_max=row_max,
426
+ row_sum=row_sum,
427
+ n_block=n_block,
428
+ n_block_min=block_sched.n_block_sink_min,
429
+ IS_MASK=True,
430
+ MASK_LOCAL=True,
431
+ MASK_SINK=True,
432
+ CHECK_INF=True,
433
+ )
434
+
435
+ # Finalize softmax
436
+ row_scale, lse_tile = softmax_sched.finalize(
437
+ row_max=row_max,
438
+ row_sum=row_sum,
439
+ IS_LOG2=True,
440
+ )
441
+
442
+ # Store LSE
443
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
444
+
445
+ # Finalize rescale
446
+ acc_o = softmax_sched.rescale_o(
447
+ acc_o=acc_o,
448
+ row_scale=row_scale,
449
+ )
450
+
451
+ # Store output
452
+ ptrs_sched.store_out(config, out_ptrs, acc_o)
453
+
454
+
455
+ _dec_dense_kernel = cache_utils.wrap_kernel(_dec_dense_kernel)
456
+
457
+
458
+ _dec_dense_autotuned_kernel = None
459
+
460
+
461
+ def _get_autotuned_kernel():
462
+ global _dec_dense_autotuned_kernel
463
+ if _dec_dense_autotuned_kernel is None:
464
+ jit_kernel = _dec_dense_kernel._kernel
465
+ autotuned = autotuner.make_dec_dense_autotuned_kernel(jit_kernel)
466
+ _dec_dense_autotuned_kernel = autotuner.AutotunedKernel(autotuned)
467
+ return _dec_dense_autotuned_kernel
468
+
469
+
470
+ def _flash_dense_attn_decode(
471
+ query: torch.Tensor,
472
+ key: torch.Tensor,
473
+ value: torch.Tensor,
474
+ softmax_scale: float = None,
475
+ query_scale: Optional[torch.Tensor] = None,
476
+ key_scale: Optional[torch.Tensor] = None,
477
+ value_scale: Optional[torch.Tensor] = None,
478
+ window_sizes: Optional[torch.Tensor] = None,
479
+ is_local: bool = False,
480
+ is_quant: bool = False,
481
+ out: Optional[torch.Tensor] = None,
482
+ lse: Optional[torch.Tensor] = None,
483
+ is_autotune: bool = False,
484
+ skip_checks: bool = False,
485
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
486
+ device = query.device
487
+ num_SMs = cache_utils.get_device_num_sms(device)
488
+ batch_size, num_heads_q, head_dim = query.shape
489
+ _, seqlen_k, num_heads_kv, _ = key.shape
490
+ softmax_scale = (
491
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
492
+ )
493
+ qhead_per_kvhead = num_heads_q // num_heads_kv
494
+ if is_local and window_sizes is None:
495
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
496
+ elif not is_local:
497
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
498
+
499
+ if not skip_checks:
500
+ assert_inputs.assert_dec_inputs(
501
+ query,
502
+ key,
503
+ value,
504
+ query_scale=query_scale,
505
+ key_scale=key_scale,
506
+ value_scale=value_scale,
507
+ window_sizes=window_sizes,
508
+ cu_seqlens_k=None,
509
+ seqused_k=None,
510
+ num_heads_q=num_heads_q,
511
+ num_heads_kv=num_heads_kv,
512
+ head_dim=head_dim,
513
+ is_quant=is_quant,
514
+ device=device,
515
+ )
516
+
517
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
518
+
519
+ launch_config = launch_template.load_launch_config(
520
+ device=device,
521
+ kernel_name="dec_dense",
522
+ seqlen_q=1,
523
+ seqlen_k=seqlen_k,
524
+ tile_k=TILE_K,
525
+ is_local=is_local,
526
+ qhead_per_kvhead=qhead_per_kvhead,
527
+ is_quant=is_quant,
528
+ )
529
+ if launch_config is not None and not is_autotune:
530
+ kernel = _dec_dense_kernel
531
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
532
+ else:
533
+ kernel = _get_autotuned_kernel()
534
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
535
+ TILE_N = 128
536
+ num_warps = num_stages = num_ctas = None
537
+
538
+ num_splits = utils.num_splits_heuristic(
539
+ seqlen_q=qhead_per_kvhead,
540
+ seqlen_k=seqlen_k,
541
+ num_SMs=num_SMs,
542
+ TILE_M=TILE_M,
543
+ TILE_N=TILE_N,
544
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
545
+ if is_local
546
+ else None,
547
+ )
548
+
549
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
550
+ out = (
551
+ out
552
+ if out is not None
553
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
554
+ )
555
+ lse = (
556
+ lse
557
+ if lse is not None
558
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
559
+ )
560
+
561
+ out_partial = torch.empty(
562
+ (num_splits, batch_size, num_heads_q, head_dim),
563
+ dtype=torch.float32,
564
+ device=device,
565
+ )
566
+ lse_partial = torch.empty(
567
+ (num_splits, batch_size, num_heads_q),
568
+ dtype=torch.float32,
569
+ device=device,
570
+ )
571
+
572
+ if not is_quant:
573
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
574
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
575
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
576
+
577
+ grid = launch_grid.get_dec_grid(
578
+ batch_size=batch_size,
579
+ num_heads_kv=num_heads_kv,
580
+ num_splits=num_splits,
581
+ )
582
+
583
+ triton.set_allocator(utils.alloc_fn)
584
+
585
+ kernel[grid](
586
+ query,
587
+ key,
588
+ value,
589
+ out_partial,
590
+ lse_partial,
591
+ softmax_scale,
592
+ query_scale,
593
+ key_scale,
594
+ value_scale,
595
+ window_sizes,
596
+ query.stride(0),
597
+ query.stride(-2),
598
+ 1,
599
+ key.stride(0),
600
+ key.stride(-2),
601
+ key.stride(-3),
602
+ value.stride(0),
603
+ value.stride(-2),
604
+ value.stride(-3),
605
+ out_partial.stride(1),
606
+ out_partial.stride(-2),
607
+ 1,
608
+ out_partial.stride(0),
609
+ lse_partial.stride(1),
610
+ lse_partial.stride(-1),
611
+ 1,
612
+ lse_partial.stride(0),
613
+ window_sizes.stride(0),
614
+ None,
615
+ None,
616
+ None,
617
+ None,
618
+ num_splits,
619
+ seqlen_q=1,
620
+ seqlen_k=seqlen_k,
621
+ head_dim=head_dim,
622
+ SEQLEN_Q_CACHE=0,
623
+ SEQLEN_K_CACHE=seqlen_k // 1024,
624
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
625
+ TILE_M=TILE_M,
626
+ TILE_N=TILE_N,
627
+ TILE_K=TILE_K,
628
+ IS_LOCAL=is_local,
629
+ HAS_CU_SEQLENS_Q=False,
630
+ HAS_CU_SEQLENS_K=False,
631
+ HAS_SEQUSED_Q=False,
632
+ HAS_SEQUSED_K=False,
633
+ num_warps=num_warps,
634
+ num_stages=num_stages,
635
+ num_ctas=num_ctas,
636
+ )
637
+
638
+ if launch_config is None or is_autotune:
639
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
640
+ if best is not None:
641
+ launch_template.store_launch_config(
642
+ device=device,
643
+ kernel_name="dec_dense",
644
+ seqlen_q=1,
645
+ seqlen_k=seqlen_k,
646
+ tile_k=TILE_K,
647
+ config=best,
648
+ is_local=is_local,
649
+ qhead_per_kvhead=qhead_per_kvhead,
650
+ is_quant=is_quant,
651
+ )
652
+
653
+ flash_dec_combine._flash_attn_dec_combine(
654
+ out_partial,
655
+ lse_partial,
656
+ out,
657
+ lse,
658
+ )
659
+
660
+ return out, lse
661
+
662
+
663
+ def _flash_dense_attn_varlen_decode(
664
+ query: torch.Tensor,
665
+ key: torch.Tensor,
666
+ value: torch.Tensor,
667
+ cu_seqlens_k: torch.Tensor,
668
+ max_seqlen_k: int,
669
+ softmax_scale: float = None,
670
+ query_scale: Optional[torch.Tensor] = None,
671
+ key_scale: Optional[torch.Tensor] = None,
672
+ value_scale: Optional[torch.Tensor] = None,
673
+ window_sizes: Optional[torch.Tensor] = None,
674
+ is_local: bool = False,
675
+ is_quant: bool = False,
676
+ seqused_k: Optional[torch.Tensor] = None,
677
+ out: Optional[torch.Tensor] = None,
678
+ lse: Optional[torch.Tensor] = None,
679
+ is_autotune: bool = False,
680
+ skip_checks: bool = False,
681
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
682
+ device = query.device
683
+ num_SMs = cache_utils.get_device_num_sms(device)
684
+ batch_size, num_heads_q, head_dim = query.shape
685
+ _, num_heads_kv, _ = key.shape
686
+ seqlen_k = max_seqlen_k
687
+ softmax_scale = (
688
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
689
+ )
690
+ qhead_per_kvhead = num_heads_q // num_heads_kv
691
+ if is_local and window_sizes is None:
692
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
693
+ elif not is_local:
694
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
695
+
696
+ if not skip_checks:
697
+ assert_inputs.assert_dec_inputs(
698
+ query,
699
+ key,
700
+ value,
701
+ query_scale=query_scale,
702
+ key_scale=key_scale,
703
+ value_scale=value_scale,
704
+ window_sizes=window_sizes,
705
+ cu_seqlens_k=cu_seqlens_k,
706
+ seqused_k=seqused_k,
707
+ num_heads_q=num_heads_q,
708
+ num_heads_kv=num_heads_kv,
709
+ head_dim=head_dim,
710
+ is_quant=is_quant,
711
+ device=device,
712
+ )
713
+
714
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
715
+
716
+ launch_config = launch_template.load_launch_config(
717
+ device=device,
718
+ kernel_name="dec_dense",
719
+ seqlen_q=1,
720
+ seqlen_k=seqlen_k,
721
+ tile_k=TILE_K,
722
+ is_local=is_local,
723
+ qhead_per_kvhead=qhead_per_kvhead,
724
+ is_quant=is_quant,
725
+ )
726
+ if launch_config is not None and not is_autotune:
727
+ kernel = _dec_dense_kernel
728
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
729
+ else:
730
+ kernel = _get_autotuned_kernel()
731
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
732
+ TILE_N = 128
733
+ num_warps = num_stages = num_ctas = None
734
+
735
+ num_splits = utils.num_splits_heuristic(
736
+ seqlen_q=qhead_per_kvhead,
737
+ seqlen_k=seqlen_k,
738
+ num_SMs=num_SMs,
739
+ TILE_M=TILE_M,
740
+ TILE_N=TILE_N,
741
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
742
+ if is_local
743
+ else None,
744
+ )
745
+
746
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
747
+ out = (
748
+ out
749
+ if out is not None
750
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
751
+ )
752
+ lse = (
753
+ lse
754
+ if lse is not None
755
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
756
+ )
757
+
758
+ out_partial = torch.empty(
759
+ (num_splits, batch_size, num_heads_q, head_dim),
760
+ dtype=torch.float32,
761
+ device=device,
762
+ )
763
+ lse_partial = torch.empty(
764
+ (num_splits, batch_size, num_heads_q),
765
+ dtype=torch.float32,
766
+ device=device,
767
+ )
768
+
769
+ if not is_quant:
770
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
771
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
772
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
773
+
774
+ grid = launch_grid.get_dec_grid(
775
+ batch_size=batch_size,
776
+ num_heads_kv=num_heads_kv,
777
+ num_splits=num_splits,
778
+ )
779
+
780
+ triton.set_allocator(utils.alloc_fn)
781
+
782
+ kernel[grid](
783
+ query,
784
+ key,
785
+ value,
786
+ out_partial,
787
+ lse_partial,
788
+ softmax_scale,
789
+ query_scale,
790
+ key_scale,
791
+ value_scale,
792
+ window_sizes,
793
+ query.stride(0),
794
+ query.stride(-2),
795
+ 1,
796
+ 0,
797
+ key.stride(-2),
798
+ key.stride(0),
799
+ 0,
800
+ value.stride(-2),
801
+ value.stride(0),
802
+ out_partial.stride(1),
803
+ out_partial.stride(-2),
804
+ 1,
805
+ out_partial.stride(0),
806
+ lse_partial.stride(1),
807
+ lse_partial.stride(-1),
808
+ 1,
809
+ lse_partial.stride(0),
810
+ window_sizes.stride(0),
811
+ None,
812
+ cu_seqlens_k,
813
+ None,
814
+ seqused_k,
815
+ num_splits,
816
+ seqlen_q=1,
817
+ seqlen_k=seqlen_k,
818
+ head_dim=head_dim,
819
+ SEQLEN_Q_CACHE=0,
820
+ SEQLEN_K_CACHE=seqlen_k // 1024,
821
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
822
+ TILE_M=TILE_M,
823
+ TILE_N=TILE_N,
824
+ TILE_K=TILE_K,
825
+ IS_LOCAL=is_local,
826
+ HAS_CU_SEQLENS_Q=False,
827
+ HAS_CU_SEQLENS_K=True,
828
+ HAS_SEQUSED_Q=False,
829
+ HAS_SEQUSED_K=seqused_k is not None,
830
+ num_warps=num_warps,
831
+ num_stages=num_stages,
832
+ num_ctas=num_ctas,
833
+ )
834
+
835
+ if launch_config is None or is_autotune:
836
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
837
+ if best is not None:
838
+ launch_template.store_launch_config(
839
+ device=device,
840
+ kernel_name="dec_dense",
841
+ seqlen_q=1,
842
+ seqlen_k=seqlen_k,
843
+ tile_k=TILE_K,
844
+ config=best,
845
+ is_local=is_local,
846
+ qhead_per_kvhead=qhead_per_kvhead,
847
+ is_quant=is_quant,
848
+ )
849
+
850
+ flash_dec_combine._flash_attn_dec_combine(
851
+ out_partial,
852
+ lse_partial,
853
+ out,
854
+ lse,
855
+ )
856
+
857
+ return out, lse
build/torch-cuda/flash_dense_fwd.py ADDED
@@ -0,0 +1,957 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_fwd_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnFwdGridIndex,
19
+ AttnFwdConfig,
20
+ AttnFwdBlockScheduler,
21
+ AttnFwdPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _fwd_inner_dense_kernel(
29
+ config: AttnFwdConfig,
30
+ ptrs_sched: AttnFwdPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ q_tile,
34
+ k_tile,
35
+ k_ptrs,
36
+ v_ptrs,
37
+ acc_o,
38
+ row_max,
39
+ row_sum,
40
+ n_block,
41
+ n_block_min,
42
+ IS_MASK: tl.constexpr,
43
+ MASK_CAUSAL: tl.constexpr,
44
+ MASK_LOCAL: tl.constexpr,
45
+ MASK_SINK: tl.constexpr,
46
+ CHECK_INF: tl.constexpr,
47
+ ):
48
+ # Compute attention scores
49
+ acc_s = tl.dot(q_tile, k_tile.T)
50
+
51
+ # Prefetch next key tile
52
+ if n_block > n_block_min:
53
+ # Load next key tile
54
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
55
+
56
+ if IS_MASK:
57
+ # Apply mask to attention scores
58
+ acc_s = mask_sched.apply_mask(
59
+ acc_s=acc_s,
60
+ iter_block=n_block,
61
+ MASK_CAUSAL=MASK_CAUSAL,
62
+ MASK_LOCAL=MASK_LOCAL,
63
+ MASK_SINK=MASK_SINK,
64
+ )
65
+
66
+ # Apply online softmax
67
+ p, row_max, row_sum, row_scale = softmax_sched.online_softmax(
68
+ acc_s=acc_s,
69
+ row_max=row_max,
70
+ row_sum=row_sum,
71
+ CHECK_INF=CHECK_INF,
72
+ )
73
+
74
+ # Load value tile
75
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
76
+
77
+ # Rescale output accumulator
78
+ acc_o = softmax_sched.rescale_o(
79
+ acc_o=acc_o,
80
+ row_scale=row_scale,
81
+ )
82
+
83
+ # Update output accumulator
84
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
85
+
86
+ return k_tile, acc_o, row_max, row_sum
87
+
88
+
89
+ @triton.jit(repr=kernel_repr.fwd_dense_repr)
90
+ def _fwd_dense_kernel(
91
+ Q,
92
+ K,
93
+ V,
94
+ Out,
95
+ Lse,
96
+ softmax_scale,
97
+ query_scale,
98
+ key_scale,
99
+ value_scale,
100
+ window_sizes,
101
+ stride_qb,
102
+ stride_qh,
103
+ stride_qm,
104
+ stride_kb,
105
+ stride_kh,
106
+ stride_kn,
107
+ stride_vb,
108
+ stride_vh,
109
+ stride_vn,
110
+ stride_ob,
111
+ stride_oh,
112
+ stride_om,
113
+ stride_os,
114
+ stride_lb,
115
+ stride_lh,
116
+ stride_ls,
117
+ stride_wh,
118
+ cu_seqlens_q,
119
+ cu_seqlens_k,
120
+ seqused_q,
121
+ seqused_k,
122
+ num_splits,
123
+ seqlen_q,
124
+ seqlen_k,
125
+ head_dim,
126
+ SEQLEN_Q_CACHE: tl.constexpr,
127
+ SEQLEN_K_CACHE: tl.constexpr,
128
+ QHEAD_PER_KVHEAD: tl.constexpr,
129
+ PACK_GQA: tl.constexpr,
130
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
131
+ TILE_M: tl.constexpr,
132
+ TILE_N: tl.constexpr,
133
+ TILE_K: tl.constexpr,
134
+ IS_CAUSAL: tl.constexpr,
135
+ IS_LOCAL: tl.constexpr,
136
+ IS_SPLIT_KV: tl.constexpr,
137
+ HAS_CU_SEQLENS_Q: tl.constexpr,
138
+ HAS_CU_SEQLENS_K: tl.constexpr,
139
+ HAS_SEQUSED_Q: tl.constexpr,
140
+ HAS_SEQUSED_K: tl.constexpr,
141
+ ):
142
+ # Create grid index
143
+ grid_idx = AttnFwdGridIndex.create(
144
+ num_splits=num_splits,
145
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
146
+ IS_SPLIT_KV=IS_SPLIT_KV,
147
+ PACK_GQA=PACK_GQA,
148
+ )
149
+
150
+ # Load window sizes
151
+ (
152
+ window_size_sink,
153
+ window_size_left,
154
+ window_size_right,
155
+ window_size_dist,
156
+ ) = grid_idx.load_window_sizes(
157
+ window_sizes=window_sizes,
158
+ stride_wh=stride_wh,
159
+ IS_LOCAL=IS_LOCAL,
160
+ )
161
+
162
+ # Create config
163
+ config = AttnFwdConfig.create(
164
+ softmax_scale=softmax_scale,
165
+ query_scale=query_scale,
166
+ key_scale=key_scale,
167
+ value_scale=value_scale,
168
+ m_block=grid_idx.m_block,
169
+ batch_idx=grid_idx.batch_idx,
170
+ window_size_sink=window_size_sink,
171
+ window_size_left=window_size_left,
172
+ window_size_right=window_size_right,
173
+ window_size_dist=window_size_dist,
174
+ head_dim=head_dim,
175
+ cu_seqlens_q=cu_seqlens_q,
176
+ cu_seqlens_k=cu_seqlens_k,
177
+ seqused_q=seqused_q,
178
+ seqused_k=seqused_k,
179
+ seqlen_q=seqlen_q,
180
+ seqlen_k=seqlen_k,
181
+ PACK_GQA=PACK_GQA,
182
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
183
+ TILE_M=TILE_M,
184
+ TILE_N=TILE_N,
185
+ TILE_K=TILE_K,
186
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
187
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
188
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
189
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
190
+ )
191
+
192
+ # Create pointer scheduler
193
+ ptrs_sched = AttnFwdPointerScheduler.create(
194
+ config=config,
195
+ Q=Q,
196
+ K=K,
197
+ V=V,
198
+ Out=Out,
199
+ Lse=Lse,
200
+ batch_idx=grid_idx.batch_idx,
201
+ head_idx=grid_idx.head_idx,
202
+ head_kv_idx=grid_idx.head_kv_idx,
203
+ split_idx=grid_idx.split_idx,
204
+ stride_qb=stride_qb,
205
+ stride_qh=stride_qh,
206
+ stride_qm=stride_qm,
207
+ stride_kb=stride_kb,
208
+ stride_kh=stride_kh,
209
+ stride_kn=stride_kn,
210
+ stride_vb=stride_vb,
211
+ stride_vh=stride_vh,
212
+ stride_vn=stride_vn,
213
+ stride_ob=stride_ob,
214
+ stride_oh=stride_oh,
215
+ stride_om=stride_om,
216
+ stride_os=stride_os,
217
+ stride_lb=stride_lb,
218
+ stride_lh=stride_lh,
219
+ stride_ls=stride_ls,
220
+ IS_SPLIT_KV=IS_SPLIT_KV,
221
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
222
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
223
+ )
224
+
225
+ # Create block scheduler
226
+ block_sched = AttnFwdBlockScheduler.create(
227
+ config=config,
228
+ split_idx=grid_idx.split_idx,
229
+ num_splits=num_splits,
230
+ IS_CAUSAL=IS_CAUSAL,
231
+ IS_LOCAL=IS_LOCAL,
232
+ IS_SPLIT_KV=IS_SPLIT_KV,
233
+ )
234
+
235
+ # Create mask scheduler
236
+ mask_sched = AttnMaskScheduler.create(config)
237
+
238
+ # Create softmax scheduler
239
+ softmax_sched = SoftmaxScheduler.create(config)
240
+
241
+ # Create pointers
242
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
243
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
244
+
245
+ # Early exit if no n_blocks to process
246
+ if block_sched.is_empty():
247
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
248
+ return
249
+
250
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
251
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
252
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
253
+
254
+ # Initialize accumulators
255
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
256
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
257
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
258
+
259
+ # Load query tile
260
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
261
+
262
+ # Load key tile
263
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
264
+
265
+ # Process n_blocks with causal masking
266
+ if IS_CAUSAL or IS_LOCAL:
267
+ n_block_max_no_mask = block_sched.n_block_max_no_mask
268
+ for n_block in tl.range(
269
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
270
+ ):
271
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
272
+ config=config,
273
+ ptrs_sched=ptrs_sched,
274
+ mask_sched=mask_sched,
275
+ softmax_sched=softmax_sched,
276
+ q_tile=q_tile,
277
+ k_tile=k_tile,
278
+ k_ptrs=k_ptrs,
279
+ v_ptrs=v_ptrs,
280
+ acc_o=acc_o,
281
+ row_max=row_max,
282
+ row_sum=row_sum,
283
+ n_block=n_block,
284
+ n_block_min=block_sched.n_block_max_no_mask,
285
+ IS_MASK=True,
286
+ MASK_CAUSAL=IS_CAUSAL,
287
+ MASK_LOCAL=True if IS_LOCAL else False,
288
+ MASK_SINK=False,
289
+ CHECK_INF=True,
290
+ )
291
+ else:
292
+ # First iteration with seqlen masking
293
+ n_block = block_sched.n_block_max - 1
294
+ # Triton aggregate does not support attribute reassignment yet
295
+ # block_sched.n_block_max_no_mask = n_block
296
+ n_block_max_no_mask = n_block
297
+
298
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
299
+ config=config,
300
+ ptrs_sched=ptrs_sched,
301
+ mask_sched=mask_sched,
302
+ softmax_sched=softmax_sched,
303
+ q_tile=q_tile,
304
+ k_tile=k_tile,
305
+ k_ptrs=k_ptrs,
306
+ v_ptrs=v_ptrs,
307
+ acc_o=acc_o,
308
+ row_max=row_max,
309
+ row_sum=row_sum,
310
+ n_block=n_block,
311
+ n_block_min=n_block,
312
+ IS_MASK=True,
313
+ MASK_CAUSAL=False,
314
+ MASK_LOCAL=False,
315
+ MASK_SINK=False,
316
+ CHECK_INF=True,
317
+ )
318
+
319
+ # Process n_blocks without masking
320
+ if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
321
+ # Load key tile
322
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
323
+
324
+ for n_block in tl.range(
325
+ n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
326
+ ):
327
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
328
+ config=config,
329
+ ptrs_sched=ptrs_sched,
330
+ mask_sched=mask_sched,
331
+ softmax_sched=softmax_sched,
332
+ q_tile=q_tile,
333
+ k_tile=k_tile,
334
+ k_ptrs=k_ptrs,
335
+ v_ptrs=v_ptrs,
336
+ acc_o=acc_o,
337
+ row_max=row_max,
338
+ row_sum=row_sum,
339
+ n_block=n_block,
340
+ n_block_min=block_sched.n_block_min,
341
+ IS_MASK=False,
342
+ MASK_CAUSAL=False,
343
+ MASK_LOCAL=False,
344
+ MASK_SINK=False,
345
+ CHECK_INF=False,
346
+ )
347
+
348
+ if IS_LOCAL:
349
+ # Process n_blocks with local right masking
350
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
351
+ # Load key tile
352
+ k_tile = ptrs_sched.load_k(
353
+ config, k_ptrs, block_sched.n_block_window_max - 1
354
+ )
355
+
356
+ for n_block in tl.range(
357
+ block_sched.n_block_window_max - 1,
358
+ block_sched.n_block_window_max_no_mask - 1,
359
+ -1,
360
+ ):
361
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
362
+ config=config,
363
+ ptrs_sched=ptrs_sched,
364
+ mask_sched=mask_sched,
365
+ softmax_sched=softmax_sched,
366
+ q_tile=q_tile,
367
+ k_tile=k_tile,
368
+ k_ptrs=k_ptrs,
369
+ v_ptrs=v_ptrs,
370
+ acc_o=acc_o,
371
+ row_max=row_max,
372
+ row_sum=row_sum,
373
+ n_block=n_block,
374
+ n_block_min=block_sched.n_block_window_max_no_mask,
375
+ IS_MASK=True,
376
+ MASK_CAUSAL=False,
377
+ MASK_LOCAL=True,
378
+ MASK_SINK=False,
379
+ CHECK_INF=True,
380
+ )
381
+
382
+ # Process n_blocks without masking
383
+ if (
384
+ block_sched.n_block_window_max_no_mask
385
+ > block_sched.n_block_window_min_no_mask
386
+ ):
387
+ # Load key tile
388
+ k_tile = ptrs_sched.load_k(
389
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
390
+ )
391
+
392
+ for n_block in tl.range(
393
+ block_sched.n_block_window_max_no_mask - 1,
394
+ block_sched.n_block_window_min_no_mask - 1,
395
+ -1,
396
+ ):
397
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
398
+ config=config,
399
+ ptrs_sched=ptrs_sched,
400
+ mask_sched=mask_sched,
401
+ softmax_sched=softmax_sched,
402
+ q_tile=q_tile,
403
+ k_tile=k_tile,
404
+ k_ptrs=k_ptrs,
405
+ v_ptrs=v_ptrs,
406
+ acc_o=acc_o,
407
+ row_max=row_max,
408
+ row_sum=row_sum,
409
+ n_block=n_block,
410
+ n_block_min=block_sched.n_block_window_min_no_mask,
411
+ IS_MASK=False,
412
+ MASK_CAUSAL=False,
413
+ MASK_LOCAL=False,
414
+ MASK_SINK=False,
415
+ CHECK_INF=False,
416
+ )
417
+
418
+ # Process n_blocks with local left masking
419
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
420
+ # Load key tile
421
+ k_tile = ptrs_sched.load_k(
422
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
423
+ )
424
+
425
+ for n_block in tl.range(
426
+ block_sched.n_block_window_min_no_mask - 1,
427
+ block_sched.n_block_window_min - 1,
428
+ -1,
429
+ ):
430
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
431
+ config=config,
432
+ ptrs_sched=ptrs_sched,
433
+ mask_sched=mask_sched,
434
+ softmax_sched=softmax_sched,
435
+ q_tile=q_tile,
436
+ k_tile=k_tile,
437
+ k_ptrs=k_ptrs,
438
+ v_ptrs=v_ptrs,
439
+ acc_o=acc_o,
440
+ row_max=row_max,
441
+ row_sum=row_sum,
442
+ n_block=n_block,
443
+ n_block_min=block_sched.n_block_window_min,
444
+ IS_MASK=True,
445
+ MASK_CAUSAL=False,
446
+ MASK_LOCAL=True,
447
+ MASK_SINK=False,
448
+ CHECK_INF=True,
449
+ )
450
+
451
+ # Process n_blocks with local sink masking
452
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
453
+ # Load key tile
454
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
455
+
456
+ for n_block in tl.range(
457
+ block_sched.n_block_sink_max - 1,
458
+ block_sched.n_block_sink_min - 1,
459
+ -1,
460
+ ):
461
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_dense_kernel(
462
+ config=config,
463
+ ptrs_sched=ptrs_sched,
464
+ mask_sched=mask_sched,
465
+ softmax_sched=softmax_sched,
466
+ q_tile=q_tile,
467
+ k_tile=k_tile,
468
+ k_ptrs=k_ptrs,
469
+ v_ptrs=v_ptrs,
470
+ acc_o=acc_o,
471
+ row_max=row_max,
472
+ row_sum=row_sum,
473
+ n_block=n_block,
474
+ n_block_min=block_sched.n_block_sink_min,
475
+ IS_MASK=True,
476
+ MASK_CAUSAL=False,
477
+ MASK_LOCAL=True,
478
+ MASK_SINK=True,
479
+ CHECK_INF=True,
480
+ )
481
+
482
+ # Finalize softmax
483
+ row_scale, lse_tile = softmax_sched.finalize(
484
+ row_max=row_max,
485
+ row_sum=row_sum,
486
+ IS_LOG2=IS_SPLIT_KV,
487
+ )
488
+
489
+ # Store LSE
490
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
491
+
492
+ # Finalize rescale
493
+ acc_o = softmax_sched.rescale_o(
494
+ acc_o=acc_o,
495
+ row_scale=row_scale,
496
+ )
497
+
498
+ # Store output
499
+ ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
500
+
501
+
502
+ _fwd_dense_kernel = cache_utils.wrap_kernel(_fwd_dense_kernel)
503
+
504
+
505
+ _fwd_dense_kernel_autotuned = None
506
+
507
+
508
+ def _get_autotuned_kernel():
509
+ global _fwd_dense_kernel_autotuned
510
+ if _fwd_dense_kernel_autotuned is None:
511
+ jit_kernel = _fwd_dense_kernel._kernel
512
+ autotuned = autotuner.make_fwd_dense_autotuned_kernel(jit_kernel)
513
+ _fwd_dense_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
514
+ return _fwd_dense_kernel_autotuned
515
+
516
+
517
+ def _flash_dense_attn_forward(
518
+ query: torch.Tensor,
519
+ key: torch.Tensor,
520
+ value: torch.Tensor,
521
+ is_causal: bool = False,
522
+ softmax_scale: float = None,
523
+ query_scale: Optional[torch.Tensor] = None,
524
+ key_scale: Optional[torch.Tensor] = None,
525
+ value_scale: Optional[torch.Tensor] = None,
526
+ window_sizes: Optional[torch.Tensor] = None,
527
+ is_local: bool = False,
528
+ is_quant: bool = False,
529
+ is_split_kv: bool = False,
530
+ pack_gqa: bool = False,
531
+ out: Optional[torch.Tensor] = None,
532
+ lse: Optional[torch.Tensor] = None,
533
+ is_autotune: bool = False,
534
+ skip_checks: bool = False,
535
+ ) -> Tuple[torch.Tensor, torch.Tensor, float]:
536
+ device = query.device
537
+ num_SMs = cache_utils.get_device_num_sms(device)
538
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
539
+ _, seqlen_k, num_heads_kv, _ = key.shape
540
+ softmax_scale = (
541
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
542
+ )
543
+ qhead_per_kvhead = num_heads_q // num_heads_kv
544
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
545
+ if is_local and window_sizes is None:
546
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
547
+ elif not is_local:
548
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
549
+
550
+ if not skip_checks:
551
+ assert_inputs.assert_fwd_inputs(
552
+ query,
553
+ key,
554
+ value,
555
+ query_scale=query_scale,
556
+ key_scale=key_scale,
557
+ value_scale=value_scale,
558
+ window_sizes=window_sizes,
559
+ cu_seqlens_q=None,
560
+ cu_seqlens_k=None,
561
+ seqused_q=None,
562
+ seqused_k=None,
563
+ num_heads_q=num_heads_q,
564
+ num_heads_kv=num_heads_kv,
565
+ head_dim=head_dim,
566
+ is_quant=is_quant,
567
+ device=device,
568
+ )
569
+
570
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
571
+
572
+ _kernel_name = f"fwd_dense{'_split' if is_split_kv else ''}"
573
+ launch_config = launch_template.load_launch_config(
574
+ device=device,
575
+ kernel_name=_kernel_name,
576
+ seqlen_q=seqlen_q,
577
+ seqlen_k=seqlen_k,
578
+ tile_k=TILE_K,
579
+ is_local=is_local,
580
+ qhead_per_kvhead=qhead_per_kvhead,
581
+ is_causal=is_causal,
582
+ pack_gqa=pack_gqa,
583
+ is_quant=is_quant,
584
+ )
585
+ if launch_config is not None and not is_autotune:
586
+ kernel = _fwd_dense_kernel
587
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
588
+ else:
589
+ kernel = _get_autotuned_kernel()
590
+ # Placeholder for pre-launch computations
591
+ TILE_M = TILE_N = 64
592
+ num_warps = num_stages = num_ctas = None
593
+
594
+ num_splits = (
595
+ utils.num_splits_heuristic(
596
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
597
+ seqlen_k=seqlen_k,
598
+ num_SMs=num_SMs,
599
+ TILE_M=TILE_M,
600
+ TILE_N=TILE_N,
601
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
602
+ window_sizes, TILE_N
603
+ )
604
+ if is_local
605
+ else None,
606
+ )
607
+ if is_split_kv
608
+ else 1
609
+ )
610
+
611
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
612
+ out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
613
+ lse = (
614
+ lse
615
+ if lse is not None
616
+ else torch.empty(
617
+ (batch_size, num_heads_q, seqlen_q),
618
+ dtype=torch.float32,
619
+ device=query.device,
620
+ )
621
+ )
622
+
623
+ if is_split_kv:
624
+ out_partial = torch.empty(
625
+ (num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
626
+ dtype=torch.float32,
627
+ device=query.device,
628
+ )
629
+ lse_partial = torch.empty(
630
+ (num_splits, batch_size, num_heads_q, seqlen_q),
631
+ dtype=torch.float32,
632
+ device=query.device,
633
+ )
634
+
635
+ if not is_quant:
636
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
637
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
638
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
639
+
640
+ grid = launch_grid.get_fwd_grid(
641
+ batch_size=batch_size,
642
+ seqlen_q=seqlen_q,
643
+ num_heads_q=num_heads_q,
644
+ num_heads_kv=num_heads_kv,
645
+ pack_gqa=pack_gqa,
646
+ num_splits=num_splits,
647
+ )
648
+
649
+ triton.set_allocator(utils.alloc_fn)
650
+
651
+ kernel[grid](
652
+ query,
653
+ key,
654
+ value,
655
+ out if not is_split_kv else out_partial,
656
+ lse if not is_split_kv else lse_partial,
657
+ softmax_scale,
658
+ query_scale,
659
+ key_scale,
660
+ value_scale,
661
+ window_sizes,
662
+ query.stride(0),
663
+ query.stride(-2),
664
+ query.stride(-3),
665
+ key.stride(0),
666
+ key.stride(-2),
667
+ key.stride(-3),
668
+ value.stride(0),
669
+ value.stride(-2),
670
+ value.stride(-3),
671
+ out.stride(0) if not is_split_kv else out_partial.stride(1),
672
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
673
+ out.stride(-3) if not is_split_kv else out_partial.stride(-3),
674
+ 0 if not is_split_kv else out_partial.stride(0),
675
+ lse.stride(0) if not is_split_kv else lse_partial.stride(1),
676
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
677
+ 0 if not is_split_kv else lse_partial.stride(0),
678
+ window_sizes.stride(0),
679
+ None,
680
+ None,
681
+ None,
682
+ None,
683
+ num_splits,
684
+ seqlen_q=seqlen_q,
685
+ seqlen_k=seqlen_k,
686
+ head_dim=head_dim,
687
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
688
+ SEQLEN_K_CACHE=seqlen_k // 1024,
689
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
690
+ PACK_GQA=pack_gqa,
691
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
692
+ TILE_M=TILE_M,
693
+ TILE_N=TILE_N,
694
+ TILE_K=TILE_K,
695
+ IS_CAUSAL=is_causal,
696
+ IS_LOCAL=is_local,
697
+ IS_SPLIT_KV=is_split_kv,
698
+ HAS_CU_SEQLENS_Q=False,
699
+ HAS_CU_SEQLENS_K=False,
700
+ HAS_SEQUSED_Q=False,
701
+ HAS_SEQUSED_K=False,
702
+ num_warps=num_warps,
703
+ num_stages=num_stages,
704
+ num_ctas=num_ctas,
705
+ )
706
+
707
+ if launch_config is None or is_autotune:
708
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
709
+ if best is not None:
710
+ launch_template.store_launch_config(
711
+ device=device,
712
+ kernel_name=_kernel_name,
713
+ seqlen_q=seqlen_q,
714
+ seqlen_k=seqlen_k,
715
+ tile_k=TILE_K,
716
+ config=best,
717
+ is_local=is_local,
718
+ qhead_per_kvhead=qhead_per_kvhead,
719
+ is_causal=is_causal,
720
+ pack_gqa=pack_gqa,
721
+ is_quant=is_quant,
722
+ )
723
+
724
+ if is_split_kv:
725
+ flash_fwd_combine._flash_attn_fwd_combine(
726
+ out_partial,
727
+ lse_partial,
728
+ out,
729
+ lse,
730
+ )
731
+
732
+ return out, lse, softmax_scale
733
+
734
+
735
+ def _flash_dense_attn_varlen_forward(
736
+ query: torch.Tensor,
737
+ key: torch.Tensor,
738
+ value: torch.Tensor,
739
+ cu_seqlens_q: torch.Tensor,
740
+ cu_seqlens_k: torch.Tensor,
741
+ max_seqlen_q: int,
742
+ max_seqlen_k: int,
743
+ is_causal: bool = False,
744
+ softmax_scale: float = None,
745
+ query_scale: Optional[torch.Tensor] = None,
746
+ key_scale: Optional[torch.Tensor] = None,
747
+ value_scale: Optional[torch.Tensor] = None,
748
+ window_sizes: Optional[torch.Tensor] = None,
749
+ is_local: bool = False,
750
+ is_quant: bool = False,
751
+ is_split_kv: bool = False,
752
+ pack_gqa: bool = False,
753
+ seqused_q: Optional[torch.Tensor] = None,
754
+ seqused_k: Optional[torch.Tensor] = None,
755
+ out: Optional[torch.Tensor] = None,
756
+ lse: Optional[torch.Tensor] = None,
757
+ is_autotune: bool = False,
758
+ skip_checks: bool = False,
759
+ ) -> Tuple[torch.Tensor, torch.Tensor, float]:
760
+ device = query.device
761
+ num_SMs = cache_utils.get_device_num_sms(device)
762
+ total_seqlen_q, num_heads_q, head_dim = query.shape
763
+ _, num_heads_kv, _ = key.shape
764
+ batch_size = cu_seqlens_q.shape[0] - 1
765
+ seqlen_q = max_seqlen_q
766
+ seqlen_k = max_seqlen_k
767
+ softmax_scale = (
768
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
769
+ )
770
+ qhead_per_kvhead = num_heads_q // num_heads_kv
771
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
772
+ if is_local and window_sizes is None:
773
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
774
+ elif not is_local:
775
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
776
+
777
+ if not skip_checks:
778
+ assert_inputs.assert_fwd_inputs(
779
+ query,
780
+ key,
781
+ value,
782
+ query_scale=query_scale,
783
+ key_scale=key_scale,
784
+ value_scale=value_scale,
785
+ window_sizes=window_sizes,
786
+ cu_seqlens_q=cu_seqlens_q,
787
+ cu_seqlens_k=cu_seqlens_k,
788
+ seqused_q=seqused_q,
789
+ seqused_k=seqused_k,
790
+ num_heads_q=num_heads_q,
791
+ num_heads_kv=num_heads_kv,
792
+ head_dim=head_dim,
793
+ is_quant=is_quant,
794
+ device=device,
795
+ )
796
+
797
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
798
+
799
+ _kernel_name = f"fwd_dense{'_split' if is_split_kv else ''}"
800
+ launch_config = launch_template.load_launch_config(
801
+ device=device,
802
+ kernel_name=_kernel_name,
803
+ seqlen_q=seqlen_q,
804
+ seqlen_k=seqlen_k,
805
+ tile_k=TILE_K,
806
+ is_local=is_local,
807
+ qhead_per_kvhead=qhead_per_kvhead,
808
+ is_causal=is_causal,
809
+ pack_gqa=pack_gqa,
810
+ is_quant=is_quant,
811
+ )
812
+ if launch_config is not None and not is_autotune:
813
+ kernel = _fwd_dense_kernel
814
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
815
+ else:
816
+ kernel = _get_autotuned_kernel()
817
+ # Placeholder for pre-launch computations
818
+ TILE_M = TILE_N = 64
819
+ num_warps = num_stages = num_ctas = None
820
+
821
+ num_splits = (
822
+ utils.num_splits_heuristic(
823
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
824
+ seqlen_k=seqlen_k,
825
+ num_SMs=num_SMs,
826
+ TILE_M=TILE_M,
827
+ TILE_N=TILE_N,
828
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
829
+ window_sizes, TILE_N
830
+ )
831
+ if is_local
832
+ else None,
833
+ )
834
+ if is_split_kv
835
+ else 1
836
+ )
837
+
838
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
839
+ out = torch.empty_like(query, dtype=out_dtype)
840
+ lse = torch.empty(
841
+ (num_heads_q, total_seqlen_q),
842
+ dtype=torch.float32,
843
+ device=query.device,
844
+ )
845
+
846
+ if is_split_kv:
847
+ out_partial = torch.empty(
848
+ (num_splits, total_seqlen_q, num_heads_q, head_dim),
849
+ dtype=torch.float32,
850
+ device=query.device,
851
+ )
852
+ lse_partial = torch.empty(
853
+ (num_splits, num_heads_q, total_seqlen_q),
854
+ dtype=torch.float32,
855
+ device=query.device,
856
+ )
857
+
858
+ if not is_quant:
859
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
860
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
861
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
862
+
863
+ grid = launch_grid.get_fwd_grid(
864
+ batch_size=batch_size,
865
+ seqlen_q=seqlen_q,
866
+ num_heads_q=num_heads_q,
867
+ num_heads_kv=num_heads_kv,
868
+ pack_gqa=pack_gqa,
869
+ num_splits=num_splits,
870
+ )
871
+
872
+ triton.set_allocator(utils.alloc_fn)
873
+
874
+ kernel[grid](
875
+ query,
876
+ key,
877
+ value,
878
+ out if not is_split_kv else out_partial,
879
+ lse if not is_split_kv else lse_partial,
880
+ softmax_scale,
881
+ query_scale,
882
+ key_scale,
883
+ value_scale,
884
+ window_sizes,
885
+ 0,
886
+ query.stride(-2),
887
+ query.stride(0),
888
+ 0,
889
+ key.stride(-2),
890
+ key.stride(0),
891
+ 0,
892
+ value.stride(-2),
893
+ value.stride(0),
894
+ 0,
895
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
896
+ out.stride(0) if not is_split_kv else out_partial.stride(-3),
897
+ 0 if not is_split_kv else out_partial.stride(0),
898
+ 0,
899
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
900
+ 0 if not is_split_kv else lse_partial.stride(0),
901
+ window_sizes.stride(0),
902
+ cu_seqlens_q,
903
+ cu_seqlens_k,
904
+ seqused_q,
905
+ seqused_k,
906
+ num_splits,
907
+ seqlen_q=seqlen_q,
908
+ seqlen_k=seqlen_k,
909
+ head_dim=head_dim,
910
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
911
+ SEQLEN_K_CACHE=seqlen_k // 1024,
912
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
913
+ PACK_GQA=pack_gqa,
914
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
915
+ TILE_M=TILE_M,
916
+ TILE_N=TILE_N,
917
+ TILE_K=TILE_K,
918
+ IS_CAUSAL=is_causal,
919
+ IS_LOCAL=is_local,
920
+ IS_SPLIT_KV=is_split_kv,
921
+ HAS_CU_SEQLENS_Q=True,
922
+ HAS_CU_SEQLENS_K=True,
923
+ HAS_SEQUSED_Q=seqused_q is not None,
924
+ HAS_SEQUSED_K=seqused_k is not None,
925
+ num_warps=num_warps,
926
+ num_stages=num_stages,
927
+ num_ctas=num_ctas,
928
+ )
929
+
930
+ if launch_config is None or is_autotune:
931
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
932
+ if best is not None:
933
+ launch_template.store_launch_config(
934
+ device=device,
935
+ kernel_name=_kernel_name,
936
+ seqlen_q=seqlen_q,
937
+ seqlen_k=seqlen_k,
938
+ tile_k=TILE_K,
939
+ config=best,
940
+ is_local=is_local,
941
+ qhead_per_kvhead=qhead_per_kvhead,
942
+ is_causal=is_causal,
943
+ pack_gqa=pack_gqa,
944
+ is_quant=is_quant,
945
+ )
946
+
947
+ if is_split_kv:
948
+ flash_fwd_combine._flash_attn_fwd_combine(
949
+ out_partial,
950
+ lse_partial,
951
+ out,
952
+ lse,
953
+ cu_seqlens_q=cu_seqlens_q,
954
+ seqused_q=seqused_q,
955
+ )
956
+
957
+ return out, lse, softmax_scale
build/torch-cuda/flash_fwd_combine.py ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+ from . import (
6
+ utils,
7
+ cache_utils,
8
+ launch_template,
9
+ launch_grid,
10
+ seqlen_info,
11
+ kernel_repr,
12
+ )
13
+
14
+
15
+ @triton.jit(repr=kernel_repr.fwd_combine_repr)
16
+ def _fwd_combine_kernel(
17
+ Out_partial,
18
+ Lse_partial,
19
+ Out,
20
+ Lse,
21
+ stride_ops,
22
+ stride_opb,
23
+ stride_oph,
24
+ stride_opm,
25
+ stride_lps,
26
+ stride_lpb,
27
+ stride_lph,
28
+ stride_ob,
29
+ stride_oh,
30
+ stride_om,
31
+ stride_lb,
32
+ stride_lh,
33
+ cu_seqlens_q,
34
+ seqused_q,
35
+ num_splits,
36
+ seqlen_q,
37
+ num_heads_q,
38
+ head_dim,
39
+ TILE_M: tl.constexpr,
40
+ TILE_K: tl.constexpr,
41
+ HAS_CU_SEQLENS_Q: tl.constexpr,
42
+ HAS_SEQUSED_Q: tl.constexpr,
43
+ ):
44
+ m_block = tl.program_id(0)
45
+ bh_idx = tl.program_id(1)
46
+ batch_idx = bh_idx // num_heads_q
47
+ head_idx = bh_idx - batch_idx * num_heads_q
48
+
49
+ # Get seqlen info for this batch
50
+ offset_q, actual_seqlen_q = seqlen_info.get_seqlen_info(
51
+ batch_idx=batch_idx,
52
+ seqlen_static=seqlen_q,
53
+ cu_seqlens=cu_seqlens_q,
54
+ seqused=seqused_q,
55
+ HAS_CU_SEQLENS=HAS_CU_SEQLENS_Q,
56
+ HAS_SEQUSED=HAS_SEQUSED_Q,
57
+ )
58
+
59
+ # Initialize base pointers
60
+ out_part_base = seqlen_info.offset_batch_Q(
61
+ Out_partial + head_idx * stride_oph,
62
+ batch_idx,
63
+ offset_q,
64
+ 0,
65
+ stride_opb,
66
+ stride_opm,
67
+ HAS_CU_SEQLENS_Q,
68
+ USE_PADDED=False,
69
+ )
70
+ lse_part_base = seqlen_info.offset_batch_Q(
71
+ Lse_partial + head_idx * stride_lph,
72
+ batch_idx,
73
+ offset_q,
74
+ 0,
75
+ stride_lpb,
76
+ 1,
77
+ HAS_CU_SEQLENS_Q,
78
+ USE_PADDED=False,
79
+ )
80
+ out_base = seqlen_info.offset_batch_Q(
81
+ Out + head_idx * stride_oh,
82
+ batch_idx,
83
+ offset_q,
84
+ 0,
85
+ stride_ob,
86
+ stride_om,
87
+ HAS_CU_SEQLENS_Q,
88
+ USE_PADDED=False,
89
+ )
90
+ lse_base = seqlen_info.offset_batch_Q(
91
+ Lse + head_idx * stride_lh,
92
+ batch_idx,
93
+ offset_q,
94
+ 0,
95
+ stride_lb,
96
+ 1,
97
+ HAS_CU_SEQLENS_Q,
98
+ USE_PADDED=False,
99
+ )
100
+
101
+ # Create descriptors
102
+ out_part_desc = tl.make_tensor_descriptor(
103
+ base=out_part_base,
104
+ shape=[num_splits, actual_seqlen_q, head_dim],
105
+ strides=[stride_ops, stride_opm, 1],
106
+ block_shape=[1, TILE_M, TILE_K],
107
+ )
108
+ lse_part_desc = tl.make_tensor_descriptor(
109
+ base=lse_part_base,
110
+ shape=[num_splits, actual_seqlen_q],
111
+ strides=[stride_lps, 1],
112
+ block_shape=[1, TILE_M],
113
+ )
114
+ out_desc = tl.make_tensor_descriptor(
115
+ base=out_base,
116
+ shape=[actual_seqlen_q, head_dim],
117
+ strides=[stride_om, 1],
118
+ block_shape=[TILE_M, TILE_K],
119
+ )
120
+ lse_desc = tl.make_tensor_descriptor(
121
+ base=lse_base,
122
+ shape=[actual_seqlen_q],
123
+ strides=[1],
124
+ block_shape=[TILE_M],
125
+ )
126
+
127
+ # Initialize accumulators
128
+ e_sum = tl.zeros((TILE_M,), dtype=tl.float32)
129
+ e_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
130
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
131
+
132
+ # Combine split outputs
133
+ for s in tl.range(0, num_splits):
134
+ # Load partial LSE
135
+ lse_s = tl.sum(lse_part_desc.load([s, m_block * TILE_M]), axis=0)
136
+
137
+ # Compute normalized exponentials
138
+ new_e_max = tl.maximum(lse_s, e_max)
139
+ old_scale = tl.where(e_max == float("-inf"), 0.0, tl.exp2(e_max - new_e_max))
140
+ exp_logic = tl.where(lse_s == float("-inf"), 0.0, tl.exp2(lse_s - new_e_max))
141
+
142
+ # Load partial outputs
143
+ o_s = tl.sum(out_part_desc.load([s, m_block * TILE_M, 0]), axis=0)
144
+
145
+ # Compute scaled outputs
146
+ acc_o *= old_scale[:, None]
147
+ acc_o += exp_logic[:, None] * o_s
148
+
149
+ # Update e_sum and e_max
150
+ e_sum = e_sum * old_scale + exp_logic
151
+ e_max = new_e_max
152
+
153
+ # Normalize output
154
+ inv_sum = tl.where((e_sum == 0.0) | (e_sum != e_sum), 0.0, 1.0 / e_sum)
155
+ acc_o *= inv_sum[:, None]
156
+
157
+ # Store output
158
+ out_desc.store([m_block * TILE_M, 0], acc_o.to(Out.dtype.element_ty))
159
+
160
+ # Compute LSE
161
+ # ln2 = math.log(2.0)
162
+ ln2 = 0.6931471805599453
163
+ lse = tl.where(e_sum > 0.0, (e_max + tl.log2(e_sum)) * ln2, float("-inf"))
164
+
165
+ # Store LSE
166
+ lse_desc.store([m_block * TILE_M], lse)
167
+
168
+
169
+ _fwd_combine_kernel = cache_utils.wrap_kernel(_fwd_combine_kernel)
170
+
171
+
172
+ def _flash_attn_fwd_combine(
173
+ out_partial: torch.Tensor,
174
+ lse_partial: torch.Tensor,
175
+ out: torch.Tensor,
176
+ lse: torch.Tensor,
177
+ cu_seqlens_q: torch.Tensor = None,
178
+ seqused_q: torch.Tensor = None,
179
+ ):
180
+ device = out.device
181
+ arch = cache_utils.get_device_arch(device)
182
+ is_varlen = cu_seqlens_q is not None
183
+ num_splits = out_partial.shape[0]
184
+ if not is_varlen:
185
+ batch_size, seqlen_q, num_heads_q, head_dim = out_partial.shape[1:]
186
+ else:
187
+ total_q, num_heads_q, head_dim = out_partial.shape[1:]
188
+ batch_size = cu_seqlens_q.shape[0] - 1
189
+ seqlen_q = total_q
190
+
191
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
192
+
193
+ TILE_M, num_warps, num_stages, num_ctas = (
194
+ launch_template.get_fwd_combine_launch_config(
195
+ tile_k=TILE_K,
196
+ device=device,
197
+ arch=arch,
198
+ )
199
+ )
200
+
201
+ grid = launch_grid.get_fwd_combine_grid(
202
+ batch_size=batch_size,
203
+ seqlen_q=seqlen_q,
204
+ num_heads_q=num_heads_q,
205
+ )
206
+
207
+ triton.set_allocator(utils.alloc_fn)
208
+
209
+ _fwd_combine_kernel[grid](
210
+ out_partial,
211
+ lse_partial,
212
+ out,
213
+ lse,
214
+ out_partial.stride(0),
215
+ out_partial.stride(1) if not is_varlen else 0,
216
+ out_partial.stride(-2),
217
+ out_partial.stride(-3),
218
+ lse_partial.stride(0),
219
+ lse_partial.stride(1) if not is_varlen else 0,
220
+ lse_partial.stride(-2),
221
+ out.stride(0) if not is_varlen else 0,
222
+ out.stride(-2),
223
+ out.stride(-3) if not is_varlen else out.stride(0),
224
+ lse.stride(0) if not is_varlen else 0,
225
+ lse.stride(-2) if not is_varlen else lse.stride(0),
226
+ cu_seqlens_q,
227
+ seqused_q,
228
+ num_splits,
229
+ seqlen_q,
230
+ num_heads_q,
231
+ head_dim,
232
+ TILE_M=TILE_M,
233
+ TILE_K=TILE_K,
234
+ HAS_CU_SEQLENS_Q=is_varlen,
235
+ HAS_SEQUSED_Q=seqused_q is not None,
236
+ num_warps=num_warps,
237
+ num_stages=num_stages,
238
+ num_ctas=num_ctas,
239
+ )
build/torch-cuda/flash_gated_bwd.py ADDED
@@ -0,0 +1,1317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple
2
+
3
+ import math
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from . import (
9
+ assert_inputs,
10
+ utils,
11
+ cache_utils,
12
+ launch_template,
13
+ launch_grid,
14
+ activations,
15
+ flash_bwd_preprocess,
16
+ flash_bwd_postprocess,
17
+ kernel_repr,
18
+ autotuner,
19
+ )
20
+ from .scheduler import (
21
+ AttnBwdGridIndex,
22
+ AttnBwdConfig,
23
+ AttnBwdBlockScheduler,
24
+ AttnBwdPointerScheduler,
25
+ AttnMaskScheduler,
26
+ SoftmaxScheduler,
27
+ )
28
+
29
+
30
+ @triton.jit
31
+ def _bwd_inner_gated_kernel(
32
+ config,
33
+ ptrs_sched,
34
+ mask_sched,
35
+ softmax_sched,
36
+ acc_dk,
37
+ acc_dv,
38
+ acc_dd,
39
+ k_tile,
40
+ v_tile,
41
+ d_tile,
42
+ q_ptrs,
43
+ a_ptrs,
44
+ do_ptrs,
45
+ dq_accum_ptrs,
46
+ da_ptrs,
47
+ lse_ptrs,
48
+ dpsum_ptrs,
49
+ d_max,
50
+ d_min,
51
+ gate_max,
52
+ m_block,
53
+ IS_MASK: tl.constexpr,
54
+ MASK_CAUSAL: tl.constexpr,
55
+ MASK_LOCAL: tl.constexpr,
56
+ MASK_SINK: tl.constexpr,
57
+ ):
58
+ skip_gate = False
59
+ skip_softmax = False
60
+
61
+ # Load alpha tile
62
+ a_tile = ptrs_sched.load_a(config, a_ptrs, m_block)
63
+ a_max = tl.max(a_tile)
64
+ a_min = tl.min(a_tile)
65
+
66
+ # Compute gate threshold for this m_block
67
+ gate_threshold_log2 = config.get_gate_threshold_log2(
68
+ m_block=m_block,
69
+ )
70
+
71
+ # Check if any gates are active for current tile
72
+ gate_max, skip_gate = softmax_sched.online_gate(
73
+ a_max=a_max,
74
+ a_min=a_min,
75
+ d_max=d_max,
76
+ d_min=d_min,
77
+ gate_max=gate_max,
78
+ gate_threshold_log2=gate_threshold_log2,
79
+ )
80
+
81
+ if not skip_gate:
82
+ # Compute attention gates
83
+ acc_s = d_tile[:, None] * a_tile[None, :]
84
+
85
+ # Compute scaling factor for gated attention score gradients
86
+ if config.IS_LOGSIGMOID_GATE:
87
+ ds_scale = tl.sigmoid(-acc_s)
88
+ else:
89
+ ds_scale = 1.0
90
+
91
+ # Load query tile
92
+ q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
93
+
94
+ # Compute attention scores
95
+ if config.IS_LOGSIGMOID_GATE:
96
+ acc_s = softmax_sched.log_sigmoid(
97
+ acc_s=acc_s,
98
+ )
99
+ acc_s += tl.dot(k_tile, q_tile.T)
100
+
101
+ if IS_MASK:
102
+ # Apply mask to attention scores
103
+ acc_s = mask_sched.apply_mask(
104
+ acc_s=acc_s,
105
+ iter_block=m_block,
106
+ MASK_CAUSAL=MASK_CAUSAL,
107
+ MASK_LOCAL=MASK_LOCAL,
108
+ MASK_SINK=MASK_SINK,
109
+ )
110
+
111
+ # Load LSE
112
+ lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
113
+
114
+ # Compute attention weights in log2-domain
115
+ p_log2 = acc_s * config.softmax_scale_log2 - lse_log2[None, :]
116
+
117
+ # Compute softmax threshold for this m_block
118
+ softmax_threshold_log2 = config.get_softmax_threshold_log2(
119
+ m_block=m_block,
120
+ )
121
+
122
+ # Update skip condition based on threshold
123
+ skip_softmax = tl.max(p_log2 - softmax_threshold_log2[None, :]) < 0.0
124
+
125
+ if not skip_softmax:
126
+ # Compute attention weights
127
+ p = activations.exp2(p_log2).to(q_tile.dtype)
128
+
129
+ # Load output gradients tile
130
+ do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
131
+
132
+ # Compute value gradients
133
+ acc_dv += tl.dot(p, do_tile)
134
+
135
+ # Compute attention weight gradients
136
+ acc_dp = tl.dot(v_tile, tl.trans(do_tile))
137
+
138
+ # Load dpsum
139
+ dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
140
+
141
+ # Compute attention score gradients
142
+ ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
143
+
144
+ # Compute query gradients
145
+ dq = tl.dot(tl.trans(ds), k_tile)
146
+
147
+ # Store query gradients
148
+ ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
149
+
150
+ # Compute key gradients
151
+ acc_dk += tl.dot(ds, q_tile)
152
+
153
+ # Compute alpha gradients
154
+ da = tl.sum(ds * ds_scale * d_tile[:, None], axis=0)
155
+
156
+ # Store alpha gradients
157
+ ptrs_sched.store_da(config, da_ptrs, m_block, da)
158
+
159
+ # Compute delta gradients
160
+ acc_dd += tl.sum(ds * ds_scale * a_tile[None, :], axis=1)
161
+
162
+ return (
163
+ acc_dk,
164
+ acc_dv,
165
+ acc_dd,
166
+ gate_max,
167
+ )
168
+
169
+
170
+ @triton.jit(repr=kernel_repr.bwd_gated_repr)
171
+ def _bwd_gated_kernel(
172
+ Q,
173
+ K,
174
+ V,
175
+ A,
176
+ D,
177
+ dO,
178
+ LSELog2,
179
+ dPsum,
180
+ dQaccum,
181
+ dK,
182
+ dV,
183
+ dA,
184
+ dD,
185
+ softmax_scale,
186
+ query_scale,
187
+ key_scale,
188
+ value_scale,
189
+ window_sizes,
190
+ softmax_threshold,
191
+ gate_threshold,
192
+ stride_qb,
193
+ stride_qh,
194
+ stride_qm,
195
+ stride_kb,
196
+ stride_kh,
197
+ stride_kn,
198
+ stride_vb,
199
+ stride_vh,
200
+ stride_vn,
201
+ stride_ab,
202
+ stride_ah,
203
+ stride_db,
204
+ stride_dh,
205
+ stride_dob,
206
+ stride_doh,
207
+ stride_dom,
208
+ stride_lb,
209
+ stride_lh,
210
+ stride_pb,
211
+ stride_ph,
212
+ stride_dqab,
213
+ stride_dqah,
214
+ stride_dqam,
215
+ stride_dkb,
216
+ stride_dkh,
217
+ stride_dkn,
218
+ stride_dks,
219
+ stride_dvb,
220
+ stride_dvh,
221
+ stride_dvn,
222
+ stride_dvs,
223
+ stride_dab,
224
+ stride_dah,
225
+ stride_ddb,
226
+ stride_ddh,
227
+ stride_dds,
228
+ stride_wh,
229
+ cu_seqlens_q,
230
+ cu_seqlens_k,
231
+ seqused_q,
232
+ seqused_k,
233
+ num_splits,
234
+ seqlen_q,
235
+ seqlen_k,
236
+ head_dim,
237
+ SEQLEN_Q_CACHE: tl.constexpr,
238
+ SEQLEN_K_CACHE: tl.constexpr,
239
+ QHEAD_PER_KVHEAD: tl.constexpr,
240
+ TILE_M: tl.constexpr,
241
+ TILE_N: tl.constexpr,
242
+ TILE_K: tl.constexpr,
243
+ IS_CAUSAL: tl.constexpr,
244
+ IS_LOCAL: tl.constexpr,
245
+ IS_SPLIT_QO: tl.constexpr,
246
+ IS_LOGSIGMOID_GATE: tl.constexpr,
247
+ IS_ADAPT_GATE: tl.constexpr,
248
+ HAS_CU_SEQLENS_Q: tl.constexpr,
249
+ HAS_CU_SEQLENS_K: tl.constexpr,
250
+ HAS_SEQUSED_Q: tl.constexpr,
251
+ HAS_SEQUSED_K: tl.constexpr,
252
+ ):
253
+ # Create grid index
254
+ grid_idx = AttnBwdGridIndex.create(
255
+ num_splits=num_splits,
256
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
257
+ IS_SPLIT_QO=IS_SPLIT_QO,
258
+ )
259
+
260
+ # Load window sizes
261
+ (
262
+ window_size_sink,
263
+ window_size_left,
264
+ window_size_right,
265
+ window_size_dist,
266
+ ) = grid_idx.load_window_sizes(
267
+ window_sizes=window_sizes,
268
+ stride_wh=stride_wh,
269
+ IS_LOCAL=IS_LOCAL,
270
+ )
271
+
272
+ # Create config
273
+ config = AttnBwdConfig.create(
274
+ softmax_scale=softmax_scale,
275
+ softmax_threshold=softmax_threshold,
276
+ gate_threshold=gate_threshold,
277
+ query_scale=query_scale,
278
+ key_scale=key_scale,
279
+ value_scale=value_scale,
280
+ n_block=grid_idx.n_block,
281
+ batch_idx=grid_idx.batch_idx,
282
+ window_size_sink=window_size_sink,
283
+ window_size_left=window_size_left,
284
+ window_size_right=window_size_right,
285
+ window_size_dist=window_size_dist,
286
+ head_dim=head_dim,
287
+ cu_seqlens_q=cu_seqlens_q,
288
+ cu_seqlens_k=cu_seqlens_k,
289
+ seqused_q=seqused_q,
290
+ seqused_k=seqused_k,
291
+ seqlen_q=seqlen_q,
292
+ seqlen_k=seqlen_k,
293
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
294
+ TILE_M=TILE_M,
295
+ TILE_N=TILE_N,
296
+ TILE_K=TILE_K,
297
+ IS_CAUSAL=IS_CAUSAL,
298
+ IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
299
+ IS_ADAPT_GATE=IS_ADAPT_GATE,
300
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
301
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
302
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
303
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
304
+ )
305
+
306
+ # Create pointer scheduler
307
+ ptrs_sched = AttnBwdPointerScheduler.create(
308
+ config=config,
309
+ Q=Q,
310
+ K=K,
311
+ V=V,
312
+ A=A,
313
+ D=D,
314
+ dO=dO,
315
+ LSELog2=LSELog2,
316
+ dPsum=dPsum,
317
+ dQaccum=dQaccum,
318
+ dK=dK,
319
+ dV=dV,
320
+ dA=dA,
321
+ dD=dD,
322
+ batch_idx=grid_idx.batch_idx,
323
+ head_idx=grid_idx.head_idx,
324
+ head_kv_idx=grid_idx.head_kv_idx,
325
+ split_idx=grid_idx.split_idx,
326
+ stride_qb=stride_qb,
327
+ stride_qh=stride_qh,
328
+ stride_qm=stride_qm,
329
+ stride_kb=stride_kb,
330
+ stride_kh=stride_kh,
331
+ stride_kn=stride_kn,
332
+ stride_vb=stride_vb,
333
+ stride_vh=stride_vh,
334
+ stride_vn=stride_vn,
335
+ stride_ab=stride_ab,
336
+ stride_ah=stride_ah,
337
+ stride_db=stride_db,
338
+ stride_dh=stride_dh,
339
+ stride_dob=stride_dob,
340
+ stride_doh=stride_doh,
341
+ stride_dom=stride_dom,
342
+ stride_lb=stride_lb,
343
+ stride_lh=stride_lh,
344
+ stride_pb=stride_pb,
345
+ stride_ph=stride_ph,
346
+ stride_dqab=stride_dqab,
347
+ stride_dqah=stride_dqah,
348
+ stride_dqam=stride_dqam,
349
+ stride_dkb=stride_dkb,
350
+ stride_dkh=stride_dkh,
351
+ stride_dkn=stride_dkn,
352
+ stride_dks=stride_dks,
353
+ stride_dvb=stride_dvb,
354
+ stride_dvh=stride_dvh,
355
+ stride_dvn=stride_dvn,
356
+ stride_dvs=stride_dvs,
357
+ stride_dab=stride_dab,
358
+ stride_dah=stride_dah,
359
+ stride_ddb=stride_ddb,
360
+ stride_ddh=stride_ddh,
361
+ stride_dds=stride_dds,
362
+ IS_SPLIT_QO=IS_SPLIT_QO,
363
+ IS_GATED=True,
364
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
365
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
366
+ )
367
+
368
+ # Create block scheduler
369
+ block_sched = AttnBwdBlockScheduler.create(
370
+ config=config,
371
+ split_idx=grid_idx.split_idx,
372
+ num_splits=num_splits,
373
+ IS_CAUSAL=IS_CAUSAL,
374
+ IS_LOCAL=IS_LOCAL,
375
+ IS_SPLIT_QO=IS_SPLIT_QO,
376
+ )
377
+
378
+ # Create mask scheduler
379
+ mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
380
+
381
+ # Create softmax scheduler
382
+ softmax_sched = SoftmaxScheduler.create(config)
383
+
384
+ # Early exit if no n_blocks to process
385
+ if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
386
+ return
387
+
388
+ # Create pointers
389
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
390
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
391
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
392
+ a_ptrs = ptrs_sched.make_a_ptrs(config)
393
+ d_ptrs = ptrs_sched.make_d_ptrs(config)
394
+ do_ptrs = ptrs_sched.make_do_ptrs(config)
395
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
396
+ dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
397
+ dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
398
+ dk_ptrs = ptrs_sched.make_dk_ptrs(config)
399
+ dv_ptrs = ptrs_sched.make_dv_ptrs(config)
400
+ da_ptrs = ptrs_sched.make_da_ptrs(config)
401
+ dd_ptrs = ptrs_sched.make_dd_ptrs(config)
402
+
403
+ # Initialize accumulators
404
+ gate_max = tl.full((), float("-inf"), dtype=tl.float32)
405
+ acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
406
+ acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
407
+ acc_dd = tl.zeros((TILE_N,), dtype=tl.float32)
408
+
409
+ # Load key tile
410
+ k_tile = ptrs_sched.load_k(config, k_ptrs)
411
+
412
+ # Load value tile
413
+ v_tile = ptrs_sched.load_v(config, v_ptrs)
414
+
415
+ # Load delta tile
416
+ d_tile = ptrs_sched.load_d(config, d_ptrs)
417
+ d_max = tl.max(d_tile)
418
+ d_min = tl.min(d_tile)
419
+
420
+ # Process m_blocks with causal masking
421
+ if IS_CAUSAL or IS_LOCAL:
422
+ for m_block in tl.range(
423
+ block_sched.m_block_min, block_sched.m_block_min_no_mask
424
+ ):
425
+ (
426
+ acc_dk,
427
+ acc_dv,
428
+ acc_dd,
429
+ gate_max,
430
+ ) = _bwd_inner_gated_kernel(
431
+ config=config,
432
+ ptrs_sched=ptrs_sched,
433
+ mask_sched=mask_sched,
434
+ softmax_sched=softmax_sched,
435
+ acc_dk=acc_dk,
436
+ acc_dv=acc_dv,
437
+ acc_dd=acc_dd,
438
+ k_tile=k_tile,
439
+ v_tile=v_tile,
440
+ d_tile=d_tile,
441
+ q_ptrs=q_ptrs,
442
+ a_ptrs=a_ptrs,
443
+ do_ptrs=do_ptrs,
444
+ dq_accum_ptrs=dq_accum_ptrs,
445
+ da_ptrs=da_ptrs,
446
+ lse_ptrs=lse_ptrs,
447
+ dpsum_ptrs=dpsum_ptrs,
448
+ d_max=d_max,
449
+ d_min=d_min,
450
+ gate_max=gate_max,
451
+ m_block=m_block,
452
+ IS_MASK=True,
453
+ MASK_CAUSAL=IS_CAUSAL,
454
+ MASK_LOCAL=True if IS_LOCAL else False,
455
+ MASK_SINK=False,
456
+ )
457
+
458
+ # Process m_blocks without masking
459
+ if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
460
+ for m_block in tl.range(
461
+ block_sched.m_block_min_no_mask, block_sched.m_block_max
462
+ ):
463
+ (
464
+ acc_dk,
465
+ acc_dv,
466
+ acc_dd,
467
+ gate_max,
468
+ ) = _bwd_inner_gated_kernel(
469
+ config=config,
470
+ ptrs_sched=ptrs_sched,
471
+ mask_sched=mask_sched,
472
+ softmax_sched=softmax_sched,
473
+ acc_dk=acc_dk,
474
+ acc_dv=acc_dv,
475
+ acc_dd=acc_dd,
476
+ k_tile=k_tile,
477
+ v_tile=v_tile,
478
+ d_tile=d_tile,
479
+ q_ptrs=q_ptrs,
480
+ a_ptrs=a_ptrs,
481
+ do_ptrs=do_ptrs,
482
+ dq_accum_ptrs=dq_accum_ptrs,
483
+ da_ptrs=da_ptrs,
484
+ lse_ptrs=lse_ptrs,
485
+ dpsum_ptrs=dpsum_ptrs,
486
+ d_max=d_max,
487
+ d_min=d_min,
488
+ gate_max=gate_max,
489
+ m_block=m_block,
490
+ IS_MASK=False,
491
+ MASK_CAUSAL=False,
492
+ MASK_LOCAL=False,
493
+ MASK_SINK=False,
494
+ )
495
+
496
+ if IS_LOCAL:
497
+ # Process m_blocks with local right masking
498
+ if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
499
+ for m_block in tl.range(
500
+ block_sched.m_block_window_min,
501
+ block_sched.m_block_window_min_no_mask,
502
+ ):
503
+ (
504
+ acc_dk,
505
+ acc_dv,
506
+ acc_dd,
507
+ gate_max,
508
+ ) = _bwd_inner_gated_kernel(
509
+ config=config,
510
+ ptrs_sched=ptrs_sched,
511
+ mask_sched=mask_sched,
512
+ softmax_sched=softmax_sched,
513
+ acc_dk=acc_dk,
514
+ acc_dv=acc_dv,
515
+ acc_dd=acc_dd,
516
+ k_tile=k_tile,
517
+ v_tile=v_tile,
518
+ d_tile=d_tile,
519
+ q_ptrs=q_ptrs,
520
+ a_ptrs=a_ptrs,
521
+ do_ptrs=do_ptrs,
522
+ dq_accum_ptrs=dq_accum_ptrs,
523
+ da_ptrs=da_ptrs,
524
+ lse_ptrs=lse_ptrs,
525
+ dpsum_ptrs=dpsum_ptrs,
526
+ d_max=d_max,
527
+ d_min=d_min,
528
+ gate_max=gate_max,
529
+ m_block=m_block,
530
+ IS_MASK=True,
531
+ MASK_CAUSAL=False,
532
+ MASK_LOCAL=True,
533
+ MASK_SINK=False,
534
+ )
535
+
536
+ # Process m_blocks without masking
537
+ if (
538
+ block_sched.m_block_window_min_no_mask
539
+ < block_sched.m_block_window_max_no_mask
540
+ ):
541
+ for m_block in tl.range(
542
+ block_sched.m_block_window_min_no_mask,
543
+ block_sched.m_block_window_max_no_mask,
544
+ ):
545
+ (
546
+ acc_dk,
547
+ acc_dv,
548
+ acc_dd,
549
+ gate_max,
550
+ ) = _bwd_inner_gated_kernel(
551
+ config=config,
552
+ ptrs_sched=ptrs_sched,
553
+ mask_sched=mask_sched,
554
+ softmax_sched=softmax_sched,
555
+ acc_dk=acc_dk,
556
+ acc_dv=acc_dv,
557
+ acc_dd=acc_dd,
558
+ k_tile=k_tile,
559
+ v_tile=v_tile,
560
+ d_tile=d_tile,
561
+ q_ptrs=q_ptrs,
562
+ a_ptrs=a_ptrs,
563
+ do_ptrs=do_ptrs,
564
+ dq_accum_ptrs=dq_accum_ptrs,
565
+ da_ptrs=da_ptrs,
566
+ lse_ptrs=lse_ptrs,
567
+ dpsum_ptrs=dpsum_ptrs,
568
+ d_max=d_max,
569
+ d_min=d_min,
570
+ gate_max=gate_max,
571
+ m_block=m_block,
572
+ IS_MASK=False,
573
+ MASK_CAUSAL=False,
574
+ MASK_LOCAL=False,
575
+ MASK_SINK=False,
576
+ )
577
+
578
+ # Process m_blocks with local left masking
579
+ if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
580
+ for m_block in tl.range(
581
+ block_sched.m_block_window_max_no_mask,
582
+ block_sched.m_block_window_max,
583
+ ):
584
+ (
585
+ acc_dk,
586
+ acc_dv,
587
+ acc_dd,
588
+ gate_max,
589
+ ) = _bwd_inner_gated_kernel(
590
+ config=config,
591
+ ptrs_sched=ptrs_sched,
592
+ mask_sched=mask_sched,
593
+ softmax_sched=softmax_sched,
594
+ acc_dk=acc_dk,
595
+ acc_dv=acc_dv,
596
+ acc_dd=acc_dd,
597
+ k_tile=k_tile,
598
+ v_tile=v_tile,
599
+ d_tile=d_tile,
600
+ q_ptrs=q_ptrs,
601
+ a_ptrs=a_ptrs,
602
+ do_ptrs=do_ptrs,
603
+ dq_accum_ptrs=dq_accum_ptrs,
604
+ da_ptrs=da_ptrs,
605
+ lse_ptrs=lse_ptrs,
606
+ dpsum_ptrs=dpsum_ptrs,
607
+ d_max=d_max,
608
+ d_min=d_min,
609
+ gate_max=gate_max,
610
+ m_block=m_block,
611
+ IS_MASK=True,
612
+ MASK_CAUSAL=False,
613
+ MASK_LOCAL=True,
614
+ MASK_SINK=False,
615
+ )
616
+
617
+ # Process m_blocks with local sink masking
618
+ if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
619
+ for m_block in tl.range(
620
+ block_sched.m_block_sink_min,
621
+ block_sched.m_block_sink_max,
622
+ ):
623
+ (
624
+ acc_dk,
625
+ acc_dv,
626
+ acc_dd,
627
+ gate_max,
628
+ ) = _bwd_inner_gated_kernel(
629
+ config=config,
630
+ ptrs_sched=ptrs_sched,
631
+ mask_sched=mask_sched,
632
+ softmax_sched=softmax_sched,
633
+ acc_dk=acc_dk,
634
+ acc_dv=acc_dv,
635
+ acc_dd=acc_dd,
636
+ k_tile=k_tile,
637
+ v_tile=v_tile,
638
+ d_tile=d_tile,
639
+ q_ptrs=q_ptrs,
640
+ a_ptrs=a_ptrs,
641
+ do_ptrs=do_ptrs,
642
+ dq_accum_ptrs=dq_accum_ptrs,
643
+ da_ptrs=da_ptrs,
644
+ lse_ptrs=lse_ptrs,
645
+ dpsum_ptrs=dpsum_ptrs,
646
+ d_max=d_max,
647
+ d_min=d_min,
648
+ gate_max=gate_max,
649
+ m_block=m_block,
650
+ IS_MASK=True,
651
+ MASK_CAUSAL=False,
652
+ MASK_LOCAL=True,
653
+ MASK_SINK=True,
654
+ )
655
+
656
+ # Store value gradients
657
+ ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
658
+
659
+ # Scale delta gradients
660
+ acc_dd = acc_dd * softmax_scale
661
+
662
+ # Store delta gradients
663
+ ptrs_sched.store_dd(config, dd_ptrs, acc_dd)
664
+
665
+ # Scale key gradients
666
+ acc_dk = acc_dk * softmax_scale
667
+
668
+ # Store key gradients
669
+ ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
670
+
671
+
672
+ _bwd_gated_kernel = cache_utils.wrap_kernel(_bwd_gated_kernel)
673
+
674
+
675
+ _bwd_gated_kernel_autotuned = None
676
+
677
+
678
+ def _get_autotuned_kernel():
679
+ global _bwd_gated_kernel_autotuned
680
+ if _bwd_gated_kernel_autotuned is None:
681
+ jit_kernel = _bwd_gated_kernel._kernel
682
+ autotuned = autotuner.make_bwd_gated_autotuned_kernel(jit_kernel)
683
+ _bwd_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
684
+ return _bwd_gated_kernel_autotuned
685
+
686
+
687
+ def _flash_gated_attn_backward(
688
+ query: torch.Tensor,
689
+ key: torch.Tensor,
690
+ value: torch.Tensor,
691
+ alpha: torch.Tensor,
692
+ delta: torch.Tensor,
693
+ out: torch.Tensor,
694
+ dout: torch.Tensor,
695
+ lse: torch.Tensor,
696
+ is_causal: bool = False,
697
+ softmax_scale: float = None,
698
+ query_scale: Optional[torch.Tensor] = None,
699
+ key_scale: Optional[torch.Tensor] = None,
700
+ value_scale: Optional[torch.Tensor] = None,
701
+ window_sizes: Optional[torch.Tensor] = None,
702
+ softmax_threshold: float = None,
703
+ gate_threshold: float = None,
704
+ is_logsigmoid_gate: bool = True,
705
+ is_adapt_gate: bool = True,
706
+ is_local: bool = False,
707
+ is_quant: bool = False,
708
+ is_split_qo: bool = False,
709
+ is_autotune: bool = False,
710
+ skip_checks: bool = False,
711
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
712
+ device = query.device
713
+ num_SMs = cache_utils.get_device_num_sms(device)
714
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
715
+ _, seqlen_k, num_heads_kv, _ = key.shape
716
+ softmax_scale = (
717
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
718
+ )
719
+ softmax_threshold = (
720
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
721
+ )
722
+ gate_threshold = (
723
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
724
+ )
725
+ qhead_per_kvhead = num_heads_q // num_heads_kv
726
+ if is_local and window_sizes is None:
727
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
728
+ elif not is_local:
729
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
730
+
731
+ if not skip_checks:
732
+ assert_inputs.assert_bwd_inputs(
733
+ query,
734
+ key,
735
+ value,
736
+ out,
737
+ dout,
738
+ lse,
739
+ alpha=alpha,
740
+ delta=delta,
741
+ query_scale=query_scale,
742
+ key_scale=key_scale,
743
+ value_scale=value_scale,
744
+ window_sizes=window_sizes,
745
+ cu_seqlens_q=None,
746
+ cu_seqlens_k=None,
747
+ seqused_q=None,
748
+ seqused_k=None,
749
+ num_heads_q=num_heads_q,
750
+ num_heads_kv=num_heads_kv,
751
+ head_dim=head_dim,
752
+ is_quant=is_quant,
753
+ device=device,
754
+ )
755
+
756
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
757
+
758
+ launch_config = launch_template.load_launch_config(
759
+ device=device,
760
+ kernel_name="bwd_gated",
761
+ seqlen_q=seqlen_q,
762
+ seqlen_k=seqlen_k,
763
+ tile_k=TILE_K,
764
+ is_local=is_local,
765
+ qhead_per_kvhead=qhead_per_kvhead,
766
+ is_causal=is_causal,
767
+ is_quant=is_quant,
768
+ )
769
+ if launch_config is not None and not is_autotune:
770
+ kernel = _bwd_gated_kernel
771
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
772
+ else:
773
+ kernel = _get_autotuned_kernel()
774
+ # Placeholder for pre-launch computations
775
+ TILE_M = TILE_N = 64
776
+ num_warps = num_stages = num_ctas = None
777
+
778
+ num_splits = (
779
+ utils.num_splits_heuristic(
780
+ seqlen_q=seqlen_k,
781
+ seqlen_k=seqlen_q,
782
+ num_SMs=num_SMs,
783
+ TILE_M=TILE_N,
784
+ TILE_N=TILE_M,
785
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
786
+ window_sizes, TILE_M
787
+ )
788
+ if is_local
789
+ else None,
790
+ )
791
+ if is_split_qo
792
+ else 1
793
+ )
794
+
795
+ seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
796
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
797
+
798
+ if not is_quant:
799
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
800
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
801
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
802
+
803
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
804
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
805
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
806
+ da = torch.empty_like(alpha)
807
+ dd = torch.empty_like(delta)
808
+ lse_log2 = torch.empty(
809
+ (batch_size, num_heads_q, seqlen_q_rounded),
810
+ dtype=torch.float32,
811
+ device=query.device,
812
+ )
813
+ dpsum = torch.empty(
814
+ (batch_size, num_heads_q, seqlen_q_rounded),
815
+ dtype=torch.float32,
816
+ device=query.device,
817
+ )
818
+ dq_accum = torch.empty(
819
+ (batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
820
+ dtype=torch.float32,
821
+ device=query.device,
822
+ )
823
+ dk_accum = torch.zeros(
824
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
825
+ if is_split_qo and num_splits > 1
826
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
827
+ dtype=torch.float32,
828
+ device=query.device,
829
+ )
830
+ dv_accum = torch.zeros(
831
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
832
+ if is_split_qo and num_splits > 1
833
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
834
+ dtype=torch.float32,
835
+ device=query.device,
836
+ )
837
+ da_accum = torch.zeros(
838
+ (batch_size, num_heads_q, seqlen_q_rounded),
839
+ dtype=torch.float32,
840
+ device=query.device,
841
+ )
842
+ dd_accum = torch.zeros(
843
+ (num_splits, batch_size, num_heads_kv, seqlen_k)
844
+ if is_split_qo and num_splits > 1
845
+ else (batch_size, num_heads_kv, seqlen_k),
846
+ dtype=torch.float32,
847
+ device=query.device,
848
+ )
849
+
850
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
851
+ out=out,
852
+ dout=dout,
853
+ dpsum=dpsum,
854
+ lse=lse,
855
+ lse_log2=lse_log2,
856
+ dq_accum=dq_accum,
857
+ head_dim_rounded=head_dim_rounded,
858
+ tile_m=TILE_M,
859
+ tile_k=TILE_K,
860
+ )
861
+
862
+ grid = launch_grid.get_bwd_grid(
863
+ seqlen_k=seqlen_k,
864
+ num_heads_q=num_heads_q,
865
+ batch_size=batch_size,
866
+ num_splits=num_splits,
867
+ )
868
+
869
+ triton.set_allocator(utils.alloc_fn)
870
+
871
+ kernel[grid](
872
+ query,
873
+ key,
874
+ value,
875
+ alpha,
876
+ delta,
877
+ dout,
878
+ lse_log2,
879
+ dpsum,
880
+ dq_accum,
881
+ dk_accum,
882
+ dv_accum,
883
+ da_accum,
884
+ dd_accum,
885
+ softmax_scale,
886
+ query_scale,
887
+ key_scale,
888
+ value_scale,
889
+ window_sizes,
890
+ softmax_threshold,
891
+ gate_threshold,
892
+ query.stride(0),
893
+ query.stride(-2),
894
+ query.stride(-3),
895
+ key.stride(0),
896
+ key.stride(-2),
897
+ key.stride(-3),
898
+ value.stride(0),
899
+ value.stride(-2),
900
+ value.stride(-3),
901
+ alpha.stride(0),
902
+ alpha.stride(-2),
903
+ delta.stride(0),
904
+ delta.stride(-2),
905
+ dout.stride(0),
906
+ dout.stride(-2),
907
+ dout.stride(-3),
908
+ lse_log2.stride(0),
909
+ lse_log2.stride(-2),
910
+ dpsum.stride(0),
911
+ dpsum.stride(-2),
912
+ dq_accum.stride(0),
913
+ dq_accum.stride(1),
914
+ head_dim_rounded,
915
+ dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
916
+ dk_accum.stride(-2),
917
+ dk_accum.stride(-3),
918
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
919
+ dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
920
+ dv_accum.stride(-2),
921
+ dv_accum.stride(-3),
922
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
923
+ da_accum.stride(0),
924
+ da_accum.stride(-2),
925
+ dd_accum.stride(-3) if is_split_qo and num_splits > 1 else dd_accum.stride(0),
926
+ dd_accum.stride(-2),
927
+ dd_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
928
+ window_sizes.stride(0),
929
+ None,
930
+ None,
931
+ None,
932
+ None,
933
+ num_splits,
934
+ seqlen_q=seqlen_q,
935
+ seqlen_k=seqlen_k,
936
+ head_dim=head_dim,
937
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
938
+ SEQLEN_K_CACHE=seqlen_k // 1024,
939
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
940
+ TILE_M=TILE_M,
941
+ TILE_N=TILE_N,
942
+ TILE_K=TILE_K,
943
+ IS_CAUSAL=is_causal,
944
+ IS_LOCAL=is_local,
945
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
946
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
947
+ IS_ADAPT_GATE=is_adapt_gate,
948
+ HAS_CU_SEQLENS_Q=False,
949
+ HAS_CU_SEQLENS_K=False,
950
+ HAS_SEQUSED_Q=False,
951
+ HAS_SEQUSED_K=False,
952
+ num_warps=num_warps,
953
+ num_stages=num_stages,
954
+ num_ctas=num_ctas,
955
+ )
956
+
957
+ if launch_config is None or is_autotune:
958
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
959
+ if best is not None:
960
+ launch_template.store_launch_config(
961
+ device=device,
962
+ kernel_name="bwd_gated",
963
+ seqlen_q=seqlen_q,
964
+ seqlen_k=seqlen_k,
965
+ tile_k=TILE_K,
966
+ config=best,
967
+ is_local=is_local,
968
+ qhead_per_kvhead=qhead_per_kvhead,
969
+ is_causal=is_causal,
970
+ is_quant=is_quant,
971
+ )
972
+
973
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
974
+ dq_accum=dq_accum,
975
+ dq=dq,
976
+ da_accum=da_accum,
977
+ da=da,
978
+ scale=softmax_scale,
979
+ head_dim_rounded=head_dim_rounded,
980
+ tile_m=TILE_M,
981
+ tile_k=TILE_K,
982
+ )
983
+
984
+ if is_split_qo and num_splits > 1:
985
+ dk_accum = dk_accum.sum(dim=0)
986
+ dv_accum = dv_accum.sum(dim=0)
987
+ dd_accum = dd_accum.sum(dim=0)
988
+
989
+ dk.copy_(dk_accum)
990
+ dv.copy_(dv_accum)
991
+ dd.copy_(dd_accum)
992
+
993
+ return dq, dk, dv, da, dd
994
+
995
+
996
+ def _flash_gated_attn_varlen_backward(
997
+ query: torch.Tensor,
998
+ key: torch.Tensor,
999
+ value: torch.Tensor,
1000
+ alpha: torch.Tensor,
1001
+ delta: torch.Tensor,
1002
+ out: torch.Tensor,
1003
+ dout: torch.Tensor,
1004
+ lse: torch.Tensor,
1005
+ cu_seqlens_q: torch.Tensor,
1006
+ cu_seqlens_k: torch.Tensor,
1007
+ max_seqlen_q: Optional[int] = None,
1008
+ max_seqlen_k: Optional[int] = None,
1009
+ is_causal: bool = False,
1010
+ softmax_scale: float = None,
1011
+ query_scale: Optional[torch.Tensor] = None,
1012
+ key_scale: Optional[torch.Tensor] = None,
1013
+ value_scale: Optional[torch.Tensor] = None,
1014
+ window_sizes: Optional[torch.Tensor] = None,
1015
+ softmax_threshold: float = None,
1016
+ gate_threshold: float = None,
1017
+ is_logsigmoid_gate: bool = True,
1018
+ is_adapt_gate: bool = True,
1019
+ is_local: bool = False,
1020
+ is_quant: bool = False,
1021
+ seqused_q: Optional[torch.Tensor] = None,
1022
+ seqused_k: Optional[torch.Tensor] = None,
1023
+ is_split_qo: bool = False,
1024
+ is_autotune: bool = False,
1025
+ skip_checks: bool = False,
1026
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
1027
+ device = query.device
1028
+ num_SMs = cache_utils.get_device_num_sms(device)
1029
+ total_q, num_heads_q, head_dim = query.shape
1030
+ total_k, num_heads_kv, _ = key.shape
1031
+ batch_size = cu_seqlens_q.shape[0] - 1
1032
+ seqlen_q = max_seqlen_q
1033
+ seqlen_k = max_seqlen_k
1034
+ softmax_scale = (
1035
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
1036
+ )
1037
+ softmax_threshold = (
1038
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
1039
+ )
1040
+ gate_threshold = (
1041
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
1042
+ )
1043
+ qhead_per_kvhead = num_heads_q // num_heads_kv
1044
+ if is_local and window_sizes is None:
1045
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
1046
+ elif not is_local:
1047
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
1048
+
1049
+ if not skip_checks:
1050
+ assert_inputs.assert_bwd_inputs(
1051
+ query,
1052
+ key,
1053
+ value,
1054
+ out,
1055
+ dout,
1056
+ lse,
1057
+ alpha=alpha,
1058
+ delta=delta,
1059
+ query_scale=query_scale,
1060
+ key_scale=key_scale,
1061
+ value_scale=value_scale,
1062
+ window_sizes=window_sizes,
1063
+ cu_seqlens_q=cu_seqlens_q,
1064
+ cu_seqlens_k=cu_seqlens_k,
1065
+ seqused_q=seqused_q,
1066
+ seqused_k=seqused_k,
1067
+ num_heads_q=num_heads_q,
1068
+ num_heads_kv=num_heads_kv,
1069
+ head_dim=head_dim,
1070
+ is_quant=is_quant,
1071
+ device=device,
1072
+ )
1073
+
1074
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
1075
+
1076
+ launch_config = launch_template.load_launch_config(
1077
+ device=device,
1078
+ kernel_name="bwd_gated",
1079
+ seqlen_q=seqlen_q,
1080
+ seqlen_k=seqlen_k,
1081
+ tile_k=TILE_K,
1082
+ is_local=is_local,
1083
+ qhead_per_kvhead=qhead_per_kvhead,
1084
+ is_causal=is_causal,
1085
+ is_quant=is_quant,
1086
+ )
1087
+ if launch_config is not None and not is_autotune:
1088
+ kernel = _bwd_gated_kernel
1089
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
1090
+ else:
1091
+ kernel = _get_autotuned_kernel()
1092
+ # Placeholder for pre-launch computations
1093
+ TILE_M = TILE_N = 64
1094
+ num_warps = num_stages = num_ctas = None
1095
+
1096
+ num_splits = (
1097
+ utils.num_splits_heuristic(
1098
+ seqlen_q=seqlen_k,
1099
+ seqlen_k=seqlen_q,
1100
+ num_SMs=num_SMs,
1101
+ TILE_M=TILE_N,
1102
+ TILE_N=TILE_M,
1103
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
1104
+ window_sizes, TILE_M
1105
+ )
1106
+ if is_local
1107
+ else None,
1108
+ )
1109
+ if is_split_qo
1110
+ else 1
1111
+ )
1112
+
1113
+ total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
1114
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
1115
+
1116
+ if not is_quant:
1117
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
1118
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
1119
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
1120
+
1121
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
1122
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
1123
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
1124
+ da = torch.empty_like(alpha)
1125
+ dd = torch.empty_like(delta)
1126
+ lse_log2 = torch.empty(
1127
+ (num_heads_q, total_q_rounded_padded),
1128
+ dtype=torch.float32,
1129
+ device=query.device,
1130
+ )
1131
+ dpsum = torch.empty(
1132
+ (num_heads_q, total_q_rounded_padded),
1133
+ dtype=torch.float32,
1134
+ device=query.device,
1135
+ )
1136
+ dq_accum = torch.empty(
1137
+ (num_heads_q, total_q_rounded_padded * head_dim_rounded),
1138
+ dtype=torch.float32,
1139
+ device=query.device,
1140
+ )
1141
+ dk_accum = torch.zeros(
1142
+ (num_splits, total_k, num_heads_kv, head_dim)
1143
+ if is_split_qo and num_splits > 1
1144
+ else (total_k, num_heads_kv, head_dim),
1145
+ dtype=torch.float32,
1146
+ device=query.device,
1147
+ )
1148
+ dv_accum = torch.zeros(
1149
+ (num_splits, total_k, num_heads_kv, head_dim)
1150
+ if is_split_qo and num_splits > 1
1151
+ else (total_k, num_heads_kv, head_dim),
1152
+ dtype=torch.float32,
1153
+ device=query.device,
1154
+ )
1155
+ da_accum = torch.zeros(
1156
+ (num_heads_q, total_q_rounded_padded),
1157
+ dtype=torch.float32,
1158
+ device=query.device,
1159
+ )
1160
+ dd_accum = torch.zeros(
1161
+ (num_splits, num_heads_kv, total_k)
1162
+ if is_split_qo and num_splits > 1
1163
+ else (num_heads_kv, total_k),
1164
+ dtype=torch.float32,
1165
+ device=query.device,
1166
+ )
1167
+
1168
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
1169
+ out=out,
1170
+ dout=dout,
1171
+ dpsum=dpsum,
1172
+ lse=lse,
1173
+ lse_log2=lse_log2,
1174
+ dq_accum=dq_accum,
1175
+ head_dim_rounded=head_dim_rounded,
1176
+ cu_seqlens_q=cu_seqlens_q,
1177
+ seqused_q=seqused_q,
1178
+ max_seqlen_q=max_seqlen_q,
1179
+ tile_m=TILE_M,
1180
+ tile_k=TILE_K,
1181
+ )
1182
+
1183
+ grid = launch_grid.get_bwd_grid(
1184
+ seqlen_k=seqlen_k,
1185
+ num_heads_q=num_heads_q,
1186
+ batch_size=batch_size,
1187
+ num_splits=num_splits,
1188
+ )
1189
+
1190
+ triton.set_allocator(utils.alloc_fn)
1191
+
1192
+ kernel[grid](
1193
+ query,
1194
+ key,
1195
+ value,
1196
+ alpha,
1197
+ delta,
1198
+ dout,
1199
+ lse_log2,
1200
+ dpsum,
1201
+ dq_accum,
1202
+ dk_accum,
1203
+ dv_accum,
1204
+ da_accum,
1205
+ dd_accum,
1206
+ softmax_scale,
1207
+ query_scale,
1208
+ key_scale,
1209
+ value_scale,
1210
+ window_sizes,
1211
+ softmax_threshold,
1212
+ gate_threshold,
1213
+ 0,
1214
+ query.stride(-2),
1215
+ query.stride(0),
1216
+ 0,
1217
+ key.stride(-2),
1218
+ key.stride(0),
1219
+ 0,
1220
+ value.stride(-2),
1221
+ value.stride(0),
1222
+ 0,
1223
+ alpha.stride(-2),
1224
+ 0,
1225
+ delta.stride(-2),
1226
+ 0,
1227
+ dout.stride(-2),
1228
+ dout.stride(0),
1229
+ 0,
1230
+ lse_log2.stride(0),
1231
+ 0,
1232
+ dpsum.stride(0),
1233
+ 0,
1234
+ dq_accum.stride(0),
1235
+ head_dim_rounded,
1236
+ 0,
1237
+ dk_accum.stride(-2),
1238
+ dk_accum.stride(-3),
1239
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
1240
+ 0,
1241
+ dv_accum.stride(-2),
1242
+ dv_accum.stride(-3),
1243
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
1244
+ 0,
1245
+ da_accum.stride(0),
1246
+ 0,
1247
+ dd_accum.stride(-2),
1248
+ dd_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
1249
+ window_sizes.stride(0),
1250
+ cu_seqlens_q,
1251
+ cu_seqlens_k,
1252
+ seqused_q,
1253
+ seqused_k,
1254
+ num_splits,
1255
+ seqlen_q=seqlen_q,
1256
+ seqlen_k=seqlen_k,
1257
+ head_dim=head_dim,
1258
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
1259
+ SEQLEN_K_CACHE=seqlen_k // 1024,
1260
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
1261
+ TILE_M=TILE_M,
1262
+ TILE_N=TILE_N,
1263
+ TILE_K=TILE_K,
1264
+ IS_CAUSAL=is_causal,
1265
+ IS_LOCAL=is_local,
1266
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
1267
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
1268
+ IS_ADAPT_GATE=is_adapt_gate,
1269
+ HAS_CU_SEQLENS_Q=True,
1270
+ HAS_CU_SEQLENS_K=True,
1271
+ HAS_SEQUSED_Q=seqused_q is not None,
1272
+ HAS_SEQUSED_K=seqused_k is not None,
1273
+ num_warps=num_warps,
1274
+ num_stages=num_stages,
1275
+ num_ctas=num_ctas,
1276
+ )
1277
+
1278
+ if launch_config is None or is_autotune:
1279
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
1280
+ if best is not None:
1281
+ launch_template.store_launch_config(
1282
+ device=device,
1283
+ kernel_name="bwd_gated",
1284
+ seqlen_q=seqlen_q,
1285
+ seqlen_k=seqlen_k,
1286
+ tile_k=TILE_K,
1287
+ config=best,
1288
+ is_local=is_local,
1289
+ qhead_per_kvhead=qhead_per_kvhead,
1290
+ is_causal=is_causal,
1291
+ is_quant=is_quant,
1292
+ )
1293
+
1294
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
1295
+ dq_accum=dq_accum,
1296
+ dq=dq,
1297
+ da_accum=da_accum,
1298
+ da=da,
1299
+ scale=softmax_scale,
1300
+ head_dim_rounded=head_dim_rounded,
1301
+ cu_seqlens_q=cu_seqlens_q,
1302
+ seqused_q=seqused_q,
1303
+ max_seqlen_q=max_seqlen_q,
1304
+ tile_m=TILE_M,
1305
+ tile_k=TILE_K,
1306
+ )
1307
+
1308
+ if is_split_qo and num_splits > 1:
1309
+ dk_accum = dk_accum.sum(dim=0)
1310
+ dv_accum = dv_accum.sum(dim=0)
1311
+ dd_accum = dd_accum.sum(dim=0)
1312
+
1313
+ dk.copy_(dk_accum)
1314
+ dv.copy_(dv_accum)
1315
+ dd.copy_(dd_accum)
1316
+
1317
+ return dq, dk, dv, da, dd
build/torch-cuda/flash_gated_dec.py ADDED
@@ -0,0 +1,1218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_dec_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnDecGridIndex,
19
+ AttnDecConfig,
20
+ AttnDecBlockScheduler,
21
+ AttnDecPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _dec_inner_gated_kernel(
29
+ config: AttnDecConfig,
30
+ ptrs_sched: AttnDecPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ skip_gate_curr,
34
+ acc_s,
35
+ acc_o,
36
+ q_tile,
37
+ a_tile,
38
+ k_ptrs,
39
+ v_ptrs,
40
+ d_ptrs,
41
+ a_max,
42
+ a_min,
43
+ gate_max,
44
+ row_max,
45
+ row_sum,
46
+ n_block,
47
+ n_block_min,
48
+ IS_MASK: tl.constexpr,
49
+ MASK_LOCAL: tl.constexpr,
50
+ MASK_SINK: tl.constexpr,
51
+ CHECK_INF: tl.constexpr,
52
+ ):
53
+ # Load next delta tile
54
+ d_tile = ptrs_sched.load_d(config, d_ptrs, n_block - 1)
55
+ d_max = tl.max(d_tile)
56
+ d_min = tl.min(d_tile)
57
+
58
+ skip_gate_next = True
59
+ if not skip_gate_curr:
60
+ if n_block > n_block_min:
61
+ # Check if any gates are active for next tile
62
+ gate_max, skip_gate_next = softmax_sched.online_gate(
63
+ a_max=a_max,
64
+ a_min=a_min,
65
+ d_max=d_max,
66
+ d_min=d_min,
67
+ gate_max=gate_max,
68
+ gate_threshold_log2=config.gate_threshold_log2,
69
+ )
70
+
71
+ if IS_MASK:
72
+ # Apply mask to attention scores
73
+ acc_s = mask_sched.apply_mask(
74
+ acc_s=acc_s,
75
+ iter_block=n_block,
76
+ MASK_LOCAL=MASK_LOCAL,
77
+ MASK_SINK=MASK_SINK,
78
+ )
79
+
80
+ # Apply online softmax
81
+ p, row_max, row_sum, row_scale, skip_softmax = (
82
+ softmax_sched.online_sparse_softmax(
83
+ acc_s=acc_s,
84
+ row_max=row_max,
85
+ row_sum=row_sum,
86
+ softmax_threshold_log2=config.softmax_threshold_log2,
87
+ CHECK_INF=CHECK_INF,
88
+ )
89
+ )
90
+
91
+ if not skip_softmax:
92
+ # Load value tile
93
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
94
+
95
+ # Rescale output accumulator
96
+ acc_o = softmax_sched.rescale_o(
97
+ acc_o=acc_o,
98
+ row_scale=row_scale,
99
+ )
100
+
101
+ # Update output accumulator
102
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
103
+
104
+ else:
105
+ if n_block > n_block_min:
106
+ # Check if any gates are active for next tile
107
+ gate_max, skip_gate_next = softmax_sched.online_gate(
108
+ a_max=a_max,
109
+ a_min=a_min,
110
+ d_max=d_max,
111
+ d_min=d_min,
112
+ gate_max=gate_max,
113
+ gate_threshold_log2=config.gate_threshold_log2,
114
+ )
115
+
116
+ if not skip_gate_next:
117
+ # Compute attention gates for next tile
118
+ acc_s = a_tile[:, None] * d_tile[None, :]
119
+ if config.IS_LOGSIGMOID_GATE:
120
+ acc_s = softmax_sched.log_sigmoid(
121
+ acc_s=acc_s,
122
+ )
123
+
124
+ # Load next key tile
125
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
126
+
127
+ # Compute attention scores for next tile
128
+ acc_s += tl.dot(q_tile, k_tile.T)
129
+
130
+ return (
131
+ skip_gate_next,
132
+ acc_s,
133
+ acc_o,
134
+ gate_max,
135
+ row_max,
136
+ row_sum,
137
+ )
138
+
139
+
140
+ @triton.jit(repr=kernel_repr.dec_gated_repr)
141
+ def _dec_gated_kernel(
142
+ Q,
143
+ K,
144
+ V,
145
+ A,
146
+ D,
147
+ Out,
148
+ Lse,
149
+ softmax_scale,
150
+ query_scale,
151
+ key_scale,
152
+ value_scale,
153
+ softmax_threshold,
154
+ gate_threshold,
155
+ window_sizes,
156
+ stride_qb,
157
+ stride_qh,
158
+ stride_qm,
159
+ stride_kb,
160
+ stride_kh,
161
+ stride_kn,
162
+ stride_vb,
163
+ stride_vh,
164
+ stride_vn,
165
+ stride_ab,
166
+ stride_ah,
167
+ stride_db,
168
+ stride_dh,
169
+ stride_ob,
170
+ stride_oh,
171
+ stride_om,
172
+ stride_os,
173
+ stride_lb,
174
+ stride_lh,
175
+ stride_lm,
176
+ stride_ls,
177
+ stride_wh,
178
+ cu_seqlens_q,
179
+ cu_seqlens_k,
180
+ seqused_q,
181
+ seqused_k,
182
+ num_splits,
183
+ seqlen_q,
184
+ seqlen_k,
185
+ head_dim,
186
+ SEQLEN_Q_CACHE: tl.constexpr,
187
+ SEQLEN_K_CACHE: tl.constexpr,
188
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
189
+ TILE_M: tl.constexpr,
190
+ TILE_N: tl.constexpr,
191
+ TILE_K: tl.constexpr,
192
+ IS_LOCAL: tl.constexpr,
193
+ HAS_CU_SEQLENS_Q: tl.constexpr,
194
+ HAS_CU_SEQLENS_K: tl.constexpr,
195
+ HAS_SEQUSED_Q: tl.constexpr,
196
+ HAS_SEQUSED_K: tl.constexpr,
197
+ IS_LOGSIGMOID_GATE: tl.constexpr,
198
+ ):
199
+ # Create grid index
200
+ grid_idx = AttnDecGridIndex.create(
201
+ num_splits=num_splits,
202
+ )
203
+
204
+ # Load window sizes
205
+ (
206
+ window_size_sink,
207
+ window_size_left,
208
+ window_size_right,
209
+ window_size_dist,
210
+ ) = grid_idx.load_window_sizes(
211
+ window_sizes=window_sizes,
212
+ stride_wh=stride_wh,
213
+ IS_LOCAL=IS_LOCAL,
214
+ )
215
+
216
+ # Create config
217
+ config = AttnDecConfig.create(
218
+ softmax_scale=softmax_scale,
219
+ softmax_threshold=softmax_threshold,
220
+ gate_threshold=gate_threshold,
221
+ query_scale=query_scale,
222
+ key_scale=key_scale,
223
+ value_scale=value_scale,
224
+ batch_idx=grid_idx.batch_idx,
225
+ window_size_sink=window_size_sink,
226
+ window_size_left=window_size_left,
227
+ window_size_right=window_size_right,
228
+ window_size_dist=window_size_dist,
229
+ head_dim=head_dim,
230
+ cu_seqlens_q=cu_seqlens_q,
231
+ cu_seqlens_k=cu_seqlens_k,
232
+ seqused_q=seqused_q,
233
+ seqused_k=seqused_k,
234
+ seqlen_q=seqlen_q,
235
+ seqlen_k=seqlen_k,
236
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
237
+ TILE_M=TILE_M,
238
+ TILE_N=TILE_N,
239
+ TILE_K=TILE_K,
240
+ IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
241
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
242
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
243
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
244
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
245
+ )
246
+
247
+ # Create pointer scheduler
248
+ ptrs_sched = AttnDecPointerScheduler.create(
249
+ config=config,
250
+ Q=Q,
251
+ K=K,
252
+ V=V,
253
+ A=A,
254
+ D=D,
255
+ Out=Out,
256
+ Lse=Lse,
257
+ batch_idx=grid_idx.batch_idx,
258
+ head_idx=grid_idx.head_idx,
259
+ head_kv_idx=grid_idx.head_kv_idx,
260
+ split_idx=grid_idx.split_idx,
261
+ stride_qb=stride_qb,
262
+ stride_qh=stride_qh,
263
+ stride_qm=stride_qm,
264
+ stride_kb=stride_kb,
265
+ stride_kh=stride_kh,
266
+ stride_kn=stride_kn,
267
+ stride_vb=stride_vb,
268
+ stride_vh=stride_vh,
269
+ stride_vn=stride_vn,
270
+ stride_ab=stride_ab,
271
+ stride_ah=stride_ah,
272
+ stride_db=stride_db,
273
+ stride_dh=stride_dh,
274
+ stride_ob=stride_ob,
275
+ stride_oh=stride_oh,
276
+ stride_om=stride_om,
277
+ stride_os=stride_os,
278
+ stride_lb=stride_lb,
279
+ stride_lh=stride_lh,
280
+ stride_lm=stride_lm,
281
+ stride_ls=stride_ls,
282
+ IS_GATED=True,
283
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
284
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
285
+ )
286
+
287
+ # Create block scheduler
288
+ block_sched = AttnDecBlockScheduler.create(
289
+ config=config,
290
+ split_idx=grid_idx.split_idx,
291
+ num_splits=num_splits,
292
+ IS_LOCAL=IS_LOCAL,
293
+ )
294
+
295
+ # Create mask scheduler
296
+ mask_sched = AttnMaskScheduler.create(config)
297
+
298
+ # Create softmax scheduler
299
+ softmax_sched = SoftmaxScheduler.create(config)
300
+
301
+ # Create pointers
302
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
303
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
304
+
305
+ # Early exit if no n_blocks to process
306
+ if block_sched.is_empty():
307
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
308
+ return
309
+
310
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
311
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
312
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
313
+ a_ptrs = ptrs_sched.make_a_ptrs(config)
314
+ d_ptrs = ptrs_sched.make_d_ptrs(config)
315
+
316
+ # Initialize accumulators
317
+ gate_max = tl.full((), float("-inf"), dtype=tl.float32)
318
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
319
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
320
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
321
+
322
+ # Load query tile
323
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
324
+
325
+ # Load alpha tile
326
+ a_tile = ptrs_sched.load_a(config, a_ptrs)
327
+ a_max = tl.max(a_tile)
328
+ a_min = tl.min(a_tile)
329
+
330
+ # Load delta tile
331
+ d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max - 1)
332
+ d_max = tl.max(d_tile)
333
+ d_min = tl.min(d_tile)
334
+
335
+ # Check if any gates are active for current tile
336
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
337
+ a_max=a_max,
338
+ a_min=a_min,
339
+ d_max=d_max,
340
+ d_min=d_min,
341
+ gate_max=gate_max,
342
+ gate_threshold_log2=config.gate_threshold_log2,
343
+ )
344
+
345
+ # Initialize skip_gate_curr to False for the first iteration
346
+ skip_gate_curr = False
347
+
348
+ # Compute attention gates for first tile
349
+ acc_s = a_tile[:, None] * d_tile[None, :]
350
+ if IS_LOGSIGMOID_GATE:
351
+ acc_s = softmax_sched.log_sigmoid(
352
+ acc_s=acc_s,
353
+ )
354
+
355
+ # Load key tile
356
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
357
+
358
+ # Compute attention scores for first tile
359
+ acc_s += tl.dot(q_tile, k_tile.T)
360
+
361
+ # Process n_blocks with seqlen masking
362
+ for n_block in tl.range(
363
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
364
+ ):
365
+ (
366
+ skip_gate_curr,
367
+ acc_s,
368
+ acc_o,
369
+ gate_max,
370
+ row_max,
371
+ row_sum,
372
+ ) = _dec_inner_gated_kernel(
373
+ config=config,
374
+ ptrs_sched=ptrs_sched,
375
+ mask_sched=mask_sched,
376
+ softmax_sched=softmax_sched,
377
+ skip_gate_curr=skip_gate_curr,
378
+ acc_s=acc_s,
379
+ acc_o=acc_o,
380
+ q_tile=q_tile,
381
+ a_tile=a_tile,
382
+ k_ptrs=k_ptrs,
383
+ v_ptrs=v_ptrs,
384
+ d_ptrs=d_ptrs,
385
+ a_max=a_max,
386
+ a_min=a_min,
387
+ gate_max=gate_max,
388
+ row_max=row_max,
389
+ row_sum=row_sum,
390
+ n_block=n_block,
391
+ n_block_min=block_sched.n_block_max_no_mask,
392
+ IS_MASK=True,
393
+ MASK_LOCAL=True if IS_LOCAL else False,
394
+ MASK_SINK=False,
395
+ CHECK_INF=True,
396
+ )
397
+
398
+ # Process n_blocks without masking
399
+ if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
400
+ # Load key tile
401
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
402
+
403
+ # Load delta tile
404
+ d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max_no_mask - 1)
405
+ d_max = tl.max(d_tile)
406
+ d_min = tl.min(d_tile)
407
+
408
+ # Check if any gates are active for current tile
409
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
410
+ a_max=a_max,
411
+ a_min=a_min,
412
+ d_max=d_max,
413
+ d_min=d_min,
414
+ gate_max=gate_max,
415
+ gate_threshold_log2=config.gate_threshold_log2,
416
+ )
417
+
418
+ # Compute attention gates
419
+ acc_s = a_tile[:, None] * d_tile[None, :]
420
+ if IS_LOGSIGMOID_GATE:
421
+ acc_s = softmax_sched.log_sigmoid(
422
+ acc_s=acc_s,
423
+ )
424
+
425
+ # Compute attention scores
426
+ acc_s += tl.dot(q_tile, k_tile.T)
427
+
428
+ for n_block in tl.range(
429
+ block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
430
+ ):
431
+ (
432
+ skip_gate_curr,
433
+ acc_s,
434
+ acc_o,
435
+ gate_max,
436
+ row_max,
437
+ row_sum,
438
+ ) = _dec_inner_gated_kernel(
439
+ config=config,
440
+ ptrs_sched=ptrs_sched,
441
+ mask_sched=mask_sched,
442
+ softmax_sched=softmax_sched,
443
+ skip_gate_curr=skip_gate_curr,
444
+ acc_s=acc_s,
445
+ acc_o=acc_o,
446
+ q_tile=q_tile,
447
+ a_tile=a_tile,
448
+ k_ptrs=k_ptrs,
449
+ v_ptrs=v_ptrs,
450
+ d_ptrs=d_ptrs,
451
+ a_max=a_max,
452
+ a_min=a_min,
453
+ gate_max=gate_max,
454
+ row_max=row_max,
455
+ row_sum=row_sum,
456
+ n_block=n_block,
457
+ n_block_min=block_sched.n_block_min,
458
+ IS_MASK=False,
459
+ MASK_LOCAL=False,
460
+ MASK_SINK=False,
461
+ CHECK_INF=False,
462
+ )
463
+
464
+ if IS_LOCAL:
465
+ # Process n_blocks with local right masking
466
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
467
+ # Load key tile
468
+ k_tile = ptrs_sched.load_k(
469
+ config, k_ptrs, block_sched.n_block_window_max - 1
470
+ )
471
+
472
+ # Load delta tile
473
+ d_tile = ptrs_sched.load_d(
474
+ config, d_ptrs, block_sched.n_block_window_max - 1
475
+ )
476
+ d_max = tl.max(d_tile)
477
+ d_min = tl.min(d_tile)
478
+
479
+ # Check if any gates are active for current tile
480
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
481
+ a_max=a_max,
482
+ a_min=a_min,
483
+ d_max=d_max,
484
+ d_min=d_min,
485
+ gate_max=gate_max,
486
+ gate_threshold_log2=config.gate_threshold_log2,
487
+ )
488
+
489
+ # Compute attention gates
490
+ acc_s = a_tile[:, None] * d_tile[None, :]
491
+ if IS_LOGSIGMOID_GATE:
492
+ acc_s = softmax_sched.log_sigmoid(
493
+ acc_s=acc_s,
494
+ )
495
+
496
+ # Compute attention scores
497
+ acc_s += tl.dot(q_tile, k_tile.T)
498
+
499
+ for n_block in tl.range(
500
+ block_sched.n_block_window_max - 1,
501
+ block_sched.n_block_window_max_no_mask - 1,
502
+ -1,
503
+ ):
504
+ (
505
+ skip_gate_curr,
506
+ acc_s,
507
+ acc_o,
508
+ gate_max,
509
+ row_max,
510
+ row_sum,
511
+ ) = _dec_inner_gated_kernel(
512
+ config=config,
513
+ ptrs_sched=ptrs_sched,
514
+ mask_sched=mask_sched,
515
+ softmax_sched=softmax_sched,
516
+ skip_gate_curr=skip_gate_curr,
517
+ acc_s=acc_s,
518
+ acc_o=acc_o,
519
+ q_tile=q_tile,
520
+ a_tile=a_tile,
521
+ k_ptrs=k_ptrs,
522
+ v_ptrs=v_ptrs,
523
+ d_ptrs=d_ptrs,
524
+ a_max=a_max,
525
+ a_min=a_min,
526
+ gate_max=gate_max,
527
+ row_max=row_max,
528
+ row_sum=row_sum,
529
+ n_block=n_block,
530
+ n_block_min=block_sched.n_block_window_max_no_mask,
531
+ IS_MASK=True,
532
+ MASK_LOCAL=True,
533
+ MASK_SINK=False,
534
+ CHECK_INF=True,
535
+ )
536
+
537
+ # Process n_blocks without masking
538
+ if (
539
+ block_sched.n_block_window_max_no_mask
540
+ > block_sched.n_block_window_min_no_mask
541
+ ):
542
+ # Load key tile
543
+ k_tile = ptrs_sched.load_k(
544
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
545
+ )
546
+
547
+ # Load delta tile
548
+ d_tile = ptrs_sched.load_d(
549
+ config, d_ptrs, block_sched.n_block_window_max_no_mask - 1
550
+ )
551
+ d_max = tl.max(d_tile)
552
+ d_min = tl.min(d_tile)
553
+
554
+ # Check if any gates are active for current tile
555
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
556
+ a_max=a_max,
557
+ a_min=a_min,
558
+ d_max=d_max,
559
+ d_min=d_min,
560
+ gate_max=gate_max,
561
+ gate_threshold_log2=config.gate_threshold_log2,
562
+ )
563
+
564
+ # Compute attention gates
565
+ acc_s = a_tile[:, None] * d_tile[None, :]
566
+ if IS_LOGSIGMOID_GATE:
567
+ acc_s = softmax_sched.log_sigmoid(
568
+ acc_s=acc_s,
569
+ )
570
+
571
+ # Compute attention scores
572
+ acc_s += tl.dot(q_tile, k_tile.T)
573
+
574
+ for n_block in tl.range(
575
+ block_sched.n_block_window_max_no_mask - 1,
576
+ block_sched.n_block_window_min_no_mask - 1,
577
+ -1,
578
+ ):
579
+ (
580
+ skip_gate_curr,
581
+ acc_s,
582
+ acc_o,
583
+ gate_max,
584
+ row_max,
585
+ row_sum,
586
+ ) = _dec_inner_gated_kernel(
587
+ config=config,
588
+ ptrs_sched=ptrs_sched,
589
+ mask_sched=mask_sched,
590
+ softmax_sched=softmax_sched,
591
+ skip_gate_curr=skip_gate_curr,
592
+ acc_s=acc_s,
593
+ acc_o=acc_o,
594
+ q_tile=q_tile,
595
+ a_tile=a_tile,
596
+ k_ptrs=k_ptrs,
597
+ v_ptrs=v_ptrs,
598
+ d_ptrs=d_ptrs,
599
+ a_max=a_max,
600
+ a_min=a_min,
601
+ gate_max=gate_max,
602
+ row_max=row_max,
603
+ row_sum=row_sum,
604
+ n_block=n_block,
605
+ n_block_min=block_sched.n_block_window_min_no_mask,
606
+ IS_MASK=False,
607
+ MASK_LOCAL=False,
608
+ MASK_SINK=False,
609
+ CHECK_INF=False,
610
+ )
611
+
612
+ # Process n_blocks with local left masking
613
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
614
+ # Load key tile
615
+ k_tile = ptrs_sched.load_k(
616
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
617
+ )
618
+
619
+ # Load delta tile
620
+ d_tile = ptrs_sched.load_d(
621
+ config, d_ptrs, block_sched.n_block_window_min_no_mask - 1
622
+ )
623
+ d_max = tl.max(d_tile)
624
+ d_min = tl.min(d_tile)
625
+
626
+ # Check if any gates are active for current tile
627
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
628
+ a_max=a_max,
629
+ a_min=a_min,
630
+ d_max=d_max,
631
+ d_min=d_min,
632
+ gate_max=gate_max,
633
+ gate_threshold_log2=config.gate_threshold_log2,
634
+ )
635
+
636
+ # Compute attention gates
637
+ acc_s = a_tile[:, None] * d_tile[None, :]
638
+ if IS_LOGSIGMOID_GATE:
639
+ acc_s = softmax_sched.log_sigmoid(
640
+ acc_s=acc_s,
641
+ )
642
+
643
+ # Compute attention scores
644
+ acc_s += tl.dot(q_tile, k_tile.T)
645
+
646
+ for n_block in tl.range(
647
+ block_sched.n_block_window_min_no_mask - 1,
648
+ block_sched.n_block_window_min - 1,
649
+ -1,
650
+ ):
651
+ (
652
+ skip_gate_curr,
653
+ acc_s,
654
+ acc_o,
655
+ gate_max,
656
+ row_max,
657
+ row_sum,
658
+ ) = _dec_inner_gated_kernel(
659
+ config=config,
660
+ ptrs_sched=ptrs_sched,
661
+ mask_sched=mask_sched,
662
+ softmax_sched=softmax_sched,
663
+ skip_gate_curr=skip_gate_curr,
664
+ acc_s=acc_s,
665
+ acc_o=acc_o,
666
+ q_tile=q_tile,
667
+ a_tile=a_tile,
668
+ k_ptrs=k_ptrs,
669
+ v_ptrs=v_ptrs,
670
+ d_ptrs=d_ptrs,
671
+ a_max=a_max,
672
+ a_min=a_min,
673
+ gate_max=gate_max,
674
+ row_max=row_max,
675
+ row_sum=row_sum,
676
+ n_block=n_block,
677
+ n_block_min=block_sched.n_block_window_min,
678
+ IS_MASK=True,
679
+ MASK_LOCAL=True,
680
+ MASK_SINK=False,
681
+ CHECK_INF=True,
682
+ )
683
+
684
+ # Process n_blocks with local sink masking
685
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
686
+ # Load key tile
687
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
688
+
689
+ # Load delta tile
690
+ d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_sink_max - 1)
691
+ d_max = tl.max(d_tile)
692
+ d_min = tl.min(d_tile)
693
+
694
+ # Check if any gates are active for current tile
695
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
696
+ a_max=a_max,
697
+ a_min=a_min,
698
+ d_max=d_max,
699
+ d_min=d_min,
700
+ gate_max=gate_max,
701
+ gate_threshold_log2=config.gate_threshold_log2,
702
+ )
703
+
704
+ # Compute attention gates
705
+ acc_s = a_tile[:, None] * d_tile[None, :]
706
+ if IS_LOGSIGMOID_GATE:
707
+ acc_s = softmax_sched.log_sigmoid(
708
+ acc_s=acc_s,
709
+ )
710
+
711
+ # Compute attention scores
712
+ acc_s += tl.dot(q_tile, k_tile.T)
713
+
714
+ for n_block in tl.range(
715
+ block_sched.n_block_sink_max - 1,
716
+ block_sched.n_block_sink_min - 1,
717
+ -1,
718
+ ):
719
+ (
720
+ skip_gate_curr,
721
+ acc_s,
722
+ acc_o,
723
+ gate_max,
724
+ row_max,
725
+ row_sum,
726
+ ) = _dec_inner_gated_kernel(
727
+ config=config,
728
+ ptrs_sched=ptrs_sched,
729
+ mask_sched=mask_sched,
730
+ softmax_sched=softmax_sched,
731
+ skip_gate_curr=skip_gate_curr,
732
+ acc_s=acc_s,
733
+ acc_o=acc_o,
734
+ q_tile=q_tile,
735
+ a_tile=a_tile,
736
+ k_ptrs=k_ptrs,
737
+ v_ptrs=v_ptrs,
738
+ d_ptrs=d_ptrs,
739
+ a_max=a_max,
740
+ a_min=a_min,
741
+ gate_max=gate_max,
742
+ row_max=row_max,
743
+ row_sum=row_sum,
744
+ n_block=n_block,
745
+ n_block_min=block_sched.n_block_sink_min,
746
+ IS_MASK=True,
747
+ MASK_LOCAL=True,
748
+ MASK_SINK=True,
749
+ CHECK_INF=True,
750
+ )
751
+
752
+ # Finalize softmax
753
+ row_scale, lse_tile = softmax_sched.finalize(
754
+ row_max=row_max,
755
+ row_sum=row_sum,
756
+ IS_LOG2=True,
757
+ )
758
+
759
+ # Store LSE
760
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
761
+
762
+ # Finalize rescale
763
+ acc_o = softmax_sched.rescale_o(
764
+ acc_o=acc_o,
765
+ row_scale=row_scale,
766
+ )
767
+
768
+ # Store output
769
+ ptrs_sched.store_out(config, out_ptrs, acc_o)
770
+
771
+
772
+ _dec_gated_kernel = cache_utils.wrap_kernel(_dec_gated_kernel)
773
+
774
+
775
+ _dec_gated_kernel_autotuned = None
776
+
777
+
778
+ def _get_autotuned_kernel():
779
+ global _dec_gated_kernel_autotuned
780
+ if _dec_gated_kernel_autotuned is None:
781
+ jit_kernel = _dec_gated_kernel._kernel
782
+ autotuned = autotuner.make_dec_gated_autotuned_kernel(jit_kernel)
783
+ _dec_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
784
+ return _dec_gated_kernel_autotuned
785
+
786
+
787
+ def _flash_gated_attn_decode(
788
+ query: torch.Tensor,
789
+ key: torch.Tensor,
790
+ value: torch.Tensor,
791
+ alpha: torch.Tensor,
792
+ delta: torch.Tensor,
793
+ softmax_scale: float = None,
794
+ softmax_threshold: float = None,
795
+ gate_threshold: float = None,
796
+ is_logsigmoid_gate: bool = True,
797
+ query_scale: Optional[torch.Tensor] = None,
798
+ key_scale: Optional[torch.Tensor] = None,
799
+ value_scale: Optional[torch.Tensor] = None,
800
+ window_sizes: Optional[torch.Tensor] = None,
801
+ is_local: bool = False,
802
+ is_quant: bool = False,
803
+ out: Optional[torch.Tensor] = None,
804
+ lse: Optional[torch.Tensor] = None,
805
+ is_autotune: bool = False,
806
+ skip_checks: bool = False,
807
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
808
+ device = query.device
809
+ num_SMs = cache_utils.get_device_num_sms(device)
810
+ batch_size, num_heads_q, head_dim = query.shape
811
+ _, seqlen_k, num_heads_kv, _ = key.shape
812
+ softmax_scale = (
813
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
814
+ )
815
+ softmax_threshold = (
816
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
817
+ )
818
+ gate_threshold = (
819
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
820
+ )
821
+ qhead_per_kvhead = num_heads_q // num_heads_kv
822
+ if is_local and window_sizes is None:
823
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
824
+ elif not is_local:
825
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
826
+
827
+ if not skip_checks:
828
+ assert_inputs.assert_dec_inputs(
829
+ query,
830
+ key,
831
+ value,
832
+ alpha=alpha,
833
+ delta=delta,
834
+ query_scale=query_scale,
835
+ key_scale=key_scale,
836
+ value_scale=value_scale,
837
+ window_sizes=window_sizes,
838
+ cu_seqlens_k=None,
839
+ seqused_k=None,
840
+ num_heads_q=num_heads_q,
841
+ num_heads_kv=num_heads_kv,
842
+ head_dim=head_dim,
843
+ is_quant=is_quant,
844
+ device=device,
845
+ )
846
+
847
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
848
+
849
+ launch_config = launch_template.load_launch_config(
850
+ device=device,
851
+ kernel_name="dec_gated",
852
+ seqlen_q=1,
853
+ seqlen_k=seqlen_k,
854
+ tile_k=TILE_K,
855
+ is_local=is_local,
856
+ qhead_per_kvhead=qhead_per_kvhead,
857
+ is_quant=is_quant,
858
+ )
859
+ if launch_config is not None and not is_autotune:
860
+ kernel = _dec_gated_kernel
861
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
862
+ else:
863
+ kernel = _get_autotuned_kernel()
864
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
865
+ TILE_N = 128
866
+ num_warps = num_stages = num_ctas = None
867
+
868
+ num_splits = utils.num_splits_heuristic(
869
+ seqlen_q=qhead_per_kvhead,
870
+ seqlen_k=seqlen_k,
871
+ num_SMs=num_SMs,
872
+ TILE_M=TILE_M,
873
+ TILE_N=TILE_N,
874
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
875
+ if is_local
876
+ else None,
877
+ )
878
+
879
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
880
+ out = (
881
+ out
882
+ if out is not None
883
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
884
+ )
885
+ lse = (
886
+ lse
887
+ if lse is not None
888
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
889
+ )
890
+
891
+ out_partial = torch.empty(
892
+ (num_splits, batch_size, num_heads_q, head_dim),
893
+ dtype=torch.float32,
894
+ device=device,
895
+ )
896
+ lse_partial = torch.empty(
897
+ (num_splits, batch_size, num_heads_q),
898
+ dtype=torch.float32,
899
+ device=device,
900
+ )
901
+
902
+ if not is_quant:
903
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
904
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
905
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
906
+
907
+ grid = launch_grid.get_dec_grid(
908
+ batch_size=batch_size,
909
+ num_heads_kv=num_heads_kv,
910
+ num_splits=num_splits,
911
+ )
912
+
913
+ triton.set_allocator(utils.alloc_fn)
914
+
915
+ kernel[grid](
916
+ query,
917
+ key,
918
+ value,
919
+ alpha,
920
+ delta,
921
+ out_partial,
922
+ lse_partial,
923
+ softmax_scale,
924
+ query_scale,
925
+ key_scale,
926
+ value_scale,
927
+ softmax_threshold,
928
+ gate_threshold,
929
+ window_sizes,
930
+ query.stride(0),
931
+ query.stride(-2),
932
+ 1,
933
+ key.stride(0),
934
+ key.stride(-2),
935
+ key.stride(-3),
936
+ value.stride(0),
937
+ value.stride(-2),
938
+ value.stride(-3),
939
+ alpha.stride(0),
940
+ 1,
941
+ delta.stride(0),
942
+ delta.stride(-2),
943
+ out_partial.stride(1),
944
+ out_partial.stride(-2),
945
+ 1,
946
+ out_partial.stride(0),
947
+ lse_partial.stride(1),
948
+ lse_partial.stride(-1),
949
+ 1,
950
+ lse_partial.stride(0),
951
+ window_sizes.stride(0),
952
+ None,
953
+ None,
954
+ None,
955
+ None,
956
+ num_splits,
957
+ seqlen_q=1,
958
+ seqlen_k=seqlen_k,
959
+ head_dim=head_dim,
960
+ SEQLEN_Q_CACHE=0,
961
+ SEQLEN_K_CACHE=seqlen_k // 1024,
962
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
963
+ TILE_M=TILE_M,
964
+ TILE_N=TILE_N,
965
+ TILE_K=TILE_K,
966
+ IS_LOCAL=is_local,
967
+ HAS_CU_SEQLENS_Q=False,
968
+ HAS_CU_SEQLENS_K=False,
969
+ HAS_SEQUSED_Q=False,
970
+ HAS_SEQUSED_K=False,
971
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
972
+ num_warps=num_warps,
973
+ num_stages=num_stages,
974
+ num_ctas=num_ctas,
975
+ )
976
+
977
+ if launch_config is None or is_autotune:
978
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
979
+ if best is not None:
980
+ launch_template.store_launch_config(
981
+ device=device,
982
+ kernel_name="dec_gated",
983
+ seqlen_q=1,
984
+ seqlen_k=seqlen_k,
985
+ tile_k=TILE_K,
986
+ config=best,
987
+ is_local=is_local,
988
+ qhead_per_kvhead=qhead_per_kvhead,
989
+ is_quant=is_quant,
990
+ )
991
+
992
+ flash_dec_combine._flash_attn_dec_combine(
993
+ out_partial,
994
+ lse_partial,
995
+ out,
996
+ lse,
997
+ )
998
+
999
+ return out, lse
1000
+
1001
+
1002
+ def _flash_gated_attn_varlen_decode(
1003
+ query: torch.Tensor,
1004
+ key: torch.Tensor,
1005
+ value: torch.Tensor,
1006
+ alpha: torch.Tensor,
1007
+ delta: torch.Tensor,
1008
+ cu_seqlens_k: torch.Tensor,
1009
+ max_seqlen_k: int,
1010
+ softmax_scale: float = None,
1011
+ softmax_threshold: float = None,
1012
+ gate_threshold: float = None,
1013
+ is_logsigmoid_gate: bool = True,
1014
+ query_scale: Optional[torch.Tensor] = None,
1015
+ key_scale: Optional[torch.Tensor] = None,
1016
+ value_scale: Optional[torch.Tensor] = None,
1017
+ window_sizes: Optional[torch.Tensor] = None,
1018
+ is_local: bool = False,
1019
+ is_quant: bool = False,
1020
+ seqused_k: Optional[torch.Tensor] = None,
1021
+ out: Optional[torch.Tensor] = None,
1022
+ lse: Optional[torch.Tensor] = None,
1023
+ is_autotune: bool = False,
1024
+ skip_checks: bool = False,
1025
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
1026
+ device = query.device
1027
+ num_SMs = cache_utils.get_device_num_sms(device)
1028
+ batch_size, num_heads_q, head_dim = query.shape
1029
+ _, num_heads_kv, _ = key.shape
1030
+ seqlen_k = max_seqlen_k
1031
+ softmax_scale = (
1032
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
1033
+ )
1034
+ softmax_threshold = (
1035
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
1036
+ )
1037
+ gate_threshold = (
1038
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
1039
+ )
1040
+ qhead_per_kvhead = num_heads_q // num_heads_kv
1041
+ if is_local and window_sizes is None:
1042
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
1043
+ elif not is_local:
1044
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
1045
+
1046
+ if not skip_checks:
1047
+ assert_inputs.assert_dec_inputs(
1048
+ query,
1049
+ key,
1050
+ value,
1051
+ alpha=alpha,
1052
+ delta=delta,
1053
+ query_scale=query_scale,
1054
+ key_scale=key_scale,
1055
+ value_scale=value_scale,
1056
+ window_sizes=window_sizes,
1057
+ cu_seqlens_k=cu_seqlens_k,
1058
+ seqused_k=seqused_k,
1059
+ num_heads_q=num_heads_q,
1060
+ num_heads_kv=num_heads_kv,
1061
+ head_dim=head_dim,
1062
+ is_quant=is_quant,
1063
+ device=device,
1064
+ )
1065
+
1066
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
1067
+
1068
+ launch_config = launch_template.load_launch_config(
1069
+ device=device,
1070
+ kernel_name="dec_gated",
1071
+ seqlen_q=1,
1072
+ seqlen_k=seqlen_k,
1073
+ tile_k=TILE_K,
1074
+ is_local=is_local,
1075
+ qhead_per_kvhead=qhead_per_kvhead,
1076
+ is_quant=is_quant,
1077
+ )
1078
+ if launch_config is not None and not is_autotune:
1079
+ kernel = _dec_gated_kernel
1080
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
1081
+ else:
1082
+ kernel = _get_autotuned_kernel()
1083
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
1084
+ TILE_N = 128
1085
+ num_warps = num_stages = num_ctas = None
1086
+
1087
+ num_splits = utils.num_splits_heuristic(
1088
+ seqlen_q=qhead_per_kvhead,
1089
+ seqlen_k=seqlen_k,
1090
+ num_SMs=num_SMs,
1091
+ TILE_M=TILE_M,
1092
+ TILE_N=TILE_N,
1093
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
1094
+ if is_local
1095
+ else None,
1096
+ )
1097
+
1098
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
1099
+ out = (
1100
+ out
1101
+ if out is not None
1102
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
1103
+ )
1104
+ lse = (
1105
+ lse
1106
+ if lse is not None
1107
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
1108
+ )
1109
+
1110
+ out_partial = torch.empty(
1111
+ (num_splits, batch_size, num_heads_q, head_dim),
1112
+ dtype=torch.float32,
1113
+ device=device,
1114
+ )
1115
+ lse_partial = torch.empty(
1116
+ (num_splits, batch_size, num_heads_q),
1117
+ dtype=torch.float32,
1118
+ device=device,
1119
+ )
1120
+
1121
+ if not is_quant:
1122
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
1123
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
1124
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
1125
+
1126
+ grid = launch_grid.get_dec_grid(
1127
+ batch_size=batch_size,
1128
+ num_heads_kv=num_heads_kv,
1129
+ num_splits=num_splits,
1130
+ )
1131
+
1132
+ triton.set_allocator(utils.alloc_fn)
1133
+
1134
+ kernel[grid](
1135
+ query,
1136
+ key,
1137
+ value,
1138
+ alpha,
1139
+ delta,
1140
+ out_partial,
1141
+ lse_partial,
1142
+ softmax_scale,
1143
+ query_scale,
1144
+ key_scale,
1145
+ value_scale,
1146
+ softmax_threshold,
1147
+ gate_threshold,
1148
+ window_sizes,
1149
+ query.stride(0),
1150
+ query.stride(-2),
1151
+ 1,
1152
+ 0,
1153
+ key.stride(-2),
1154
+ key.stride(0),
1155
+ 0,
1156
+ value.stride(-2),
1157
+ value.stride(0),
1158
+ alpha.stride(0),
1159
+ 1,
1160
+ 0,
1161
+ delta.stride(-2),
1162
+ out_partial.stride(1),
1163
+ out_partial.stride(-2),
1164
+ 1,
1165
+ out_partial.stride(0),
1166
+ lse_partial.stride(1),
1167
+ lse_partial.stride(-1),
1168
+ 1,
1169
+ lse_partial.stride(0),
1170
+ window_sizes.stride(0),
1171
+ None,
1172
+ cu_seqlens_k,
1173
+ None,
1174
+ seqused_k,
1175
+ num_splits,
1176
+ seqlen_q=1,
1177
+ seqlen_k=seqlen_k,
1178
+ head_dim=head_dim,
1179
+ SEQLEN_Q_CACHE=0,
1180
+ SEQLEN_K_CACHE=seqlen_k // 1024,
1181
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
1182
+ TILE_M=TILE_M,
1183
+ TILE_N=TILE_N,
1184
+ TILE_K=TILE_K,
1185
+ IS_LOCAL=is_local,
1186
+ HAS_CU_SEQLENS_Q=False,
1187
+ HAS_CU_SEQLENS_K=True,
1188
+ HAS_SEQUSED_Q=False,
1189
+ HAS_SEQUSED_K=seqused_k is not None,
1190
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
1191
+ num_warps=num_warps,
1192
+ num_stages=num_stages,
1193
+ num_ctas=num_ctas,
1194
+ )
1195
+
1196
+ if launch_config is None or is_autotune:
1197
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
1198
+ if best is not None:
1199
+ launch_template.store_launch_config(
1200
+ device=device,
1201
+ kernel_name="dec_gated",
1202
+ seqlen_q=1,
1203
+ seqlen_k=seqlen_k,
1204
+ tile_k=TILE_K,
1205
+ config=best,
1206
+ is_local=is_local,
1207
+ qhead_per_kvhead=qhead_per_kvhead,
1208
+ is_quant=is_quant,
1209
+ )
1210
+
1211
+ flash_dec_combine._flash_attn_dec_combine(
1212
+ out_partial,
1213
+ lse_partial,
1214
+ out,
1215
+ lse,
1216
+ )
1217
+
1218
+ return out, lse
build/torch-cuda/flash_gated_fwd.py ADDED
@@ -0,0 +1,1337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_fwd_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnFwdGridIndex,
19
+ AttnFwdConfig,
20
+ AttnFwdBlockScheduler,
21
+ AttnFwdPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _fwd_inner_gated_kernel(
29
+ config: AttnFwdConfig,
30
+ ptrs_sched: AttnFwdPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ skip_gate_curr,
34
+ acc_s,
35
+ acc_o,
36
+ q_tile,
37
+ a_tile,
38
+ k_ptrs,
39
+ v_ptrs,
40
+ d_ptrs,
41
+ a_max,
42
+ a_min,
43
+ gate_max,
44
+ row_max,
45
+ row_sum,
46
+ n_block,
47
+ n_block_min,
48
+ IS_MASK: tl.constexpr,
49
+ MASK_CAUSAL: tl.constexpr,
50
+ MASK_LOCAL: tl.constexpr,
51
+ MASK_SINK: tl.constexpr,
52
+ CHECK_INF: tl.constexpr,
53
+ ):
54
+ skip_gate_next = True
55
+ if not skip_gate_curr:
56
+ if IS_MASK:
57
+ # Apply mask
58
+ acc_s = mask_sched.apply_mask(
59
+ acc_s=acc_s,
60
+ iter_block=n_block,
61
+ MASK_CAUSAL=MASK_CAUSAL,
62
+ MASK_LOCAL=MASK_LOCAL,
63
+ MASK_SINK=MASK_SINK,
64
+ )
65
+
66
+ # Apply online sparse softmax
67
+ p, row_max, row_sum, row_scale, skip_softmax = (
68
+ softmax_sched.online_sparse_softmax(
69
+ acc_s=acc_s,
70
+ row_max=row_max,
71
+ row_sum=row_sum,
72
+ softmax_threshold_log2=config.softmax_threshold_log2,
73
+ CHECK_INF=CHECK_INF,
74
+ )
75
+ )
76
+
77
+ if not skip_softmax:
78
+ # Load value tile
79
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
80
+
81
+ # Rescale output accumulator
82
+ acc_o = softmax_sched.rescale_o(
83
+ acc_o=acc_o,
84
+ row_scale=row_scale,
85
+ )
86
+
87
+ # Update output accumulator
88
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
89
+
90
+ if n_block > n_block_min:
91
+ # Load next delta tile
92
+ d_tile = ptrs_sched.load_d(config, d_ptrs, n_block - 1)
93
+ d_max = tl.max(d_tile)
94
+ d_min = tl.min(d_tile)
95
+
96
+ # Check if any gates are active for next tile
97
+ gate_max, skip_gate_next = softmax_sched.online_gate(
98
+ a_max=a_max,
99
+ a_min=a_min,
100
+ d_max=d_max,
101
+ d_min=d_min,
102
+ gate_max=gate_max,
103
+ gate_threshold_log2=config.gate_threshold_log2,
104
+ )
105
+
106
+ if not skip_gate_next:
107
+ # Compute attention gates for next tile
108
+ acc_s = a_tile[:, None] * d_tile[None, :]
109
+ if config.IS_LOGSIGMOID_GATE:
110
+ acc_s = softmax_sched.log_sigmoid(
111
+ acc_s=acc_s,
112
+ )
113
+
114
+ # Load next key tile
115
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
116
+
117
+ # Compute attention scores for next tile
118
+ acc_s += tl.dot(q_tile, k_tile.T)
119
+
120
+ return (
121
+ skip_gate_next,
122
+ acc_s,
123
+ acc_o,
124
+ gate_max,
125
+ row_max,
126
+ row_sum,
127
+ )
128
+
129
+
130
+ @triton.jit(repr=kernel_repr.fwd_gated_repr)
131
+ def _fwd_gated_kernel(
132
+ Q,
133
+ K,
134
+ V,
135
+ A,
136
+ D,
137
+ Out,
138
+ Lse,
139
+ softmax_scale,
140
+ softmax_threshold,
141
+ gate_threshold,
142
+ query_scale,
143
+ key_scale,
144
+ value_scale,
145
+ window_sizes,
146
+ stride_qb,
147
+ stride_qh,
148
+ stride_qm,
149
+ stride_kb,
150
+ stride_kh,
151
+ stride_kn,
152
+ stride_vb,
153
+ stride_vh,
154
+ stride_vn,
155
+ stride_ab,
156
+ stride_ah,
157
+ stride_am,
158
+ stride_db,
159
+ stride_dh,
160
+ stride_dn,
161
+ stride_ob,
162
+ stride_oh,
163
+ stride_om,
164
+ stride_os,
165
+ stride_lb,
166
+ stride_lh,
167
+ stride_ls,
168
+ stride_wh,
169
+ cu_seqlens_q,
170
+ cu_seqlens_k,
171
+ seqused_q,
172
+ seqused_k,
173
+ num_splits,
174
+ seqlen_q,
175
+ seqlen_k,
176
+ head_dim,
177
+ SEQLEN_Q_CACHE: tl.constexpr,
178
+ SEQLEN_K_CACHE: tl.constexpr,
179
+ QHEAD_PER_KVHEAD: tl.constexpr,
180
+ PACK_GQA: tl.constexpr,
181
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
182
+ TILE_M: tl.constexpr,
183
+ TILE_N: tl.constexpr,
184
+ TILE_K: tl.constexpr,
185
+ IS_CAUSAL: tl.constexpr,
186
+ IS_LOCAL: tl.constexpr,
187
+ IS_SPLIT_KV: tl.constexpr,
188
+ IS_LOGSIGMOID_GATE: tl.constexpr,
189
+ IS_ADAPT_GATE: tl.constexpr,
190
+ HAS_CU_SEQLENS_Q: tl.constexpr,
191
+ HAS_CU_SEQLENS_K: tl.constexpr,
192
+ HAS_SEQUSED_Q: tl.constexpr,
193
+ HAS_SEQUSED_K: tl.constexpr,
194
+ ):
195
+ # Create grid index
196
+ grid_idx = AttnFwdGridIndex.create(
197
+ num_splits=num_splits,
198
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
199
+ IS_SPLIT_KV=IS_SPLIT_KV,
200
+ PACK_GQA=PACK_GQA,
201
+ )
202
+
203
+ # Load window sizes
204
+ (
205
+ window_size_sink,
206
+ window_size_left,
207
+ window_size_right,
208
+ window_size_dist,
209
+ ) = grid_idx.load_window_sizes(
210
+ window_sizes=window_sizes,
211
+ stride_wh=stride_wh,
212
+ IS_LOCAL=IS_LOCAL,
213
+ )
214
+
215
+ # Create config
216
+ config = AttnFwdConfig.create(
217
+ softmax_scale=softmax_scale,
218
+ softmax_threshold=softmax_threshold,
219
+ gate_threshold=gate_threshold,
220
+ query_scale=query_scale,
221
+ key_scale=key_scale,
222
+ value_scale=value_scale,
223
+ m_block=grid_idx.m_block,
224
+ batch_idx=grid_idx.batch_idx,
225
+ window_size_sink=window_size_sink,
226
+ window_size_left=window_size_left,
227
+ window_size_right=window_size_right,
228
+ window_size_dist=window_size_dist,
229
+ head_dim=head_dim,
230
+ cu_seqlens_q=cu_seqlens_q,
231
+ cu_seqlens_k=cu_seqlens_k,
232
+ seqused_q=seqused_q,
233
+ seqused_k=seqused_k,
234
+ seqlen_q=seqlen_q,
235
+ seqlen_k=seqlen_k,
236
+ PACK_GQA=PACK_GQA,
237
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
238
+ TILE_M=TILE_M,
239
+ TILE_N=TILE_N,
240
+ TILE_K=TILE_K,
241
+ IS_CAUSAL=IS_CAUSAL,
242
+ IS_LOGSIGMOID_GATE=IS_LOGSIGMOID_GATE,
243
+ IS_ADAPT_GATE=IS_ADAPT_GATE,
244
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
245
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
246
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
247
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
248
+ )
249
+
250
+ # Create pointer scheduler
251
+ ptrs_sched = AttnFwdPointerScheduler.create(
252
+ config=config,
253
+ Q=Q,
254
+ K=K,
255
+ V=V,
256
+ A=A,
257
+ D=D,
258
+ Out=Out,
259
+ Lse=Lse,
260
+ batch_idx=grid_idx.batch_idx,
261
+ head_idx=grid_idx.head_idx,
262
+ head_kv_idx=grid_idx.head_kv_idx,
263
+ split_idx=grid_idx.split_idx,
264
+ stride_qb=stride_qb,
265
+ stride_qh=stride_qh,
266
+ stride_qm=stride_qm,
267
+ stride_kb=stride_kb,
268
+ stride_kh=stride_kh,
269
+ stride_kn=stride_kn,
270
+ stride_vb=stride_vb,
271
+ stride_vh=stride_vh,
272
+ stride_vn=stride_vn,
273
+ stride_ab=stride_ab,
274
+ stride_ah=stride_ah,
275
+ stride_am=stride_am,
276
+ stride_db=stride_db,
277
+ stride_dh=stride_dh,
278
+ stride_dn=stride_dn,
279
+ stride_ob=stride_ob,
280
+ stride_oh=stride_oh,
281
+ stride_om=stride_om,
282
+ stride_os=stride_os,
283
+ stride_lb=stride_lb,
284
+ stride_lh=stride_lh,
285
+ stride_ls=stride_ls,
286
+ IS_SPLIT_KV=IS_SPLIT_KV,
287
+ IS_GATED=True,
288
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
289
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
290
+ )
291
+
292
+ # Create block scheduler
293
+ block_sched = AttnFwdBlockScheduler.create(
294
+ config=config,
295
+ split_idx=grid_idx.split_idx,
296
+ num_splits=num_splits,
297
+ IS_CAUSAL=IS_CAUSAL,
298
+ IS_LOCAL=IS_LOCAL,
299
+ IS_SPLIT_KV=IS_SPLIT_KV,
300
+ )
301
+
302
+ # Create mask scheduler
303
+ mask_sched = AttnMaskScheduler.create(config)
304
+
305
+ # Create softmax scheduler
306
+ softmax_sched = SoftmaxScheduler.create(config)
307
+
308
+ # Create pointers
309
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
310
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
311
+
312
+ # Early exit if no n_blocks to process
313
+ if block_sched.is_empty():
314
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
315
+ return
316
+
317
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
318
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
319
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
320
+ a_ptrs = ptrs_sched.make_a_ptrs(config)
321
+ d_ptrs = ptrs_sched.make_d_ptrs(config)
322
+
323
+ # Initialize accumulators
324
+ gate_max = tl.full((), float("-inf"), dtype=tl.float32)
325
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
326
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
327
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
328
+
329
+ # Load query tile
330
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
331
+
332
+ # Load alpha tile
333
+ a_tile = ptrs_sched.load_a(config, a_ptrs)
334
+ a_max = tl.max(a_tile)
335
+ a_min = tl.min(a_tile)
336
+
337
+ # Load key tile
338
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
339
+
340
+ # Load delta tile
341
+ d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_max - 1)
342
+ d_max = tl.max(d_tile)
343
+ d_min = tl.min(d_tile)
344
+
345
+ # Check if any gates are active for current tile
346
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
347
+ a_max=a_max,
348
+ a_min=a_min,
349
+ d_max=d_max,
350
+ d_min=d_min,
351
+ gate_max=gate_max,
352
+ gate_threshold_log2=config.gate_threshold_log2,
353
+ )
354
+
355
+ # Initialize skip_gate_curr to False for the first iteration
356
+ skip_gate_curr = False
357
+
358
+ # Compute attention gates for first tile
359
+ acc_s = a_tile[:, None] * d_tile[None, :]
360
+ if IS_LOGSIGMOID_GATE:
361
+ acc_s = softmax_sched.log_sigmoid(
362
+ acc_s=acc_s,
363
+ )
364
+
365
+ # Compute attention scores for first tile
366
+ acc_s += tl.dot(q_tile, k_tile.T)
367
+
368
+ # Process n_blocks with causal masking
369
+ if IS_CAUSAL or IS_LOCAL:
370
+ n_block_max_no_mask = block_sched.n_block_max_no_mask
371
+ for n_block in tl.range(
372
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
373
+ ):
374
+ (
375
+ skip_gate_curr,
376
+ acc_s,
377
+ acc_o,
378
+ gate_max,
379
+ row_max,
380
+ row_sum,
381
+ ) = _fwd_inner_gated_kernel(
382
+ config=config,
383
+ ptrs_sched=ptrs_sched,
384
+ mask_sched=mask_sched,
385
+ softmax_sched=softmax_sched,
386
+ skip_gate_curr=skip_gate_curr,
387
+ acc_s=acc_s,
388
+ acc_o=acc_o,
389
+ q_tile=q_tile,
390
+ a_tile=a_tile,
391
+ k_ptrs=k_ptrs,
392
+ v_ptrs=v_ptrs,
393
+ d_ptrs=d_ptrs,
394
+ a_max=a_max,
395
+ a_min=a_min,
396
+ gate_max=gate_max,
397
+ row_max=row_max,
398
+ row_sum=row_sum,
399
+ n_block=n_block,
400
+ n_block_min=block_sched.n_block_max_no_mask,
401
+ IS_MASK=True,
402
+ MASK_CAUSAL=IS_CAUSAL,
403
+ MASK_LOCAL=True if IS_LOCAL else False,
404
+ MASK_SINK=False,
405
+ CHECK_INF=True,
406
+ )
407
+ else:
408
+ # First iteration with seqlen masking
409
+ n_block = block_sched.n_block_max - 1
410
+ # Triton aggregate does not support attribute reassignment yet
411
+ # block_sched.n_block_max_no_mask = n_block
412
+ n_block_max_no_mask = n_block
413
+
414
+ (
415
+ skip_gate_curr,
416
+ acc_s,
417
+ acc_o,
418
+ gate_max,
419
+ row_max,
420
+ row_sum,
421
+ ) = _fwd_inner_gated_kernel(
422
+ config=config,
423
+ ptrs_sched=ptrs_sched,
424
+ mask_sched=mask_sched,
425
+ softmax_sched=softmax_sched,
426
+ skip_gate_curr=skip_gate_curr,
427
+ acc_s=acc_s,
428
+ acc_o=acc_o,
429
+ q_tile=q_tile,
430
+ a_tile=a_tile,
431
+ k_ptrs=k_ptrs,
432
+ v_ptrs=v_ptrs,
433
+ d_ptrs=d_ptrs,
434
+ a_max=a_max,
435
+ a_min=a_min,
436
+ gate_max=gate_max,
437
+ row_max=row_max,
438
+ row_sum=row_sum,
439
+ n_block=n_block,
440
+ n_block_min=n_block,
441
+ IS_MASK=True,
442
+ MASK_CAUSAL=False,
443
+ MASK_LOCAL=False,
444
+ MASK_SINK=False,
445
+ CHECK_INF=True,
446
+ )
447
+
448
+ # Process n_blocks without masking
449
+ if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
450
+ # Load key tile
451
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
452
+
453
+ # Load delta tile
454
+ d_tile = ptrs_sched.load_d(config, d_ptrs, n_block_max_no_mask - 1)
455
+ d_max = tl.max(d_tile)
456
+ d_min = tl.min(d_tile)
457
+
458
+ # Check if any gates are active for current tile
459
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
460
+ a_max=a_max,
461
+ a_min=a_min,
462
+ d_max=d_max,
463
+ d_min=d_min,
464
+ gate_max=gate_max,
465
+ gate_threshold_log2=config.gate_threshold_log2,
466
+ )
467
+
468
+ # Compute attention gates
469
+ acc_s = a_tile[:, None] * d_tile[None, :]
470
+ if IS_LOGSIGMOID_GATE:
471
+ acc_s = softmax_sched.log_sigmoid(
472
+ acc_s=acc_s,
473
+ )
474
+
475
+ # Compute attention scores
476
+ acc_s += tl.dot(q_tile, k_tile.T)
477
+
478
+ for n_block in tl.range(
479
+ n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
480
+ ):
481
+ (
482
+ skip_gate_curr,
483
+ acc_s,
484
+ acc_o,
485
+ gate_max,
486
+ row_max,
487
+ row_sum,
488
+ ) = _fwd_inner_gated_kernel(
489
+ config=config,
490
+ ptrs_sched=ptrs_sched,
491
+ mask_sched=mask_sched,
492
+ softmax_sched=softmax_sched,
493
+ skip_gate_curr=skip_gate_curr,
494
+ acc_s=acc_s,
495
+ acc_o=acc_o,
496
+ q_tile=q_tile,
497
+ a_tile=a_tile,
498
+ k_ptrs=k_ptrs,
499
+ v_ptrs=v_ptrs,
500
+ d_ptrs=d_ptrs,
501
+ a_max=a_max,
502
+ a_min=a_min,
503
+ gate_max=gate_max,
504
+ row_max=row_max,
505
+ row_sum=row_sum,
506
+ n_block=n_block,
507
+ n_block_min=block_sched.n_block_min,
508
+ IS_MASK=False,
509
+ MASK_CAUSAL=False,
510
+ MASK_LOCAL=False,
511
+ MASK_SINK=False,
512
+ CHECK_INF=False,
513
+ )
514
+
515
+ if IS_LOCAL:
516
+ # Process n_blocks with local right masking
517
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
518
+ # Load key tile
519
+ k_tile = ptrs_sched.load_k(
520
+ config, k_ptrs, block_sched.n_block_window_max - 1
521
+ )
522
+
523
+ # Load delta tile
524
+ d_tile = ptrs_sched.load_d(
525
+ config, d_ptrs, block_sched.n_block_window_max - 1
526
+ )
527
+ d_max = tl.max(d_tile)
528
+ d_min = tl.min(d_tile)
529
+
530
+ # Check if any gates are active for current tile
531
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
532
+ a_max=a_max,
533
+ a_min=a_min,
534
+ d_max=d_max,
535
+ d_min=d_min,
536
+ gate_max=gate_max,
537
+ gate_threshold_log2=config.gate_threshold_log2,
538
+ )
539
+
540
+ # Compute attention gates
541
+ acc_s = a_tile[:, None] * d_tile[None, :]
542
+ if IS_LOGSIGMOID_GATE:
543
+ acc_s = softmax_sched.log_sigmoid(
544
+ acc_s=acc_s,
545
+ )
546
+
547
+ # Compute attention scores
548
+ acc_s += tl.dot(q_tile, k_tile.T)
549
+
550
+ for n_block in tl.range(
551
+ block_sched.n_block_window_max - 1,
552
+ block_sched.n_block_window_max_no_mask - 1,
553
+ -1,
554
+ ):
555
+ (
556
+ skip_gate_curr,
557
+ acc_s,
558
+ acc_o,
559
+ gate_max,
560
+ row_max,
561
+ row_sum,
562
+ ) = _fwd_inner_gated_kernel(
563
+ config=config,
564
+ ptrs_sched=ptrs_sched,
565
+ mask_sched=mask_sched,
566
+ softmax_sched=softmax_sched,
567
+ skip_gate_curr=skip_gate_curr,
568
+ acc_s=acc_s,
569
+ acc_o=acc_o,
570
+ q_tile=q_tile,
571
+ a_tile=a_tile,
572
+ k_ptrs=k_ptrs,
573
+ v_ptrs=v_ptrs,
574
+ d_ptrs=d_ptrs,
575
+ a_max=a_max,
576
+ a_min=a_min,
577
+ gate_max=gate_max,
578
+ row_max=row_max,
579
+ row_sum=row_sum,
580
+ n_block=n_block,
581
+ n_block_min=block_sched.n_block_window_max_no_mask,
582
+ IS_MASK=True,
583
+ MASK_CAUSAL=False,
584
+ MASK_LOCAL=True,
585
+ MASK_SINK=False,
586
+ CHECK_INF=True,
587
+ )
588
+
589
+ # Process n_blocks without masking
590
+ if (
591
+ block_sched.n_block_window_max_no_mask
592
+ > block_sched.n_block_window_min_no_mask
593
+ ):
594
+ # Load key tile
595
+ k_tile = ptrs_sched.load_k(
596
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
597
+ )
598
+
599
+ # Load delta tile
600
+ d_tile = ptrs_sched.load_d(
601
+ config, d_ptrs, block_sched.n_block_window_max_no_mask - 1
602
+ )
603
+ d_max = tl.max(d_tile)
604
+ d_min = tl.min(d_tile)
605
+
606
+ # Check if any gates are active for current tile
607
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
608
+ a_max=a_max,
609
+ a_min=a_min,
610
+ d_max=d_max,
611
+ d_min=d_min,
612
+ gate_max=gate_max,
613
+ gate_threshold_log2=config.gate_threshold_log2,
614
+ )
615
+
616
+ # Compute attention gates
617
+ acc_s = a_tile[:, None] * d_tile[None, :]
618
+ if IS_LOGSIGMOID_GATE:
619
+ acc_s = softmax_sched.log_sigmoid(
620
+ acc_s=acc_s,
621
+ )
622
+
623
+ # Compute attention scores
624
+ acc_s += tl.dot(q_tile, k_tile.T)
625
+
626
+ for n_block in tl.range(
627
+ block_sched.n_block_window_max_no_mask - 1,
628
+ block_sched.n_block_window_min_no_mask - 1,
629
+ -1,
630
+ ):
631
+ (
632
+ skip_gate_curr,
633
+ acc_s,
634
+ acc_o,
635
+ gate_max,
636
+ row_max,
637
+ row_sum,
638
+ ) = _fwd_inner_gated_kernel(
639
+ config=config,
640
+ ptrs_sched=ptrs_sched,
641
+ mask_sched=mask_sched,
642
+ softmax_sched=softmax_sched,
643
+ skip_gate_curr=skip_gate_curr,
644
+ acc_s=acc_s,
645
+ acc_o=acc_o,
646
+ q_tile=q_tile,
647
+ a_tile=a_tile,
648
+ k_ptrs=k_ptrs,
649
+ v_ptrs=v_ptrs,
650
+ d_ptrs=d_ptrs,
651
+ a_max=a_max,
652
+ a_min=a_min,
653
+ gate_max=gate_max,
654
+ row_max=row_max,
655
+ row_sum=row_sum,
656
+ n_block=n_block,
657
+ n_block_min=block_sched.n_block_window_min_no_mask,
658
+ IS_MASK=False,
659
+ MASK_CAUSAL=False,
660
+ MASK_LOCAL=False,
661
+ MASK_SINK=False,
662
+ CHECK_INF=False,
663
+ )
664
+
665
+ # Process n_blocks with local left masking
666
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
667
+ # Load key tile
668
+ k_tile = ptrs_sched.load_k(
669
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
670
+ )
671
+
672
+ # Load delta tile
673
+ d_tile = ptrs_sched.load_d(
674
+ config, d_ptrs, block_sched.n_block_window_min_no_mask - 1
675
+ )
676
+ d_max = tl.max(d_tile)
677
+ d_min = tl.min(d_tile)
678
+
679
+ # Check if any gates are active for current tile
680
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
681
+ a_max=a_max,
682
+ a_min=a_min,
683
+ d_max=d_max,
684
+ d_min=d_min,
685
+ gate_max=gate_max,
686
+ gate_threshold_log2=config.gate_threshold_log2,
687
+ )
688
+
689
+ # Compute attention gates
690
+ acc_s = a_tile[:, None] * d_tile[None, :]
691
+ if IS_LOGSIGMOID_GATE:
692
+ acc_s = softmax_sched.log_sigmoid(
693
+ acc_s=acc_s,
694
+ )
695
+
696
+ # Compute attention scores
697
+ acc_s += tl.dot(q_tile, k_tile.T)
698
+
699
+ for n_block in tl.range(
700
+ block_sched.n_block_window_min_no_mask - 1,
701
+ block_sched.n_block_window_min - 1,
702
+ -1,
703
+ ):
704
+ (
705
+ skip_gate_curr,
706
+ acc_s,
707
+ acc_o,
708
+ gate_max,
709
+ row_max,
710
+ row_sum,
711
+ ) = _fwd_inner_gated_kernel(
712
+ config=config,
713
+ ptrs_sched=ptrs_sched,
714
+ mask_sched=mask_sched,
715
+ softmax_sched=softmax_sched,
716
+ skip_gate_curr=skip_gate_curr,
717
+ acc_s=acc_s,
718
+ acc_o=acc_o,
719
+ q_tile=q_tile,
720
+ a_tile=a_tile,
721
+ k_ptrs=k_ptrs,
722
+ v_ptrs=v_ptrs,
723
+ d_ptrs=d_ptrs,
724
+ a_max=a_max,
725
+ a_min=a_min,
726
+ gate_max=gate_max,
727
+ row_max=row_max,
728
+ row_sum=row_sum,
729
+ n_block=n_block,
730
+ n_block_min=block_sched.n_block_window_min,
731
+ IS_MASK=True,
732
+ MASK_CAUSAL=False,
733
+ MASK_LOCAL=True,
734
+ MASK_SINK=False,
735
+ CHECK_INF=True,
736
+ )
737
+
738
+ # Process n_blocks with local sink masking
739
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
740
+ # Load key tile
741
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
742
+
743
+ # Load delta tile
744
+ d_tile = ptrs_sched.load_d(config, d_ptrs, block_sched.n_block_sink_max - 1)
745
+ d_max = tl.max(d_tile)
746
+ d_min = tl.min(d_tile)
747
+
748
+ # Check if any gates are active for current tile
749
+ gate_max, skip_gate_curr = softmax_sched.online_gate(
750
+ a_max=a_max,
751
+ a_min=a_min,
752
+ d_max=d_max,
753
+ d_min=d_min,
754
+ gate_max=gate_max,
755
+ gate_threshold_log2=config.gate_threshold_log2,
756
+ )
757
+
758
+ # Compute attention gates
759
+ acc_s = a_tile[:, None] * d_tile[None, :]
760
+ if IS_LOGSIGMOID_GATE:
761
+ acc_s = softmax_sched.log_sigmoid(
762
+ acc_s=acc_s,
763
+ )
764
+
765
+ # Compute attention scores
766
+ acc_s += tl.dot(q_tile, k_tile.T)
767
+
768
+ for n_block in tl.range(
769
+ block_sched.n_block_sink_max - 1,
770
+ block_sched.n_block_sink_min - 1,
771
+ -1,
772
+ ):
773
+ (
774
+ skip_gate_curr,
775
+ acc_s,
776
+ acc_o,
777
+ gate_max,
778
+ row_max,
779
+ row_sum,
780
+ ) = _fwd_inner_gated_kernel(
781
+ config=config,
782
+ ptrs_sched=ptrs_sched,
783
+ mask_sched=mask_sched,
784
+ softmax_sched=softmax_sched,
785
+ skip_gate_curr=skip_gate_curr,
786
+ acc_s=acc_s,
787
+ acc_o=acc_o,
788
+ q_tile=q_tile,
789
+ a_tile=a_tile,
790
+ k_ptrs=k_ptrs,
791
+ v_ptrs=v_ptrs,
792
+ d_ptrs=d_ptrs,
793
+ a_max=a_max,
794
+ a_min=a_min,
795
+ gate_max=gate_max,
796
+ row_max=row_max,
797
+ row_sum=row_sum,
798
+ n_block=n_block,
799
+ n_block_min=block_sched.n_block_sink_min,
800
+ IS_MASK=True,
801
+ MASK_CAUSAL=False,
802
+ MASK_LOCAL=True,
803
+ MASK_SINK=True,
804
+ CHECK_INF=True,
805
+ )
806
+
807
+ # Finalize softmax
808
+ row_scale, lse_tile = softmax_sched.finalize(
809
+ row_max=row_max,
810
+ row_sum=row_sum,
811
+ IS_LOG2=IS_SPLIT_KV,
812
+ )
813
+
814
+ # Store LSE
815
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
816
+
817
+ # Finalize rescale
818
+ acc_o = softmax_sched.rescale_o(
819
+ acc_o=acc_o,
820
+ row_scale=row_scale,
821
+ )
822
+
823
+ # Store output
824
+ ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
825
+
826
+
827
+ _fwd_gated_kernel = cache_utils.wrap_kernel(_fwd_gated_kernel)
828
+
829
+
830
+ _fwd_gated_kernel_autotuned = None
831
+
832
+
833
+ def _get_autotuned_kernel():
834
+ global _fwd_gated_kernel_autotuned
835
+ if _fwd_gated_kernel_autotuned is None:
836
+ jit_kernel = _fwd_gated_kernel._kernel
837
+ autotuned = autotuner.make_fwd_gated_autotuned_kernel(jit_kernel)
838
+ _fwd_gated_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
839
+ return _fwd_gated_kernel_autotuned
840
+
841
+
842
+ def _flash_gated_attn_forward(
843
+ query: torch.Tensor,
844
+ key: torch.Tensor,
845
+ value: torch.Tensor,
846
+ alpha: torch.Tensor,
847
+ delta: torch.Tensor,
848
+ is_causal: bool = False,
849
+ softmax_scale: float = None,
850
+ query_scale: Optional[torch.Tensor] = None,
851
+ key_scale: Optional[torch.Tensor] = None,
852
+ value_scale: Optional[torch.Tensor] = None,
853
+ window_sizes: Optional[torch.Tensor] = None,
854
+ softmax_threshold: float = None,
855
+ gate_threshold: float = None,
856
+ is_logsigmoid_gate: bool = False,
857
+ is_adapt_gate: bool = True,
858
+ is_local: bool = False,
859
+ is_quant: bool = False,
860
+ is_split_kv: bool = False,
861
+ pack_gqa: bool = False,
862
+ out: Optional[torch.Tensor] = None,
863
+ lse: Optional[torch.Tensor] = None,
864
+ is_autotune: bool = False,
865
+ skip_checks: bool = False,
866
+ ) -> Tuple[torch.Tensor, torch.Tensor, float, float, float]:
867
+ device = query.device
868
+ num_SMs = cache_utils.get_device_num_sms(device)
869
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
870
+ _, seqlen_k, num_heads_kv, _ = key.shape
871
+ softmax_scale = (
872
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
873
+ )
874
+ softmax_threshold = (
875
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
876
+ )
877
+ gate_threshold = (
878
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
879
+ )
880
+ qhead_per_kvhead = num_heads_q // num_heads_kv
881
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
882
+ if is_local and window_sizes is None:
883
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
884
+ elif not is_local:
885
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
886
+
887
+ if not skip_checks:
888
+ assert_inputs.assert_fwd_inputs(
889
+ query,
890
+ key,
891
+ value,
892
+ query_scale=query_scale,
893
+ key_scale=key_scale,
894
+ value_scale=value_scale,
895
+ window_sizes=window_sizes,
896
+ alpha=alpha,
897
+ delta=delta,
898
+ cu_seqlens_q=None,
899
+ cu_seqlens_k=None,
900
+ seqused_q=None,
901
+ seqused_k=None,
902
+ num_heads_q=num_heads_q,
903
+ num_heads_kv=num_heads_kv,
904
+ head_dim=head_dim,
905
+ is_quant=is_quant,
906
+ device=device,
907
+ )
908
+
909
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
910
+
911
+ _kernel_name = f"fwd_gated{'_split' if is_split_kv else ''}"
912
+ launch_config = launch_template.load_launch_config(
913
+ device=device,
914
+ kernel_name=_kernel_name,
915
+ seqlen_q=seqlen_q,
916
+ seqlen_k=seqlen_k,
917
+ tile_k=TILE_K,
918
+ is_local=is_local,
919
+ qhead_per_kvhead=qhead_per_kvhead,
920
+ is_causal=is_causal,
921
+ pack_gqa=pack_gqa,
922
+ is_quant=is_quant,
923
+ )
924
+ if launch_config is not None and not is_autotune:
925
+ kernel = _fwd_gated_kernel
926
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
927
+ else:
928
+ kernel = _get_autotuned_kernel()
929
+ # Placeholder for pre-launch computations
930
+ TILE_M = TILE_N = 64
931
+ num_warps = num_stages = num_ctas = None
932
+
933
+ num_splits = (
934
+ utils.num_splits_heuristic(
935
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
936
+ seqlen_k=seqlen_k,
937
+ num_SMs=num_SMs,
938
+ TILE_M=TILE_M,
939
+ TILE_N=TILE_N,
940
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
941
+ window_sizes, TILE_N
942
+ )
943
+ if is_local
944
+ else None,
945
+ )
946
+ if is_split_kv
947
+ else 1
948
+ )
949
+
950
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
951
+ out = out if out is not None else torch.zeros_like(query, dtype=out_dtype)
952
+ lse = (
953
+ lse
954
+ if lse is not None
955
+ else torch.zeros(
956
+ (batch_size, num_heads_q, seqlen_q),
957
+ device=query.device,
958
+ dtype=torch.float32,
959
+ )
960
+ )
961
+
962
+ if is_split_kv:
963
+ out_partial = torch.empty(
964
+ (num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
965
+ dtype=torch.float32,
966
+ device=query.device,
967
+ )
968
+ lse_partial = torch.empty(
969
+ (num_splits, batch_size, num_heads_q, seqlen_q),
970
+ dtype=torch.float32,
971
+ device=query.device,
972
+ )
973
+
974
+ if not is_quant:
975
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
976
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
977
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
978
+
979
+ grid = launch_grid.get_fwd_grid(
980
+ batch_size=batch_size,
981
+ seqlen_q=seqlen_q,
982
+ num_heads_q=num_heads_q,
983
+ num_heads_kv=num_heads_kv,
984
+ pack_gqa=pack_gqa,
985
+ num_splits=num_splits,
986
+ )
987
+
988
+ triton.set_allocator(utils.alloc_fn)
989
+
990
+ kernel[grid](
991
+ query,
992
+ key,
993
+ value,
994
+ alpha,
995
+ delta,
996
+ out if not is_split_kv else out_partial,
997
+ lse if not is_split_kv else lse_partial,
998
+ softmax_scale,
999
+ softmax_threshold,
1000
+ gate_threshold,
1001
+ query_scale,
1002
+ key_scale,
1003
+ value_scale,
1004
+ window_sizes,
1005
+ query.stride(0),
1006
+ query.stride(-2),
1007
+ query.stride(-3),
1008
+ key.stride(0),
1009
+ key.stride(-2),
1010
+ key.stride(-3),
1011
+ value.stride(0),
1012
+ value.stride(-2),
1013
+ value.stride(-3),
1014
+ alpha.stride(0),
1015
+ alpha.stride(-2),
1016
+ alpha.stride(-1),
1017
+ delta.stride(0),
1018
+ delta.stride(-2),
1019
+ delta.stride(-1),
1020
+ out.stride(0) if not is_split_kv else out_partial.stride(1),
1021
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
1022
+ out.stride(-3) if not is_split_kv else out_partial.stride(-3),
1023
+ 0 if not is_split_kv else out_partial.stride(0),
1024
+ lse.stride(0) if not is_split_kv else lse_partial.stride(1),
1025
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
1026
+ 0 if not is_split_kv else lse_partial.stride(0),
1027
+ window_sizes.stride(0),
1028
+ None,
1029
+ None,
1030
+ None,
1031
+ None,
1032
+ num_splits,
1033
+ seqlen_q=seqlen_q,
1034
+ seqlen_k=seqlen_k,
1035
+ head_dim=head_dim,
1036
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
1037
+ SEQLEN_K_CACHE=seqlen_k // 1024,
1038
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
1039
+ PACK_GQA=pack_gqa,
1040
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
1041
+ TILE_M=TILE_M,
1042
+ TILE_N=TILE_N,
1043
+ TILE_K=TILE_K,
1044
+ IS_CAUSAL=is_causal,
1045
+ IS_LOCAL=is_local,
1046
+ IS_SPLIT_KV=is_split_kv,
1047
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
1048
+ IS_ADAPT_GATE=is_adapt_gate,
1049
+ HAS_CU_SEQLENS_Q=False,
1050
+ HAS_CU_SEQLENS_K=False,
1051
+ HAS_SEQUSED_Q=False,
1052
+ HAS_SEQUSED_K=False,
1053
+ num_warps=num_warps,
1054
+ num_stages=num_stages,
1055
+ num_ctas=num_ctas,
1056
+ )
1057
+
1058
+ if launch_config is None or is_autotune:
1059
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
1060
+ if best is not None:
1061
+ launch_template.store_launch_config(
1062
+ device=device,
1063
+ kernel_name=_kernel_name,
1064
+ seqlen_q=seqlen_q,
1065
+ seqlen_k=seqlen_k,
1066
+ tile_k=TILE_K,
1067
+ config=best,
1068
+ is_local=is_local,
1069
+ qhead_per_kvhead=qhead_per_kvhead,
1070
+ is_causal=is_causal,
1071
+ pack_gqa=pack_gqa,
1072
+ is_quant=is_quant,
1073
+ )
1074
+
1075
+ if is_split_kv:
1076
+ flash_fwd_combine._flash_attn_fwd_combine(
1077
+ out_partial,
1078
+ lse_partial,
1079
+ out,
1080
+ lse,
1081
+ )
1082
+
1083
+ return out, lse, softmax_scale, softmax_threshold, gate_threshold
1084
+
1085
+
1086
+ def _flash_gated_attn_varlen_forward(
1087
+ query: torch.Tensor,
1088
+ key: torch.Tensor,
1089
+ value: torch.Tensor,
1090
+ alpha: torch.Tensor,
1091
+ delta: torch.Tensor,
1092
+ cu_seqlens_q: torch.Tensor,
1093
+ cu_seqlens_k: torch.Tensor,
1094
+ max_seqlen_q: int,
1095
+ max_seqlen_k: int,
1096
+ is_causal: bool = False,
1097
+ softmax_scale: float = None,
1098
+ query_scale: Optional[torch.Tensor] = None,
1099
+ key_scale: Optional[torch.Tensor] = None,
1100
+ value_scale: Optional[torch.Tensor] = None,
1101
+ window_sizes: Optional[torch.Tensor] = None,
1102
+ softmax_threshold: float = None,
1103
+ gate_threshold: float = None,
1104
+ is_logsigmoid_gate: bool = True,
1105
+ is_adapt_gate: bool = True,
1106
+ is_local: bool = False,
1107
+ is_quant: bool = False,
1108
+ is_split_kv: bool = False,
1109
+ pack_gqa: bool = False,
1110
+ seqused_q: Optional[torch.Tensor] = None,
1111
+ seqused_k: Optional[torch.Tensor] = None,
1112
+ out: Optional[torch.Tensor] = None,
1113
+ lse: Optional[torch.Tensor] = None,
1114
+ is_autotune: bool = False,
1115
+ skip_checks: bool = False,
1116
+ ) -> Tuple[torch.Tensor, torch.Tensor, float, float, float]:
1117
+ device = query.device
1118
+ num_SMs = cache_utils.get_device_num_sms(device)
1119
+ total_seqlen_q, num_heads_q, head_dim = query.shape
1120
+ _, num_heads_kv, _ = key.shape
1121
+ batch_size = cu_seqlens_q.shape[0] - 1
1122
+ seqlen_q = max_seqlen_q
1123
+ seqlen_k = max_seqlen_k
1124
+ softmax_scale = (
1125
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
1126
+ )
1127
+ softmax_threshold = (
1128
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
1129
+ )
1130
+ gate_threshold = (
1131
+ gate_threshold if gate_threshold is not None else head_dim / seqlen_k
1132
+ )
1133
+ qhead_per_kvhead = num_heads_q // num_heads_kv
1134
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
1135
+ if is_local and window_sizes is None:
1136
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
1137
+ elif not is_local:
1138
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
1139
+
1140
+ if not skip_checks:
1141
+ assert_inputs.assert_fwd_inputs(
1142
+ query,
1143
+ key,
1144
+ value,
1145
+ query_scale=query_scale,
1146
+ key_scale=key_scale,
1147
+ value_scale=value_scale,
1148
+ window_sizes=window_sizes,
1149
+ alpha=alpha,
1150
+ delta=delta,
1151
+ cu_seqlens_q=cu_seqlens_q,
1152
+ cu_seqlens_k=cu_seqlens_k,
1153
+ seqused_q=seqused_q,
1154
+ seqused_k=seqused_k,
1155
+ num_heads_q=num_heads_q,
1156
+ num_heads_kv=num_heads_kv,
1157
+ head_dim=head_dim,
1158
+ is_quant=is_quant,
1159
+ device=device,
1160
+ )
1161
+
1162
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
1163
+
1164
+ _kernel_name = f"fwd_gated{'_split' if is_split_kv else ''}"
1165
+ launch_config = launch_template.load_launch_config(
1166
+ device=device,
1167
+ kernel_name=_kernel_name,
1168
+ seqlen_q=seqlen_q,
1169
+ seqlen_k=seqlen_k,
1170
+ tile_k=TILE_K,
1171
+ is_local=is_local,
1172
+ qhead_per_kvhead=qhead_per_kvhead,
1173
+ is_causal=is_causal,
1174
+ pack_gqa=pack_gqa,
1175
+ is_quant=is_quant,
1176
+ )
1177
+ if launch_config is not None and not is_autotune:
1178
+ kernel = _fwd_gated_kernel
1179
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
1180
+ else:
1181
+ kernel = _get_autotuned_kernel()
1182
+ # Placeholder for pre-launch computations
1183
+ TILE_M = TILE_N = 64
1184
+ num_warps = num_stages = num_ctas = None
1185
+
1186
+ num_splits = (
1187
+ utils.num_splits_heuristic(
1188
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
1189
+ seqlen_k=seqlen_k,
1190
+ num_SMs=num_SMs,
1191
+ TILE_M=TILE_M,
1192
+ TILE_N=TILE_N,
1193
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
1194
+ window_sizes, TILE_N
1195
+ )
1196
+ if is_local
1197
+ else None,
1198
+ )
1199
+ if is_split_kv
1200
+ else 1
1201
+ )
1202
+
1203
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
1204
+ out = out if out is not None else torch.zeros_like(query, dtype=out_dtype)
1205
+ lse = (
1206
+ lse
1207
+ if lse is not None
1208
+ else torch.empty(
1209
+ (num_heads_q, total_seqlen_q),
1210
+ dtype=torch.float32,
1211
+ device=query.device,
1212
+ )
1213
+ )
1214
+
1215
+ if is_split_kv:
1216
+ out_partial = torch.empty(
1217
+ (num_splits, total_seqlen_q, num_heads_q, head_dim),
1218
+ dtype=torch.float32,
1219
+ device=query.device,
1220
+ )
1221
+ lse_partial = torch.empty(
1222
+ (num_splits, num_heads_q, total_seqlen_q),
1223
+ dtype=torch.float32,
1224
+ device=query.device,
1225
+ )
1226
+
1227
+ if not is_quant:
1228
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
1229
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
1230
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
1231
+
1232
+ grid = launch_grid.get_fwd_grid(
1233
+ batch_size=batch_size,
1234
+ seqlen_q=seqlen_q,
1235
+ num_heads_q=num_heads_q,
1236
+ num_heads_kv=num_heads_kv,
1237
+ pack_gqa=pack_gqa,
1238
+ num_splits=num_splits,
1239
+ )
1240
+
1241
+ triton.set_allocator(utils.alloc_fn)
1242
+
1243
+ kernel[grid](
1244
+ query,
1245
+ key,
1246
+ value,
1247
+ alpha,
1248
+ delta,
1249
+ out if not is_split_kv else out_partial,
1250
+ lse if not is_split_kv else lse_partial,
1251
+ softmax_scale,
1252
+ softmax_threshold,
1253
+ gate_threshold,
1254
+ query_scale,
1255
+ key_scale,
1256
+ value_scale,
1257
+ window_sizes,
1258
+ 0,
1259
+ query.stride(-2),
1260
+ query.stride(0),
1261
+ 0,
1262
+ key.stride(-2),
1263
+ key.stride(0),
1264
+ 0,
1265
+ value.stride(-2),
1266
+ value.stride(0),
1267
+ 0,
1268
+ alpha.stride(0),
1269
+ alpha.stride(-1),
1270
+ 0,
1271
+ delta.stride(0),
1272
+ delta.stride(-1),
1273
+ 0,
1274
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
1275
+ out.stride(0) if not is_split_kv else out_partial.stride(-3),
1276
+ 0 if not is_split_kv else out_partial.stride(0),
1277
+ 0,
1278
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
1279
+ 0 if not is_split_kv else lse_partial.stride(0),
1280
+ window_sizes.stride(0),
1281
+ cu_seqlens_q,
1282
+ cu_seqlens_k,
1283
+ seqused_q,
1284
+ seqused_k,
1285
+ num_splits,
1286
+ seqlen_q=seqlen_q,
1287
+ seqlen_k=seqlen_k,
1288
+ head_dim=head_dim,
1289
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
1290
+ SEQLEN_K_CACHE=seqlen_k // 1024,
1291
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
1292
+ PACK_GQA=pack_gqa,
1293
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
1294
+ TILE_M=TILE_M,
1295
+ TILE_N=TILE_N,
1296
+ TILE_K=TILE_K,
1297
+ IS_CAUSAL=is_causal,
1298
+ IS_LOCAL=is_local,
1299
+ IS_SPLIT_KV=is_split_kv,
1300
+ IS_LOGSIGMOID_GATE=is_logsigmoid_gate,
1301
+ IS_ADAPT_GATE=is_adapt_gate,
1302
+ HAS_CU_SEQLENS_Q=True,
1303
+ HAS_CU_SEQLENS_K=True,
1304
+ HAS_SEQUSED_Q=seqused_q is not None,
1305
+ HAS_SEQUSED_K=seqused_k is not None,
1306
+ num_warps=num_warps,
1307
+ num_stages=num_stages,
1308
+ num_ctas=num_ctas,
1309
+ )
1310
+
1311
+ if launch_config is None or is_autotune:
1312
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
1313
+ if best is not None:
1314
+ launch_template.store_launch_config(
1315
+ device=device,
1316
+ kernel_name=_kernel_name,
1317
+ seqlen_q=seqlen_q,
1318
+ seqlen_k=seqlen_k,
1319
+ tile_k=TILE_K,
1320
+ config=best,
1321
+ is_local=is_local,
1322
+ qhead_per_kvhead=qhead_per_kvhead,
1323
+ is_causal=is_causal,
1324
+ pack_gqa=pack_gqa,
1325
+ is_quant=is_quant,
1326
+ )
1327
+
1328
+ if is_split_kv:
1329
+ flash_fwd_combine._flash_attn_fwd_combine(
1330
+ out_partial,
1331
+ lse_partial,
1332
+ out,
1333
+ lse,
1334
+ cu_seqlens_q=cu_seqlens_q,
1335
+ )
1336
+
1337
+ return out, lse, softmax_scale, softmax_threshold, gate_threshold
build/torch-cuda/flash_proj_bwd.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from triton_kernels.matmul_ogs import matmul_ogs, PrecisionConfig
3
+
4
+
5
+ def _flash_proj_backward(
6
+ dout: torch.Tensor,
7
+ input: torch.Tensor,
8
+ weight: torch.Tensor,
9
+ ) -> tuple[torch.Tensor, torch.Tensor]:
10
+ batch_size, seqlen, out_features = dout.shape
11
+ in_features = input.shape[-1]
12
+
13
+ dy = dout.view(batch_size * seqlen, out_features)
14
+ x = input.view(batch_size * seqlen, in_features)
15
+
16
+ precision_config = PrecisionConfig(out_dtype=dy.dtype)
17
+
18
+ dx = matmul_ogs(dy, weight, None, precision_config=precision_config)
19
+ dw = matmul_ogs(dy.T, x, None, precision_config=precision_config)
20
+
21
+ return dx.view(batch_size, seqlen, in_features), dw
build/torch-cuda/flash_proj_fwd.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from triton_kernels.matmul_ogs import matmul_ogs, PrecisionConfig, FlexCtx
3
+ from triton_kernels.numerics import InFlexData, OutFlexData
4
+
5
+
6
+ def _flash_proj_forward(
7
+ input: torch.Tensor,
8
+ weight: torch.Tensor,
9
+ out_dtype: torch.dtype = torch.float8_e5m2,
10
+ prev_scale: torch.Tensor | None = None,
11
+ ) -> tuple[torch.Tensor, torch.Tensor]:
12
+ batch_size, seqlen, in_features = input.shape
13
+ out_features = weight.shape[0]
14
+ device = input.device
15
+
16
+ x = input.view(batch_size * seqlen, in_features)
17
+
18
+ if prev_scale is None:
19
+ expected_scale = torch.tensor([1.0], dtype=torch.float32, device=device)
20
+ else:
21
+ expected_scale = prev_scale.view(1).float()
22
+
23
+ actual_scale = torch.zeros(1, dtype=torch.float32, device=device)
24
+
25
+ out_flex = OutFlexData(
26
+ dtype=out_dtype,
27
+ expected_scale=expected_scale,
28
+ actual_scale=actual_scale,
29
+ checksum_scale=None,
30
+ )
31
+
32
+ precision_config = PrecisionConfig(
33
+ flex_ctx=FlexCtx(
34
+ lhs_data=InFlexData(),
35
+ rhs_data=InFlexData(),
36
+ out_data=out_flex,
37
+ ),
38
+ out_dtype=out_dtype,
39
+ )
40
+
41
+ out = matmul_ogs(x, weight.T, None, precision_config=precision_config)
42
+
43
+ descale = precision_config.flex_ctx.out_data.actual_scale.clone()
44
+
45
+ return out.view(batch_size, seqlen, out_features), descale
build/torch-cuda/flash_sparse_attention/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-cuda/flash_sparse_bwd.py ADDED
@@ -0,0 +1,1041 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple
2
+
3
+ import math
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+
8
+ from . import (
9
+ assert_inputs,
10
+ utils,
11
+ cache_utils,
12
+ launch_template,
13
+ launch_grid,
14
+ activations,
15
+ flash_bwd_preprocess,
16
+ flash_bwd_postprocess,
17
+ kernel_repr,
18
+ autotuner,
19
+ )
20
+ from .scheduler import (
21
+ AttnBwdGridIndex,
22
+ AttnBwdConfig,
23
+ AttnBwdBlockScheduler,
24
+ AttnBwdPointerScheduler,
25
+ AttnMaskScheduler,
26
+ )
27
+
28
+
29
+ @triton.jit
30
+ def _bwd_inner_sparse_kernel(
31
+ config,
32
+ ptrs_sched,
33
+ mask_sched,
34
+ acc_dk,
35
+ acc_dv,
36
+ k_tile,
37
+ v_tile,
38
+ q_ptrs,
39
+ do_ptrs,
40
+ dq_accum_ptrs,
41
+ lse_ptrs,
42
+ dpsum_ptrs,
43
+ m_block,
44
+ IS_MASK: tl.constexpr,
45
+ MASK_CAUSAL: tl.constexpr,
46
+ MASK_LOCAL: tl.constexpr,
47
+ MASK_SINK: tl.constexpr,
48
+ ):
49
+ # Load query tile
50
+ q_tile = ptrs_sched.load_q(config, q_ptrs, m_block)
51
+
52
+ # Compute attention scores
53
+ acc_s = tl.dot(k_tile, q_tile.T)
54
+
55
+ if IS_MASK:
56
+ # Apply mask to attention scores
57
+ acc_s = mask_sched.apply_mask(
58
+ acc_s=acc_s,
59
+ iter_block=m_block,
60
+ MASK_CAUSAL=MASK_CAUSAL,
61
+ MASK_LOCAL=MASK_LOCAL,
62
+ MASK_SINK=MASK_SINK,
63
+ )
64
+
65
+ # Load LSE
66
+ lse_log2 = ptrs_sched.load_lse(config, lse_ptrs, m_block)
67
+
68
+ # Compute attention weights in log2-domain
69
+ p_log2 = acc_s * config.softmax_scale_log2 - lse_log2[None, :]
70
+
71
+ # Compute softmax threshold for this m_block
72
+ softmax_threshold_log2 = config.get_softmax_threshold_log2(
73
+ m_block=m_block,
74
+ )
75
+
76
+ # Update skip condition based on threshold
77
+ skip_softmax = tl.max(p_log2 - softmax_threshold_log2[None, :]) < 0.0
78
+
79
+ if not skip_softmax:
80
+ # Compute attention weights
81
+ p = activations.exp2(p_log2).to(q_tile.dtype)
82
+
83
+ # Load output gradients tile
84
+ do_tile = ptrs_sched.load_do(config, do_ptrs, m_block)
85
+
86
+ # Compute value gradients
87
+ acc_dv += tl.dot(p, do_tile)
88
+
89
+ # Compute attention weight gradients
90
+ acc_dp = tl.dot(v_tile, tl.trans(do_tile))
91
+
92
+ # Load dpsum
93
+ dpsum = ptrs_sched.load_dpsum(config, dpsum_ptrs, m_block)
94
+
95
+ # Compute attention score gradients
96
+ ds = p * (acc_dp - dpsum[None, :]).to(q_tile.dtype)
97
+
98
+ # Compute query gradients
99
+ dq = tl.dot(tl.trans(ds), k_tile)
100
+
101
+ # Store query gradients
102
+ ptrs_sched.store_dq(config, dq_accum_ptrs, m_block, dq)
103
+
104
+ # Compute key gradients
105
+ acc_dk += tl.dot(ds, q_tile)
106
+
107
+ return acc_dk, acc_dv
108
+
109
+
110
+ @triton.jit(repr=kernel_repr.bwd_sparse_repr)
111
+ def _bwd_sparse_kernel(
112
+ Q,
113
+ K,
114
+ V,
115
+ dO,
116
+ LSELog2,
117
+ dPsum,
118
+ dQaccum,
119
+ dK,
120
+ dV,
121
+ softmax_scale,
122
+ query_scale,
123
+ key_scale,
124
+ value_scale,
125
+ softmax_threshold,
126
+ window_sizes,
127
+ stride_qb,
128
+ stride_qh,
129
+ stride_qm,
130
+ stride_kb,
131
+ stride_kh,
132
+ stride_kn,
133
+ stride_vb,
134
+ stride_vh,
135
+ stride_vn,
136
+ stride_dob,
137
+ stride_doh,
138
+ stride_dom,
139
+ stride_lb,
140
+ stride_lh,
141
+ stride_pb,
142
+ stride_ph,
143
+ stride_dqab,
144
+ stride_dqah,
145
+ stride_dqam,
146
+ stride_dkb,
147
+ stride_dkh,
148
+ stride_dkn,
149
+ stride_dks,
150
+ stride_dvb,
151
+ stride_dvh,
152
+ stride_dvn,
153
+ stride_dvs,
154
+ stride_wh,
155
+ cu_seqlens_q,
156
+ cu_seqlens_k,
157
+ seqused_q,
158
+ seqused_k,
159
+ num_splits,
160
+ seqlen_q,
161
+ seqlen_k,
162
+ head_dim,
163
+ SEQLEN_Q_CACHE: tl.constexpr,
164
+ SEQLEN_K_CACHE: tl.constexpr,
165
+ QHEAD_PER_KVHEAD: tl.constexpr,
166
+ TILE_M: tl.constexpr,
167
+ TILE_N: tl.constexpr,
168
+ TILE_K: tl.constexpr,
169
+ IS_CAUSAL: tl.constexpr,
170
+ IS_LOCAL: tl.constexpr,
171
+ IS_SPLIT_QO: tl.constexpr,
172
+ HAS_CU_SEQLENS_Q: tl.constexpr,
173
+ HAS_CU_SEQLENS_K: tl.constexpr,
174
+ HAS_SEQUSED_Q: tl.constexpr,
175
+ HAS_SEQUSED_K: tl.constexpr,
176
+ ):
177
+ # Create grid index
178
+ grid_idx = AttnBwdGridIndex.create(
179
+ num_splits=num_splits,
180
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
181
+ IS_SPLIT_QO=IS_SPLIT_QO,
182
+ )
183
+
184
+ # Load window sizes
185
+ (
186
+ window_size_sink,
187
+ window_size_left,
188
+ window_size_right,
189
+ window_size_dist,
190
+ ) = grid_idx.load_window_sizes(
191
+ window_sizes=window_sizes,
192
+ stride_wh=stride_wh,
193
+ IS_LOCAL=IS_LOCAL,
194
+ )
195
+
196
+ # Create config
197
+ config = AttnBwdConfig.create(
198
+ softmax_scale=softmax_scale,
199
+ softmax_threshold=softmax_threshold,
200
+ query_scale=query_scale,
201
+ key_scale=key_scale,
202
+ value_scale=value_scale,
203
+ n_block=grid_idx.n_block,
204
+ batch_idx=grid_idx.batch_idx,
205
+ window_size_sink=window_size_sink,
206
+ window_size_left=window_size_left,
207
+ window_size_right=window_size_right,
208
+ window_size_dist=window_size_dist,
209
+ head_dim=head_dim,
210
+ cu_seqlens_q=cu_seqlens_q,
211
+ cu_seqlens_k=cu_seqlens_k,
212
+ seqused_q=seqused_q,
213
+ seqused_k=seqused_k,
214
+ seqlen_q=seqlen_q,
215
+ seqlen_k=seqlen_k,
216
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
217
+ TILE_M=TILE_M,
218
+ TILE_N=TILE_N,
219
+ TILE_K=TILE_K,
220
+ IS_CAUSAL=IS_CAUSAL,
221
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
222
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
223
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
224
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
225
+ )
226
+
227
+ # Create pointer scheduler
228
+ ptrs_sched = AttnBwdPointerScheduler.create(
229
+ config=config,
230
+ Q=Q,
231
+ K=K,
232
+ V=V,
233
+ dO=dO,
234
+ LSELog2=LSELog2,
235
+ dPsum=dPsum,
236
+ dQaccum=dQaccum,
237
+ dK=dK,
238
+ dV=dV,
239
+ batch_idx=grid_idx.batch_idx,
240
+ head_idx=grid_idx.head_idx,
241
+ head_kv_idx=grid_idx.head_kv_idx,
242
+ split_idx=grid_idx.split_idx,
243
+ stride_qb=stride_qb,
244
+ stride_qh=stride_qh,
245
+ stride_qm=stride_qm,
246
+ stride_kb=stride_kb,
247
+ stride_kh=stride_kh,
248
+ stride_kn=stride_kn,
249
+ stride_vb=stride_vb,
250
+ stride_vh=stride_vh,
251
+ stride_vn=stride_vn,
252
+ stride_dob=stride_dob,
253
+ stride_doh=stride_doh,
254
+ stride_dom=stride_dom,
255
+ stride_lb=stride_lb,
256
+ stride_lh=stride_lh,
257
+ stride_pb=stride_pb,
258
+ stride_ph=stride_ph,
259
+ stride_dqab=stride_dqab,
260
+ stride_dqah=stride_dqah,
261
+ stride_dqam=stride_dqam,
262
+ stride_dkb=stride_dkb,
263
+ stride_dkh=stride_dkh,
264
+ stride_dkn=stride_dkn,
265
+ stride_dks=stride_dks,
266
+ stride_dvb=stride_dvb,
267
+ stride_dvh=stride_dvh,
268
+ stride_dvn=stride_dvn,
269
+ stride_dvs=stride_dvs,
270
+ IS_SPLIT_QO=IS_SPLIT_QO,
271
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
272
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
273
+ )
274
+
275
+ # Create block scheduler
276
+ block_sched = AttnBwdBlockScheduler.create(
277
+ config=config,
278
+ split_idx=grid_idx.split_idx,
279
+ num_splits=num_splits,
280
+ IS_CAUSAL=IS_CAUSAL,
281
+ IS_LOCAL=IS_LOCAL,
282
+ IS_SPLIT_QO=IS_SPLIT_QO,
283
+ )
284
+
285
+ # Create mask scheduler
286
+ mask_sched = AttnMaskScheduler.create(config, SWAP_AB=True)
287
+
288
+ # Early exit if no n_blocks to process
289
+ if grid_idx.n_block * TILE_N >= config.actual_seqlen_k:
290
+ return
291
+
292
+ # Create pointers
293
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
294
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
295
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
296
+ do_ptrs = ptrs_sched.make_do_ptrs(config)
297
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
298
+ dpsum_ptrs = ptrs_sched.make_dpsum_ptrs(config)
299
+ dq_accum_ptrs = ptrs_sched.make_dq_accum_ptrs(config)
300
+ dk_ptrs = ptrs_sched.make_dk_ptrs(config)
301
+ dv_ptrs = ptrs_sched.make_dv_ptrs(config)
302
+
303
+ # Initialize accumulators
304
+ acc_dk = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
305
+ acc_dv = tl.zeros((TILE_N, TILE_K), dtype=tl.float32)
306
+
307
+ # Load key tile
308
+ k_tile = ptrs_sched.load_k(config, k_ptrs)
309
+
310
+ # Load value tile
311
+ v_tile = ptrs_sched.load_v(config, v_ptrs)
312
+
313
+ # Process m_blocks with causal masking
314
+ if IS_CAUSAL or IS_LOCAL:
315
+ for m_block in tl.range(
316
+ block_sched.m_block_min, block_sched.m_block_min_no_mask
317
+ ):
318
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
319
+ config=config,
320
+ ptrs_sched=ptrs_sched,
321
+ mask_sched=mask_sched,
322
+ acc_dk=acc_dk,
323
+ acc_dv=acc_dv,
324
+ k_tile=k_tile,
325
+ v_tile=v_tile,
326
+ q_ptrs=q_ptrs,
327
+ do_ptrs=do_ptrs,
328
+ dq_accum_ptrs=dq_accum_ptrs,
329
+ lse_ptrs=lse_ptrs,
330
+ dpsum_ptrs=dpsum_ptrs,
331
+ m_block=m_block,
332
+ IS_MASK=True,
333
+ MASK_CAUSAL=IS_CAUSAL,
334
+ MASK_LOCAL=True if IS_LOCAL else False,
335
+ MASK_SINK=False,
336
+ )
337
+
338
+ # Process m_blocks without masking
339
+ if not IS_LOCAL and block_sched.m_block_min_no_mask < block_sched.m_block_max:
340
+ for m_block in tl.range(
341
+ block_sched.m_block_min_no_mask, block_sched.m_block_max
342
+ ):
343
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
344
+ config=config,
345
+ ptrs_sched=ptrs_sched,
346
+ mask_sched=mask_sched,
347
+ acc_dk=acc_dk,
348
+ acc_dv=acc_dv,
349
+ k_tile=k_tile,
350
+ v_tile=v_tile,
351
+ q_ptrs=q_ptrs,
352
+ do_ptrs=do_ptrs,
353
+ dq_accum_ptrs=dq_accum_ptrs,
354
+ lse_ptrs=lse_ptrs,
355
+ dpsum_ptrs=dpsum_ptrs,
356
+ m_block=m_block,
357
+ IS_MASK=False,
358
+ MASK_CAUSAL=False,
359
+ MASK_LOCAL=False,
360
+ MASK_SINK=False,
361
+ )
362
+
363
+ if IS_LOCAL:
364
+ # Process m_blocks with local right masking
365
+ if block_sched.m_block_window_min < block_sched.m_block_window_min_no_mask:
366
+ for m_block in tl.range(
367
+ block_sched.m_block_window_min,
368
+ block_sched.m_block_window_min_no_mask,
369
+ ):
370
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
371
+ config=config,
372
+ ptrs_sched=ptrs_sched,
373
+ mask_sched=mask_sched,
374
+ acc_dk=acc_dk,
375
+ acc_dv=acc_dv,
376
+ k_tile=k_tile,
377
+ v_tile=v_tile,
378
+ q_ptrs=q_ptrs,
379
+ do_ptrs=do_ptrs,
380
+ dq_accum_ptrs=dq_accum_ptrs,
381
+ lse_ptrs=lse_ptrs,
382
+ dpsum_ptrs=dpsum_ptrs,
383
+ m_block=m_block,
384
+ IS_MASK=True,
385
+ MASK_CAUSAL=False,
386
+ MASK_LOCAL=True,
387
+ MASK_SINK=False,
388
+ )
389
+
390
+ # Process m_blocks without masking
391
+ if (
392
+ block_sched.m_block_window_min_no_mask
393
+ < block_sched.m_block_window_max_no_mask
394
+ ):
395
+ for m_block in tl.range(
396
+ block_sched.m_block_window_min_no_mask,
397
+ block_sched.m_block_window_max_no_mask,
398
+ ):
399
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
400
+ config=config,
401
+ ptrs_sched=ptrs_sched,
402
+ mask_sched=mask_sched,
403
+ acc_dk=acc_dk,
404
+ acc_dv=acc_dv,
405
+ k_tile=k_tile,
406
+ v_tile=v_tile,
407
+ q_ptrs=q_ptrs,
408
+ do_ptrs=do_ptrs,
409
+ dq_accum_ptrs=dq_accum_ptrs,
410
+ lse_ptrs=lse_ptrs,
411
+ dpsum_ptrs=dpsum_ptrs,
412
+ m_block=m_block,
413
+ IS_MASK=False,
414
+ MASK_CAUSAL=False,
415
+ MASK_LOCAL=False,
416
+ MASK_SINK=False,
417
+ )
418
+
419
+ # Process m_blocks with local left masking
420
+ if block_sched.m_block_window_max_no_mask < block_sched.m_block_window_max:
421
+ for m_block in tl.range(
422
+ block_sched.m_block_window_max_no_mask,
423
+ block_sched.m_block_window_max,
424
+ ):
425
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
426
+ config=config,
427
+ ptrs_sched=ptrs_sched,
428
+ mask_sched=mask_sched,
429
+ acc_dk=acc_dk,
430
+ acc_dv=acc_dv,
431
+ k_tile=k_tile,
432
+ v_tile=v_tile,
433
+ q_ptrs=q_ptrs,
434
+ do_ptrs=do_ptrs,
435
+ dq_accum_ptrs=dq_accum_ptrs,
436
+ lse_ptrs=lse_ptrs,
437
+ dpsum_ptrs=dpsum_ptrs,
438
+ m_block=m_block,
439
+ IS_MASK=True,
440
+ MASK_CAUSAL=False,
441
+ MASK_LOCAL=True,
442
+ MASK_SINK=False,
443
+ )
444
+
445
+ # Process m_blocks with local sink masking
446
+ if block_sched.m_block_sink_max > block_sched.m_block_sink_min:
447
+ for m_block in tl.range(
448
+ block_sched.m_block_sink_min,
449
+ block_sched.m_block_sink_max,
450
+ ):
451
+ acc_dk, acc_dv = _bwd_inner_sparse_kernel(
452
+ config=config,
453
+ ptrs_sched=ptrs_sched,
454
+ mask_sched=mask_sched,
455
+ acc_dk=acc_dk,
456
+ acc_dv=acc_dv,
457
+ k_tile=k_tile,
458
+ v_tile=v_tile,
459
+ q_ptrs=q_ptrs,
460
+ do_ptrs=do_ptrs,
461
+ dq_accum_ptrs=dq_accum_ptrs,
462
+ lse_ptrs=lse_ptrs,
463
+ dpsum_ptrs=dpsum_ptrs,
464
+ m_block=m_block,
465
+ IS_MASK=True,
466
+ MASK_CAUSAL=False,
467
+ MASK_LOCAL=True,
468
+ MASK_SINK=True,
469
+ )
470
+
471
+ # Store value gradients
472
+ ptrs_sched.store_dv(config, dv_ptrs, acc_dv)
473
+
474
+ # Scale key gradients
475
+ acc_dk = acc_dk * softmax_scale
476
+
477
+ # Store key gradients
478
+ ptrs_sched.store_dk(config, dk_ptrs, acc_dk)
479
+
480
+
481
+ _bwd_sparse_kernel = cache_utils.wrap_kernel(_bwd_sparse_kernel)
482
+
483
+
484
+ _bwd_sparse_kernel_autotuned = None
485
+
486
+
487
+ def _get_autotuned_kernel():
488
+ global _bwd_sparse_kernel_autotuned
489
+ if _bwd_sparse_kernel_autotuned is None:
490
+ jit_kernel = _bwd_sparse_kernel._kernel
491
+ autotuned = autotuner.make_bwd_sparse_autotuned_kernel(jit_kernel)
492
+ _bwd_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
493
+ return _bwd_sparse_kernel_autotuned
494
+
495
+
496
+ def _flash_sparse_attn_backward(
497
+ query: torch.Tensor,
498
+ key: torch.Tensor,
499
+ value: torch.Tensor,
500
+ out: torch.Tensor,
501
+ dout: torch.Tensor,
502
+ lse: torch.Tensor,
503
+ is_causal: bool = False,
504
+ softmax_scale: float = None,
505
+ query_scale: Optional[torch.Tensor] = None,
506
+ key_scale: Optional[torch.Tensor] = None,
507
+ value_scale: Optional[torch.Tensor] = None,
508
+ window_sizes: Optional[torch.Tensor] = None,
509
+ softmax_threshold: float = None,
510
+ is_local: bool = False,
511
+ is_quant: bool = False,
512
+ is_split_qo: bool = False,
513
+ is_autotune: bool = False,
514
+ skip_checks: bool = False,
515
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
516
+ device = query.device
517
+ num_SMs = cache_utils.get_device_num_sms(device)
518
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
519
+ _, seqlen_k, num_heads_kv, _ = key.shape
520
+ softmax_scale = (
521
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
522
+ )
523
+ softmax_threshold = (
524
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
525
+ )
526
+ qhead_per_kvhead = num_heads_q // num_heads_kv
527
+ if is_local and window_sizes is None:
528
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
529
+ elif not is_local:
530
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
531
+
532
+ if not skip_checks:
533
+ assert_inputs.assert_bwd_inputs(
534
+ query,
535
+ key,
536
+ value,
537
+ out,
538
+ dout,
539
+ lse,
540
+ query_scale=query_scale,
541
+ key_scale=key_scale,
542
+ value_scale=value_scale,
543
+ window_sizes=window_sizes,
544
+ cu_seqlens_q=None,
545
+ cu_seqlens_k=None,
546
+ seqused_q=None,
547
+ seqused_k=None,
548
+ num_heads_q=num_heads_q,
549
+ num_heads_kv=num_heads_kv,
550
+ head_dim=head_dim,
551
+ is_quant=is_quant,
552
+ device=device,
553
+ )
554
+
555
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
556
+
557
+ launch_config = launch_template.load_launch_config(
558
+ device=device,
559
+ kernel_name="bwd_sparse",
560
+ seqlen_q=seqlen_q,
561
+ seqlen_k=seqlen_k,
562
+ tile_k=TILE_K,
563
+ is_local=is_local,
564
+ qhead_per_kvhead=qhead_per_kvhead,
565
+ is_causal=is_causal,
566
+ is_quant=is_quant,
567
+ )
568
+ if launch_config is not None and not is_autotune:
569
+ kernel = _bwd_sparse_kernel
570
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
571
+ else:
572
+ kernel = _get_autotuned_kernel()
573
+ # Placeholder for pre-launch computations
574
+ TILE_M = TILE_N = 64
575
+ num_warps = num_stages = num_ctas = None
576
+
577
+ num_splits = (
578
+ utils.num_splits_heuristic(
579
+ seqlen_q=seqlen_k,
580
+ seqlen_k=seqlen_q,
581
+ num_SMs=num_SMs,
582
+ TILE_M=TILE_N,
583
+ TILE_N=TILE_M,
584
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
585
+ window_sizes, TILE_M
586
+ )
587
+ if is_local
588
+ else None,
589
+ )
590
+ if is_split_qo
591
+ else 1
592
+ )
593
+
594
+ seqlen_q_rounded = int(math.ceil(seqlen_q / 128) * 128)
595
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
596
+
597
+ if not is_quant:
598
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
599
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
600
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
601
+
602
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
603
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
604
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
605
+ lse_log2 = torch.empty(
606
+ (batch_size, num_heads_q, seqlen_q_rounded),
607
+ dtype=torch.float32,
608
+ device=query.device,
609
+ )
610
+ dpsum = torch.empty(
611
+ (batch_size, num_heads_q, seqlen_q_rounded),
612
+ dtype=torch.float32,
613
+ device=query.device,
614
+ )
615
+ dq_accum = torch.empty(
616
+ (batch_size, num_heads_q, seqlen_q_rounded * head_dim_rounded),
617
+ dtype=torch.float32,
618
+ device=query.device,
619
+ )
620
+ dk_accum = torch.zeros(
621
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
622
+ if is_split_qo and num_splits > 1
623
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
624
+ dtype=torch.float32,
625
+ device=query.device,
626
+ )
627
+ dv_accum = torch.zeros(
628
+ (num_splits, batch_size, seqlen_k, num_heads_kv, head_dim)
629
+ if is_split_qo and num_splits > 1
630
+ else (batch_size, seqlen_k, num_heads_kv, head_dim),
631
+ dtype=torch.float32,
632
+ device=query.device,
633
+ )
634
+
635
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
636
+ out=out,
637
+ dout=dout,
638
+ dpsum=dpsum,
639
+ lse=lse,
640
+ lse_log2=lse_log2,
641
+ dq_accum=dq_accum,
642
+ head_dim_rounded=head_dim_rounded,
643
+ tile_m=TILE_M,
644
+ tile_k=TILE_K,
645
+ )
646
+
647
+ grid = launch_grid.get_bwd_grid(
648
+ seqlen_k=seqlen_k,
649
+ num_heads_q=num_heads_q,
650
+ batch_size=batch_size,
651
+ num_splits=num_splits,
652
+ )
653
+
654
+ triton.set_allocator(utils.alloc_fn)
655
+
656
+ kernel[grid](
657
+ query,
658
+ key,
659
+ value,
660
+ dout,
661
+ lse_log2,
662
+ dpsum,
663
+ dq_accum,
664
+ dk_accum,
665
+ dv_accum,
666
+ softmax_scale,
667
+ query_scale,
668
+ key_scale,
669
+ value_scale,
670
+ softmax_threshold,
671
+ window_sizes,
672
+ query.stride(0),
673
+ query.stride(-2),
674
+ query.stride(-3),
675
+ key.stride(0),
676
+ key.stride(-2),
677
+ key.stride(-3),
678
+ value.stride(0),
679
+ value.stride(-2),
680
+ value.stride(-3),
681
+ dout.stride(0),
682
+ dout.stride(-2),
683
+ dout.stride(-3),
684
+ lse_log2.stride(0),
685
+ lse_log2.stride(-2),
686
+ dpsum.stride(0),
687
+ dpsum.stride(-2),
688
+ dq_accum.stride(0),
689
+ dq_accum.stride(1),
690
+ head_dim_rounded,
691
+ dk_accum.stride(-4) if is_split_qo and num_splits > 1 else dk_accum.stride(0),
692
+ dk_accum.stride(-2),
693
+ dk_accum.stride(-3),
694
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
695
+ dv_accum.stride(-4) if is_split_qo and num_splits > 1 else dv_accum.stride(0),
696
+ dv_accum.stride(-2),
697
+ dv_accum.stride(-3),
698
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
699
+ window_sizes.stride(0),
700
+ None,
701
+ None,
702
+ None,
703
+ None,
704
+ num_splits,
705
+ seqlen_q=seqlen_q,
706
+ seqlen_k=seqlen_k,
707
+ head_dim=head_dim,
708
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
709
+ SEQLEN_K_CACHE=seqlen_k // 1024,
710
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
711
+ TILE_M=TILE_M,
712
+ TILE_N=TILE_N,
713
+ TILE_K=TILE_K,
714
+ IS_CAUSAL=is_causal,
715
+ IS_LOCAL=is_local,
716
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
717
+ HAS_CU_SEQLENS_Q=False,
718
+ HAS_CU_SEQLENS_K=False,
719
+ HAS_SEQUSED_Q=False,
720
+ HAS_SEQUSED_K=False,
721
+ num_warps=num_warps,
722
+ num_stages=num_stages,
723
+ num_ctas=num_ctas,
724
+ )
725
+
726
+ if launch_config is None or is_autotune:
727
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
728
+ if best is not None:
729
+ launch_template.store_launch_config(
730
+ device=device,
731
+ kernel_name="bwd_sparse",
732
+ seqlen_q=seqlen_q,
733
+ seqlen_k=seqlen_k,
734
+ tile_k=TILE_K,
735
+ config=best,
736
+ is_local=is_local,
737
+ qhead_per_kvhead=qhead_per_kvhead,
738
+ is_causal=is_causal,
739
+ is_quant=is_quant,
740
+ )
741
+
742
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
743
+ dq_accum=dq_accum,
744
+ dq=dq,
745
+ scale=softmax_scale,
746
+ head_dim_rounded=head_dim_rounded,
747
+ tile_m=TILE_M,
748
+ tile_k=TILE_K,
749
+ )
750
+
751
+ if is_split_qo and num_splits > 1:
752
+ dk_accum = dk_accum.sum(dim=0)
753
+ dv_accum = dv_accum.sum(dim=0)
754
+
755
+ dk.copy_(dk_accum)
756
+ dv.copy_(dv_accum)
757
+
758
+ return dq, dk, dv
759
+
760
+
761
+ def _flash_sparse_attn_varlen_backward(
762
+ query: torch.Tensor,
763
+ key: torch.Tensor,
764
+ value: torch.Tensor,
765
+ out: torch.Tensor,
766
+ dout: torch.Tensor,
767
+ lse: torch.Tensor,
768
+ cu_seqlens_q: torch.Tensor,
769
+ cu_seqlens_k: torch.Tensor,
770
+ max_seqlen_q: Optional[int] = None,
771
+ max_seqlen_k: Optional[int] = None,
772
+ is_causal: bool = False,
773
+ softmax_scale: float = None,
774
+ query_scale: Optional[torch.Tensor] = None,
775
+ key_scale: Optional[torch.Tensor] = None,
776
+ value_scale: Optional[torch.Tensor] = None,
777
+ window_sizes: Optional[torch.Tensor] = None,
778
+ softmax_threshold: float = None,
779
+ is_local: bool = False,
780
+ is_quant: bool = False,
781
+ seqused_q: Optional[torch.Tensor] = None,
782
+ seqused_k: Optional[torch.Tensor] = None,
783
+ is_split_qo: bool = False,
784
+ is_autotune: bool = False,
785
+ skip_checks: bool = False,
786
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
787
+ device = query.device
788
+ num_SMs = cache_utils.get_device_num_sms(device)
789
+ total_q, num_heads_q, head_dim = query.shape
790
+ total_k, num_heads_kv, _ = key.shape
791
+ batch_size = cu_seqlens_q.shape[0] - 1
792
+ seqlen_q = max_seqlen_q
793
+ seqlen_k = max_seqlen_k
794
+ softmax_scale = (
795
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
796
+ )
797
+ softmax_threshold = (
798
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
799
+ )
800
+ qhead_per_kvhead = num_heads_q // num_heads_kv
801
+ if is_local and window_sizes is None:
802
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
803
+ elif not is_local:
804
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
805
+
806
+ if not skip_checks:
807
+ assert_inputs.assert_bwd_inputs(
808
+ query,
809
+ key,
810
+ value,
811
+ out,
812
+ dout,
813
+ lse,
814
+ query_scale=query_scale,
815
+ key_scale=key_scale,
816
+ value_scale=value_scale,
817
+ window_sizes=window_sizes,
818
+ cu_seqlens_q=cu_seqlens_q,
819
+ cu_seqlens_k=cu_seqlens_k,
820
+ seqused_q=seqused_q,
821
+ seqused_k=seqused_k,
822
+ num_heads_q=num_heads_q,
823
+ num_heads_kv=num_heads_kv,
824
+ head_dim=head_dim,
825
+ is_quant=is_quant,
826
+ device=device,
827
+ )
828
+
829
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
830
+
831
+ launch_config = launch_template.load_launch_config(
832
+ device=device,
833
+ kernel_name="bwd_sparse",
834
+ seqlen_q=seqlen_q,
835
+ seqlen_k=seqlen_k,
836
+ tile_k=TILE_K,
837
+ is_local=is_local,
838
+ qhead_per_kvhead=qhead_per_kvhead,
839
+ is_causal=is_causal,
840
+ is_quant=is_quant,
841
+ )
842
+ if launch_config is not None and not is_autotune:
843
+ kernel = _bwd_sparse_kernel
844
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
845
+ else:
846
+ kernel = _get_autotuned_kernel()
847
+ # Placeholder for pre-launch computations
848
+ TILE_M = TILE_N = 64
849
+ num_warps = num_stages = num_ctas = None
850
+
851
+ num_splits = (
852
+ utils.num_splits_heuristic(
853
+ seqlen_q=seqlen_k,
854
+ seqlen_k=seqlen_q,
855
+ num_SMs=num_SMs,
856
+ TILE_M=TILE_N,
857
+ TILE_N=TILE_M,
858
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
859
+ window_sizes, TILE_M
860
+ )
861
+ if is_local
862
+ else None,
863
+ )
864
+ if is_split_qo
865
+ else 1
866
+ )
867
+
868
+ total_q_rounded_padded = int(math.ceil((total_q + batch_size * 128) / 128) * 128)
869
+ head_dim_rounded = int(math.ceil(head_dim / 32) * 32)
870
+
871
+ if not is_quant:
872
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
873
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
874
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
875
+
876
+ dq = torch.empty_like(query, dtype=query_scale.dtype)
877
+ dk = torch.empty_like(key, dtype=key_scale.dtype)
878
+ dv = torch.empty_like(value, dtype=value_scale.dtype)
879
+ lse_log2 = torch.empty(
880
+ num_heads_q,
881
+ total_q_rounded_padded,
882
+ dtype=torch.float32,
883
+ device=query.device,
884
+ )
885
+ dpsum = torch.empty(
886
+ num_heads_q,
887
+ total_q_rounded_padded,
888
+ dtype=torch.float32,
889
+ device=query.device,
890
+ )
891
+ dq_accum = torch.empty(
892
+ num_heads_q,
893
+ total_q_rounded_padded * head_dim_rounded,
894
+ dtype=torch.float32,
895
+ device=query.device,
896
+ )
897
+ dk_accum = torch.zeros(
898
+ (num_splits, total_k, num_heads_kv, head_dim)
899
+ if is_split_qo and num_splits > 1
900
+ else (total_k, num_heads_kv, head_dim),
901
+ dtype=torch.float32,
902
+ device=query.device,
903
+ )
904
+ dv_accum = torch.zeros(
905
+ (num_splits, total_k, num_heads_kv, head_dim)
906
+ if is_split_qo and num_splits > 1
907
+ else (total_k, num_heads_kv, head_dim),
908
+ dtype=torch.float32,
909
+ device=query.device,
910
+ )
911
+
912
+ flash_bwd_preprocess._flash_attn_bwd_preprocess(
913
+ out=out,
914
+ dout=dout,
915
+ dpsum=dpsum,
916
+ lse=lse,
917
+ lse_log2=lse_log2,
918
+ dq_accum=dq_accum,
919
+ head_dim_rounded=head_dim_rounded,
920
+ cu_seqlens_q=cu_seqlens_q,
921
+ seqused_q=seqused_q,
922
+ max_seqlen_q=max_seqlen_q,
923
+ tile_m=TILE_M,
924
+ tile_k=TILE_K,
925
+ )
926
+
927
+ grid = launch_grid.get_bwd_grid(
928
+ seqlen_k=seqlen_k,
929
+ num_heads_q=num_heads_q,
930
+ batch_size=batch_size,
931
+ num_splits=num_splits,
932
+ )
933
+
934
+ triton.set_allocator(utils.alloc_fn)
935
+
936
+ kernel[grid](
937
+ query,
938
+ key,
939
+ value,
940
+ dout,
941
+ lse_log2,
942
+ dpsum,
943
+ dq_accum,
944
+ dk_accum,
945
+ dv_accum,
946
+ softmax_scale,
947
+ query_scale,
948
+ key_scale,
949
+ value_scale,
950
+ softmax_threshold,
951
+ window_sizes,
952
+ 0,
953
+ query.stride(-2),
954
+ query.stride(0),
955
+ 0,
956
+ key.stride(-2),
957
+ key.stride(0),
958
+ 0,
959
+ value.stride(-2),
960
+ value.stride(0),
961
+ 0,
962
+ dout.stride(-2),
963
+ dout.stride(0),
964
+ 0,
965
+ lse_log2.stride(0),
966
+ 0,
967
+ dpsum.stride(0),
968
+ 0,
969
+ dq_accum.stride(0),
970
+ head_dim_rounded,
971
+ 0,
972
+ dk_accum.stride(-2),
973
+ dk_accum.stride(-3),
974
+ dk_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
975
+ 0,
976
+ dv_accum.stride(-2),
977
+ dv_accum.stride(-3),
978
+ dv_accum.stride(0) if is_split_qo and num_splits > 1 else 0,
979
+ window_sizes.stride(0),
980
+ cu_seqlens_q,
981
+ cu_seqlens_k,
982
+ seqused_q,
983
+ seqused_k,
984
+ num_splits,
985
+ seqlen_q=seqlen_q,
986
+ seqlen_k=seqlen_k,
987
+ head_dim=head_dim,
988
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
989
+ SEQLEN_K_CACHE=seqlen_k // 1024,
990
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
991
+ TILE_M=TILE_M,
992
+ TILE_N=TILE_N,
993
+ TILE_K=TILE_K,
994
+ IS_CAUSAL=is_causal,
995
+ IS_LOCAL=is_local,
996
+ IS_SPLIT_QO=is_split_qo and num_splits > 1,
997
+ HAS_CU_SEQLENS_Q=True,
998
+ HAS_CU_SEQLENS_K=True,
999
+ HAS_SEQUSED_Q=seqused_q is not None,
1000
+ HAS_SEQUSED_K=seqused_k is not None,
1001
+ num_warps=num_warps,
1002
+ num_stages=num_stages,
1003
+ num_ctas=num_ctas,
1004
+ )
1005
+
1006
+ if launch_config is None or is_autotune:
1007
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
1008
+ if best is not None:
1009
+ launch_template.store_launch_config(
1010
+ device=device,
1011
+ kernel_name="bwd_sparse",
1012
+ seqlen_q=seqlen_q,
1013
+ seqlen_k=seqlen_k,
1014
+ tile_k=TILE_K,
1015
+ config=best,
1016
+ is_local=is_local,
1017
+ qhead_per_kvhead=qhead_per_kvhead,
1018
+ is_causal=is_causal,
1019
+ is_quant=is_quant,
1020
+ )
1021
+
1022
+ flash_bwd_postprocess._flash_attn_bwd_postprocess(
1023
+ dq_accum=dq_accum,
1024
+ dq=dq,
1025
+ scale=softmax_scale,
1026
+ head_dim_rounded=head_dim_rounded,
1027
+ cu_seqlens_q=cu_seqlens_q,
1028
+ seqused_q=seqused_q,
1029
+ max_seqlen_q=max_seqlen_q,
1030
+ tile_m=TILE_M,
1031
+ tile_k=TILE_K,
1032
+ )
1033
+
1034
+ if is_split_qo and num_splits > 1:
1035
+ dk_accum = dk_accum.sum(dim=0)
1036
+ dv_accum = dv_accum.sum(dim=0)
1037
+
1038
+ dk.copy_(dk_accum)
1039
+ dv.copy_(dv_accum)
1040
+
1041
+ return dq, dk, dv
build/torch-cuda/flash_sparse_dec.py ADDED
@@ -0,0 +1,871 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_dec_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnDecGridIndex,
19
+ AttnDecConfig,
20
+ AttnDecBlockScheduler,
21
+ AttnDecPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _dec_inner_sparse_kernel(
29
+ config: AttnDecConfig,
30
+ ptrs_sched: AttnDecPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ q_tile,
34
+ k_tile,
35
+ k_ptrs,
36
+ v_ptrs,
37
+ acc_o,
38
+ row_max,
39
+ row_sum,
40
+ n_block,
41
+ n_block_min,
42
+ IS_MASK: tl.constexpr,
43
+ MASK_LOCAL: tl.constexpr,
44
+ MASK_SINK: tl.constexpr,
45
+ CHECK_INF: tl.constexpr,
46
+ ):
47
+ # Compute attention scores
48
+ acc_s = tl.dot(q_tile, k_tile.T)
49
+
50
+ # Prefetch next key tile
51
+ if n_block > n_block_min:
52
+ # Load next key tile
53
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
54
+
55
+ if IS_MASK:
56
+ # Apply mask to attention scores
57
+ acc_s = mask_sched.apply_mask(
58
+ acc_s=acc_s,
59
+ iter_block=n_block,
60
+ MASK_LOCAL=MASK_LOCAL,
61
+ MASK_SINK=MASK_SINK,
62
+ )
63
+
64
+ # Apply online sparse softmax
65
+ p, row_max, row_sum, row_scale, skip_softmax = softmax_sched.online_sparse_softmax(
66
+ acc_s=acc_s,
67
+ row_max=row_max,
68
+ row_sum=row_sum,
69
+ softmax_threshold_log2=config.softmax_threshold_log2,
70
+ CHECK_INF=CHECK_INF,
71
+ )
72
+
73
+ if not skip_softmax:
74
+ # Load value tile
75
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
76
+
77
+ # Rescale output accumulator
78
+ acc_o = softmax_sched.rescale_o(
79
+ acc_o=acc_o,
80
+ row_scale=row_scale,
81
+ )
82
+
83
+ # Update output accumulator
84
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
85
+
86
+ return k_tile, acc_o, row_max, row_sum
87
+
88
+
89
+ @triton.jit(repr=kernel_repr.dec_sparse_repr)
90
+ def _dec_sparse_kernel(
91
+ Q,
92
+ K,
93
+ V,
94
+ Out,
95
+ Lse,
96
+ softmax_scale,
97
+ query_scale,
98
+ key_scale,
99
+ value_scale,
100
+ softmax_threshold,
101
+ window_sizes,
102
+ stride_qb,
103
+ stride_qh,
104
+ stride_qm,
105
+ stride_kb,
106
+ stride_kh,
107
+ stride_kn,
108
+ stride_vb,
109
+ stride_vh,
110
+ stride_vn,
111
+ stride_ob,
112
+ stride_oh,
113
+ stride_om,
114
+ stride_os,
115
+ stride_lb,
116
+ stride_lh,
117
+ stride_lm,
118
+ stride_ls,
119
+ stride_wh,
120
+ cu_seqlens_q,
121
+ cu_seqlens_k,
122
+ seqused_q,
123
+ seqused_k,
124
+ num_splits,
125
+ seqlen_q,
126
+ seqlen_k,
127
+ head_dim,
128
+ SEQLEN_Q_CACHE: tl.constexpr,
129
+ SEQLEN_K_CACHE: tl.constexpr,
130
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
131
+ TILE_M: tl.constexpr,
132
+ TILE_N: tl.constexpr,
133
+ TILE_K: tl.constexpr,
134
+ IS_LOCAL: tl.constexpr,
135
+ HAS_CU_SEQLENS_Q: tl.constexpr,
136
+ HAS_CU_SEQLENS_K: tl.constexpr,
137
+ HAS_SEQUSED_Q: tl.constexpr,
138
+ HAS_SEQUSED_K: tl.constexpr,
139
+ ):
140
+ # Create grid index
141
+ grid_idx = AttnDecGridIndex.create(
142
+ num_splits=num_splits,
143
+ )
144
+
145
+ # Load window sizes
146
+ (
147
+ window_size_sink,
148
+ window_size_left,
149
+ window_size_right,
150
+ window_size_dist,
151
+ ) = grid_idx.load_window_sizes(
152
+ window_sizes=window_sizes,
153
+ stride_wh=stride_wh,
154
+ IS_LOCAL=IS_LOCAL,
155
+ )
156
+
157
+ # Create config
158
+ config = AttnDecConfig.create(
159
+ softmax_scale=softmax_scale,
160
+ softmax_threshold=softmax_threshold,
161
+ query_scale=query_scale,
162
+ key_scale=key_scale,
163
+ value_scale=value_scale,
164
+ batch_idx=grid_idx.batch_idx,
165
+ window_size_sink=window_size_sink,
166
+ window_size_left=window_size_left,
167
+ window_size_right=window_size_right,
168
+ window_size_dist=window_size_dist,
169
+ head_dim=head_dim,
170
+ cu_seqlens_q=cu_seqlens_q,
171
+ cu_seqlens_k=cu_seqlens_k,
172
+ seqused_q=seqused_q,
173
+ seqused_k=seqused_k,
174
+ seqlen_q=seqlen_q,
175
+ seqlen_k=seqlen_k,
176
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
177
+ TILE_M=TILE_M,
178
+ TILE_N=TILE_N,
179
+ TILE_K=TILE_K,
180
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
181
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
182
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
183
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
184
+ )
185
+
186
+ # Create pointer scheduler
187
+ ptrs_sched = AttnDecPointerScheduler.create(
188
+ config=config,
189
+ Q=Q,
190
+ K=K,
191
+ V=V,
192
+ Out=Out,
193
+ Lse=Lse,
194
+ batch_idx=grid_idx.batch_idx,
195
+ head_idx=grid_idx.head_idx,
196
+ head_kv_idx=grid_idx.head_kv_idx,
197
+ split_idx=grid_idx.split_idx,
198
+ stride_qb=stride_qb,
199
+ stride_qh=stride_qh,
200
+ stride_qm=stride_qm,
201
+ stride_kb=stride_kb,
202
+ stride_kh=stride_kh,
203
+ stride_kn=stride_kn,
204
+ stride_vb=stride_vb,
205
+ stride_vh=stride_vh,
206
+ stride_vn=stride_vn,
207
+ stride_ob=stride_ob,
208
+ stride_oh=stride_oh,
209
+ stride_om=stride_om,
210
+ stride_os=stride_os,
211
+ stride_lb=stride_lb,
212
+ stride_lh=stride_lh,
213
+ stride_lm=stride_lm,
214
+ stride_ls=stride_ls,
215
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
216
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
217
+ )
218
+
219
+ # Create block scheduler
220
+ block_sched = AttnDecBlockScheduler.create(
221
+ config=config,
222
+ split_idx=grid_idx.split_idx,
223
+ num_splits=num_splits,
224
+ IS_LOCAL=IS_LOCAL,
225
+ )
226
+
227
+ # Create mask scheduler
228
+ mask_sched = AttnMaskScheduler.create(config)
229
+
230
+ # Create softmax scheduler
231
+ softmax_sched = SoftmaxScheduler.create(config)
232
+
233
+ # Create pointers
234
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
235
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
236
+
237
+ # Early exit if no n_blocks to process
238
+ if block_sched.is_empty():
239
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, Out)
240
+ return
241
+
242
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
243
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
244
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
245
+
246
+ # Initialize accumulators
247
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
248
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
249
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
250
+
251
+ # Load query tile
252
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
253
+
254
+ # Load key tile
255
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
256
+
257
+ # Process n_blocks with seqlen masking
258
+ for n_block in tl.range(
259
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
260
+ ):
261
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
262
+ config=config,
263
+ ptrs_sched=ptrs_sched,
264
+ mask_sched=mask_sched,
265
+ softmax_sched=softmax_sched,
266
+ q_tile=q_tile,
267
+ k_tile=k_tile,
268
+ k_ptrs=k_ptrs,
269
+ v_ptrs=v_ptrs,
270
+ acc_o=acc_o,
271
+ row_max=row_max,
272
+ row_sum=row_sum,
273
+ n_block=n_block,
274
+ n_block_min=block_sched.n_block_max_no_mask,
275
+ IS_MASK=True,
276
+ MASK_LOCAL=True if IS_LOCAL else False,
277
+ MASK_SINK=False,
278
+ CHECK_INF=True,
279
+ )
280
+
281
+ # Process n_blocks without masking
282
+ if not IS_LOCAL and block_sched.n_block_max_no_mask > block_sched.n_block_min:
283
+ # Load key tile
284
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max_no_mask - 1)
285
+
286
+ for n_block in tl.range(
287
+ block_sched.n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
288
+ ):
289
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
290
+ config=config,
291
+ ptrs_sched=ptrs_sched,
292
+ mask_sched=mask_sched,
293
+ softmax_sched=softmax_sched,
294
+ q_tile=q_tile,
295
+ k_tile=k_tile,
296
+ k_ptrs=k_ptrs,
297
+ v_ptrs=v_ptrs,
298
+ acc_o=acc_o,
299
+ row_max=row_max,
300
+ row_sum=row_sum,
301
+ n_block=n_block,
302
+ n_block_min=block_sched.n_block_min,
303
+ IS_MASK=False,
304
+ MASK_LOCAL=False,
305
+ MASK_SINK=False,
306
+ CHECK_INF=False,
307
+ )
308
+
309
+ if IS_LOCAL:
310
+ # Process n_blocks with local right masking
311
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
312
+ # Load key tile
313
+ k_tile = ptrs_sched.load_k(
314
+ config, k_ptrs, block_sched.n_block_window_max - 1
315
+ )
316
+
317
+ for n_block in tl.range(
318
+ block_sched.n_block_window_max - 1,
319
+ block_sched.n_block_window_max_no_mask - 1,
320
+ -1,
321
+ ):
322
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
323
+ config=config,
324
+ ptrs_sched=ptrs_sched,
325
+ mask_sched=mask_sched,
326
+ softmax_sched=softmax_sched,
327
+ q_tile=q_tile,
328
+ k_tile=k_tile,
329
+ k_ptrs=k_ptrs,
330
+ v_ptrs=v_ptrs,
331
+ acc_o=acc_o,
332
+ row_max=row_max,
333
+ row_sum=row_sum,
334
+ n_block=n_block,
335
+ n_block_min=block_sched.n_block_window_max_no_mask,
336
+ IS_MASK=True,
337
+ MASK_LOCAL=True,
338
+ MASK_SINK=False,
339
+ CHECK_INF=True,
340
+ )
341
+
342
+ # Process n_blocks without masking
343
+ if (
344
+ block_sched.n_block_window_max_no_mask
345
+ > block_sched.n_block_window_min_no_mask
346
+ ):
347
+ # Load key tile
348
+ k_tile = ptrs_sched.load_k(
349
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
350
+ )
351
+
352
+ for n_block in tl.range(
353
+ block_sched.n_block_window_max_no_mask - 1,
354
+ block_sched.n_block_window_min_no_mask - 1,
355
+ -1,
356
+ ):
357
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
358
+ config=config,
359
+ ptrs_sched=ptrs_sched,
360
+ mask_sched=mask_sched,
361
+ softmax_sched=softmax_sched,
362
+ q_tile=q_tile,
363
+ k_tile=k_tile,
364
+ k_ptrs=k_ptrs,
365
+ v_ptrs=v_ptrs,
366
+ acc_o=acc_o,
367
+ row_max=row_max,
368
+ row_sum=row_sum,
369
+ n_block=n_block,
370
+ n_block_min=block_sched.n_block_window_min_no_mask,
371
+ IS_MASK=False,
372
+ MASK_LOCAL=False,
373
+ MASK_SINK=False,
374
+ CHECK_INF=False,
375
+ )
376
+
377
+ # Process n_blocks with local left masking
378
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
379
+ # Load key tile
380
+ k_tile = ptrs_sched.load_k(
381
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
382
+ )
383
+
384
+ for n_block in tl.range(
385
+ block_sched.n_block_window_min_no_mask - 1,
386
+ block_sched.n_block_window_min - 1,
387
+ -1,
388
+ ):
389
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
390
+ config=config,
391
+ ptrs_sched=ptrs_sched,
392
+ mask_sched=mask_sched,
393
+ softmax_sched=softmax_sched,
394
+ q_tile=q_tile,
395
+ k_tile=k_tile,
396
+ k_ptrs=k_ptrs,
397
+ v_ptrs=v_ptrs,
398
+ acc_o=acc_o,
399
+ row_max=row_max,
400
+ row_sum=row_sum,
401
+ n_block=n_block,
402
+ n_block_min=block_sched.n_block_window_min,
403
+ IS_MASK=True,
404
+ MASK_LOCAL=True,
405
+ MASK_SINK=False,
406
+ CHECK_INF=True,
407
+ )
408
+
409
+ # Process n_blocks with local sink masking
410
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
411
+ # Load key tile
412
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
413
+
414
+ for n_block in tl.range(
415
+ block_sched.n_block_sink_max - 1,
416
+ block_sched.n_block_sink_min - 1,
417
+ -1,
418
+ ):
419
+ k_tile, acc_o, row_max, row_sum = _dec_inner_sparse_kernel(
420
+ config=config,
421
+ ptrs_sched=ptrs_sched,
422
+ mask_sched=mask_sched,
423
+ softmax_sched=softmax_sched,
424
+ q_tile=q_tile,
425
+ k_tile=k_tile,
426
+ k_ptrs=k_ptrs,
427
+ v_ptrs=v_ptrs,
428
+ acc_o=acc_o,
429
+ row_max=row_max,
430
+ row_sum=row_sum,
431
+ n_block=n_block,
432
+ n_block_min=block_sched.n_block_sink_min,
433
+ IS_MASK=True,
434
+ MASK_LOCAL=True,
435
+ MASK_SINK=True,
436
+ CHECK_INF=True,
437
+ )
438
+
439
+ # Finalize softmax
440
+ row_scale, lse_tile = softmax_sched.finalize(
441
+ row_max=row_max,
442
+ row_sum=row_sum,
443
+ IS_LOG2=True,
444
+ )
445
+
446
+ # Store LSE
447
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
448
+
449
+ # Finalize rescale
450
+ acc_o = softmax_sched.rescale_o(
451
+ acc_o=acc_o,
452
+ row_scale=row_scale,
453
+ )
454
+
455
+ # Store output
456
+ ptrs_sched.store_out(config, out_ptrs, acc_o)
457
+
458
+
459
+ _dec_sparse_kernel = cache_utils.wrap_kernel(_dec_sparse_kernel)
460
+
461
+
462
+ _dec_sparse_kernel_autotuned = None
463
+
464
+
465
+ def _get_autotuned_kernel():
466
+ global _dec_sparse_kernel_autotuned
467
+ if _dec_sparse_kernel_autotuned is None:
468
+ jit_kernel = _dec_sparse_kernel._kernel
469
+ autotuned = autotuner.make_dec_sparse_autotuned_kernel(jit_kernel)
470
+ _dec_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
471
+ return _dec_sparse_kernel_autotuned
472
+
473
+
474
+ def _flash_sparse_attn_decode(
475
+ query: torch.Tensor,
476
+ key: torch.Tensor,
477
+ value: torch.Tensor,
478
+ softmax_scale: float = None,
479
+ softmax_threshold: float = None,
480
+ query_scale: Optional[torch.Tensor] = None,
481
+ key_scale: Optional[torch.Tensor] = None,
482
+ value_scale: Optional[torch.Tensor] = None,
483
+ window_sizes: Optional[torch.Tensor] = None,
484
+ is_local: bool = False,
485
+ is_quant: bool = False,
486
+ out: Optional[torch.Tensor] = None,
487
+ lse: Optional[torch.Tensor] = None,
488
+ is_autotune: bool = False,
489
+ skip_checks: bool = False,
490
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
491
+ device = query.device
492
+ num_SMs = cache_utils.get_device_num_sms(device)
493
+ batch_size, num_heads_q, head_dim = query.shape
494
+ _, seqlen_k, num_heads_kv, _ = key.shape
495
+ softmax_scale = (
496
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
497
+ )
498
+ softmax_threshold = (
499
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
500
+ )
501
+ qhead_per_kvhead = num_heads_q // num_heads_kv
502
+ if is_local and window_sizes is None:
503
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
504
+ elif not is_local:
505
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
506
+
507
+ if not skip_checks:
508
+ assert_inputs.assert_dec_inputs(
509
+ query,
510
+ key,
511
+ value,
512
+ query_scale=query_scale,
513
+ key_scale=key_scale,
514
+ value_scale=value_scale,
515
+ window_sizes=window_sizes,
516
+ cu_seqlens_k=None,
517
+ seqused_k=None,
518
+ num_heads_q=num_heads_q,
519
+ num_heads_kv=num_heads_kv,
520
+ head_dim=head_dim,
521
+ is_quant=is_quant,
522
+ device=device,
523
+ )
524
+
525
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
526
+
527
+ launch_config = launch_template.load_launch_config(
528
+ device=device,
529
+ kernel_name="dec_sparse",
530
+ seqlen_q=1,
531
+ seqlen_k=seqlen_k,
532
+ tile_k=TILE_K,
533
+ is_local=is_local,
534
+ qhead_per_kvhead=qhead_per_kvhead,
535
+ is_quant=is_quant,
536
+ )
537
+ if launch_config is not None and not is_autotune:
538
+ kernel = _dec_sparse_kernel
539
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
540
+ else:
541
+ kernel = _get_autotuned_kernel()
542
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
543
+ TILE_N = 128
544
+ num_warps = num_stages = num_ctas = None
545
+
546
+ num_splits = utils.num_splits_heuristic(
547
+ seqlen_q=qhead_per_kvhead,
548
+ seqlen_k=seqlen_k,
549
+ num_SMs=num_SMs,
550
+ TILE_M=TILE_M,
551
+ TILE_N=TILE_N,
552
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
553
+ if is_local
554
+ else None,
555
+ )
556
+
557
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
558
+ out = (
559
+ out
560
+ if out is not None
561
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
562
+ )
563
+ lse = (
564
+ lse
565
+ if lse is not None
566
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
567
+ )
568
+
569
+ out_partial = torch.empty(
570
+ (num_splits, batch_size, num_heads_q, head_dim),
571
+ dtype=torch.float32,
572
+ device=device,
573
+ )
574
+ lse_partial = torch.empty(
575
+ (num_splits, batch_size, num_heads_q),
576
+ dtype=torch.float32,
577
+ device=device,
578
+ )
579
+
580
+ if not is_quant:
581
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
582
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
583
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
584
+
585
+ grid = launch_grid.get_dec_grid(
586
+ batch_size=batch_size,
587
+ num_heads_kv=num_heads_kv,
588
+ num_splits=num_splits,
589
+ )
590
+
591
+ triton.set_allocator(utils.alloc_fn)
592
+
593
+ kernel[grid](
594
+ query,
595
+ key,
596
+ value,
597
+ out_partial,
598
+ lse_partial,
599
+ softmax_scale,
600
+ query_scale,
601
+ key_scale,
602
+ value_scale,
603
+ softmax_threshold,
604
+ window_sizes,
605
+ query.stride(0),
606
+ query.stride(-2),
607
+ 1,
608
+ key.stride(0),
609
+ key.stride(-2),
610
+ key.stride(-3),
611
+ value.stride(0),
612
+ value.stride(-2),
613
+ value.stride(-3),
614
+ out_partial.stride(1),
615
+ out_partial.stride(-2),
616
+ 1,
617
+ out_partial.stride(0),
618
+ lse_partial.stride(1),
619
+ lse_partial.stride(-1),
620
+ 1,
621
+ lse_partial.stride(0),
622
+ window_sizes.stride(0),
623
+ None,
624
+ None,
625
+ None,
626
+ None,
627
+ num_splits,
628
+ seqlen_q=1,
629
+ seqlen_k=seqlen_k,
630
+ head_dim=head_dim,
631
+ SEQLEN_Q_CACHE=0,
632
+ SEQLEN_K_CACHE=seqlen_k // 1024,
633
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
634
+ TILE_M=TILE_M,
635
+ TILE_N=TILE_N,
636
+ TILE_K=TILE_K,
637
+ IS_LOCAL=is_local,
638
+ HAS_CU_SEQLENS_Q=False,
639
+ HAS_CU_SEQLENS_K=False,
640
+ HAS_SEQUSED_Q=False,
641
+ HAS_SEQUSED_K=False,
642
+ num_warps=num_warps,
643
+ num_stages=num_stages,
644
+ num_ctas=num_ctas,
645
+ )
646
+
647
+ if launch_config is None or is_autotune:
648
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
649
+ if best is not None:
650
+ launch_template.store_launch_config(
651
+ device=device,
652
+ kernel_name="dec_sparse",
653
+ seqlen_q=1,
654
+ seqlen_k=seqlen_k,
655
+ tile_k=TILE_K,
656
+ config=best,
657
+ is_local=is_local,
658
+ qhead_per_kvhead=qhead_per_kvhead,
659
+ is_quant=is_quant,
660
+ )
661
+
662
+ flash_dec_combine._flash_attn_dec_combine(
663
+ out_partial,
664
+ lse_partial,
665
+ out,
666
+ lse,
667
+ )
668
+
669
+ return out, lse
670
+
671
+
672
+ def _flash_sparse_attn_varlen_decode(
673
+ query: torch.Tensor,
674
+ key: torch.Tensor,
675
+ value: torch.Tensor,
676
+ cu_seqlens_k: torch.Tensor,
677
+ max_seqlen_k: int,
678
+ softmax_scale: float = None,
679
+ softmax_threshold: float = None,
680
+ query_scale: Optional[torch.Tensor] = None,
681
+ key_scale: Optional[torch.Tensor] = None,
682
+ value_scale: Optional[torch.Tensor] = None,
683
+ window_sizes: Optional[torch.Tensor] = None,
684
+ is_local: bool = False,
685
+ is_quant: bool = False,
686
+ seqused_k: Optional[torch.Tensor] = None,
687
+ out: Optional[torch.Tensor] = None,
688
+ lse: Optional[torch.Tensor] = None,
689
+ is_autotune: bool = False,
690
+ skip_checks: bool = False,
691
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
692
+ device = query.device
693
+ num_SMs = cache_utils.get_device_num_sms(device)
694
+ batch_size, num_heads_q, head_dim = query.shape
695
+ _, num_heads_kv, _ = key.shape
696
+ seqlen_k = max_seqlen_k
697
+ softmax_scale = (
698
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
699
+ )
700
+ softmax_threshold = (
701
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
702
+ )
703
+ qhead_per_kvhead = num_heads_q // num_heads_kv
704
+ if is_local and window_sizes is None:
705
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
706
+ elif not is_local:
707
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
708
+
709
+ if not skip_checks:
710
+ assert_inputs.assert_dec_inputs(
711
+ query,
712
+ key,
713
+ value,
714
+ query_scale=query_scale,
715
+ key_scale=key_scale,
716
+ value_scale=value_scale,
717
+ window_sizes=window_sizes,
718
+ cu_seqlens_k=cu_seqlens_k,
719
+ seqused_k=seqused_k,
720
+ num_heads_q=num_heads_q,
721
+ num_heads_kv=num_heads_kv,
722
+ head_dim=head_dim,
723
+ is_quant=is_quant,
724
+ device=device,
725
+ )
726
+
727
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
728
+
729
+ launch_config = launch_template.load_launch_config(
730
+ device=device,
731
+ kernel_name="dec_sparse",
732
+ seqlen_q=1,
733
+ seqlen_k=seqlen_k,
734
+ tile_k=TILE_K,
735
+ is_local=is_local,
736
+ qhead_per_kvhead=qhead_per_kvhead,
737
+ is_quant=is_quant,
738
+ )
739
+ if launch_config is not None and not is_autotune:
740
+ kernel = _dec_sparse_kernel
741
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
742
+ else:
743
+ kernel = _get_autotuned_kernel()
744
+ TILE_M = max(triton.next_power_of_2(qhead_per_kvhead), 16)
745
+ TILE_N = 128
746
+ num_warps = num_stages = num_ctas = None
747
+
748
+ num_splits = utils.num_splits_heuristic(
749
+ seqlen_q=qhead_per_kvhead,
750
+ seqlen_k=seqlen_k,
751
+ num_SMs=num_SMs,
752
+ TILE_M=TILE_M,
753
+ TILE_N=TILE_N,
754
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(window_sizes, TILE_N)
755
+ if is_local
756
+ else None,
757
+ )
758
+
759
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
760
+ out = (
761
+ out
762
+ if out is not None
763
+ else torch.empty(query.shape, dtype=out_dtype, device=device)
764
+ )
765
+ lse = (
766
+ lse
767
+ if lse is not None
768
+ else torch.empty((batch_size, num_heads_q), dtype=torch.float32, device=device)
769
+ )
770
+
771
+ out_partial = torch.empty(
772
+ (num_splits, batch_size, num_heads_q, head_dim),
773
+ dtype=torch.float32,
774
+ device=device,
775
+ )
776
+ lse_partial = torch.empty(
777
+ (num_splits, batch_size, num_heads_q),
778
+ dtype=torch.float32,
779
+ device=device,
780
+ )
781
+
782
+ if not is_quant:
783
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
784
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
785
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
786
+
787
+ grid = launch_grid.get_dec_grid(
788
+ batch_size=batch_size,
789
+ num_heads_kv=num_heads_kv,
790
+ num_splits=num_splits,
791
+ )
792
+
793
+ triton.set_allocator(utils.alloc_fn)
794
+
795
+ kernel[grid](
796
+ query,
797
+ key,
798
+ value,
799
+ out_partial,
800
+ lse_partial,
801
+ softmax_scale,
802
+ query_scale,
803
+ key_scale,
804
+ value_scale,
805
+ softmax_threshold,
806
+ window_sizes,
807
+ query.stride(0),
808
+ query.stride(-2),
809
+ 1,
810
+ 0,
811
+ key.stride(-2),
812
+ key.stride(0),
813
+ 0,
814
+ value.stride(-2),
815
+ value.stride(0),
816
+ out_partial.stride(1),
817
+ out_partial.stride(-2),
818
+ 1,
819
+ out_partial.stride(0),
820
+ lse_partial.stride(1),
821
+ lse_partial.stride(-1),
822
+ 1,
823
+ lse_partial.stride(0),
824
+ window_sizes.stride(0),
825
+ None,
826
+ cu_seqlens_k,
827
+ None,
828
+ seqused_k,
829
+ num_splits,
830
+ seqlen_q=1,
831
+ seqlen_k=seqlen_k,
832
+ head_dim=head_dim,
833
+ SEQLEN_Q_CACHE=0,
834
+ SEQLEN_K_CACHE=seqlen_k // 1024,
835
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead,
836
+ TILE_M=TILE_M,
837
+ TILE_N=TILE_N,
838
+ TILE_K=TILE_K,
839
+ IS_LOCAL=is_local,
840
+ HAS_CU_SEQLENS_Q=False,
841
+ HAS_CU_SEQLENS_K=True,
842
+ HAS_SEQUSED_Q=False,
843
+ HAS_SEQUSED_K=seqused_k is not None,
844
+ num_warps=num_warps,
845
+ num_stages=num_stages,
846
+ num_ctas=num_ctas,
847
+ )
848
+
849
+ if launch_config is None or is_autotune:
850
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
851
+ if best is not None:
852
+ launch_template.store_launch_config(
853
+ device=device,
854
+ kernel_name="dec_sparse",
855
+ seqlen_q=1,
856
+ seqlen_k=seqlen_k,
857
+ tile_k=TILE_K,
858
+ config=best,
859
+ is_local=is_local,
860
+ qhead_per_kvhead=qhead_per_kvhead,
861
+ is_quant=is_quant,
862
+ )
863
+
864
+ flash_dec_combine._flash_attn_dec_combine(
865
+ out_partial,
866
+ lse_partial,
867
+ out,
868
+ lse,
869
+ )
870
+
871
+ return out, lse
build/torch-cuda/flash_sparse_fwd.py ADDED
@@ -0,0 +1,974 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple, Optional
2
+
3
+ import torch
4
+ import triton
5
+ import triton.language as tl
6
+
7
+ from . import (
8
+ assert_inputs,
9
+ utils,
10
+ cache_utils,
11
+ launch_template,
12
+ launch_grid,
13
+ flash_fwd_combine,
14
+ kernel_repr,
15
+ autotuner,
16
+ )
17
+ from .scheduler import (
18
+ AttnFwdGridIndex,
19
+ AttnFwdConfig,
20
+ AttnFwdBlockScheduler,
21
+ AttnFwdPointerScheduler,
22
+ AttnMaskScheduler,
23
+ SoftmaxScheduler,
24
+ )
25
+
26
+
27
+ @triton.jit
28
+ def _fwd_inner_sparse_kernel(
29
+ config: AttnFwdConfig,
30
+ ptrs_sched: AttnFwdPointerScheduler,
31
+ mask_sched: AttnMaskScheduler,
32
+ softmax_sched: SoftmaxScheduler,
33
+ q_tile,
34
+ k_tile,
35
+ k_ptrs,
36
+ v_ptrs,
37
+ acc_o,
38
+ row_max,
39
+ row_sum,
40
+ n_block,
41
+ n_block_min,
42
+ IS_MASK: tl.constexpr,
43
+ MASK_CAUSAL: tl.constexpr,
44
+ MASK_LOCAL: tl.constexpr,
45
+ MASK_SINK: tl.constexpr,
46
+ CHECK_INF: tl.constexpr,
47
+ ):
48
+ # Compute attention scores
49
+ acc_s = tl.dot(q_tile, k_tile.T)
50
+
51
+ # Prefetch next key tile
52
+ if n_block > n_block_min:
53
+ # Load next key tile
54
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block - 1)
55
+
56
+ if IS_MASK:
57
+ # Apply mask to attention scores
58
+ acc_s = mask_sched.apply_mask(
59
+ acc_s=acc_s,
60
+ iter_block=n_block,
61
+ MASK_CAUSAL=MASK_CAUSAL,
62
+ MASK_LOCAL=MASK_LOCAL,
63
+ MASK_SINK=MASK_SINK,
64
+ )
65
+
66
+ # Apply online sparse softmax
67
+ p, row_max, row_sum, row_scale, skip_softmax = softmax_sched.online_sparse_softmax(
68
+ acc_s=acc_s,
69
+ row_max=row_max,
70
+ row_sum=row_sum,
71
+ softmax_threshold_log2=config.softmax_threshold_log2,
72
+ CHECK_INF=CHECK_INF,
73
+ )
74
+
75
+ if not skip_softmax:
76
+ # Load value tile
77
+ v_tile = ptrs_sched.load_v(config, v_ptrs, n_block)
78
+
79
+ # Rescale output accumulator
80
+ acc_o = softmax_sched.rescale_o(
81
+ acc_o=acc_o,
82
+ row_scale=row_scale,
83
+ )
84
+
85
+ # Update output accumulator
86
+ acc_o += tl.dot(p.to(v_tile.dtype), v_tile)
87
+
88
+ return k_tile, acc_o, row_max, row_sum
89
+
90
+
91
+ @triton.jit(repr=kernel_repr.fwd_sparse_repr)
92
+ def _fwd_sparse_kernel(
93
+ Q,
94
+ K,
95
+ V,
96
+ Out,
97
+ Lse,
98
+ softmax_scale,
99
+ softmax_threshold,
100
+ query_scale,
101
+ key_scale,
102
+ value_scale,
103
+ window_sizes,
104
+ stride_qb,
105
+ stride_qh,
106
+ stride_qm,
107
+ stride_kb,
108
+ stride_kh,
109
+ stride_kn,
110
+ stride_vb,
111
+ stride_vh,
112
+ stride_vn,
113
+ stride_ob,
114
+ stride_oh,
115
+ stride_om,
116
+ stride_os,
117
+ stride_lb,
118
+ stride_lh,
119
+ stride_ls,
120
+ stride_wh,
121
+ cu_seqlens_q,
122
+ cu_seqlens_k,
123
+ seqused_q,
124
+ seqused_k,
125
+ num_splits,
126
+ seqlen_q,
127
+ seqlen_k,
128
+ head_dim,
129
+ SEQLEN_Q_CACHE: tl.constexpr,
130
+ SEQLEN_K_CACHE: tl.constexpr,
131
+ QHEAD_PER_KVHEAD: tl.constexpr,
132
+ PACK_GQA: tl.constexpr,
133
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
134
+ TILE_M: tl.constexpr,
135
+ TILE_N: tl.constexpr,
136
+ TILE_K: tl.constexpr,
137
+ IS_CAUSAL: tl.constexpr,
138
+ IS_LOCAL: tl.constexpr,
139
+ IS_SPLIT_KV: tl.constexpr,
140
+ HAS_CU_SEQLENS_Q: tl.constexpr,
141
+ HAS_CU_SEQLENS_K: tl.constexpr,
142
+ HAS_SEQUSED_Q: tl.constexpr,
143
+ HAS_SEQUSED_K: tl.constexpr,
144
+ ):
145
+ # Create grid index
146
+ grid_idx = AttnFwdGridIndex.create(
147
+ num_splits=num_splits,
148
+ QHEAD_PER_KVHEAD=QHEAD_PER_KVHEAD,
149
+ IS_SPLIT_KV=IS_SPLIT_KV,
150
+ PACK_GQA=PACK_GQA,
151
+ )
152
+
153
+ # Load window sizes
154
+ (
155
+ window_size_sink,
156
+ window_size_left,
157
+ window_size_right,
158
+ window_size_dist,
159
+ ) = grid_idx.load_window_sizes(
160
+ window_sizes=window_sizes,
161
+ stride_wh=stride_wh,
162
+ IS_LOCAL=IS_LOCAL,
163
+ )
164
+
165
+ # Create config
166
+ config = AttnFwdConfig.create(
167
+ softmax_scale=softmax_scale,
168
+ softmax_threshold=softmax_threshold,
169
+ query_scale=query_scale,
170
+ key_scale=key_scale,
171
+ value_scale=value_scale,
172
+ m_block=grid_idx.m_block,
173
+ batch_idx=grid_idx.batch_idx,
174
+ window_size_sink=window_size_sink,
175
+ window_size_left=window_size_left,
176
+ window_size_right=window_size_right,
177
+ window_size_dist=window_size_dist,
178
+ head_dim=head_dim,
179
+ cu_seqlens_q=cu_seqlens_q,
180
+ cu_seqlens_k=cu_seqlens_k,
181
+ seqused_q=seqused_q,
182
+ seqused_k=seqused_k,
183
+ seqlen_q=seqlen_q,
184
+ seqlen_k=seqlen_k,
185
+ PACK_GQA=PACK_GQA,
186
+ QHEAD_PER_KVHEAD_PACKGQA=QHEAD_PER_KVHEAD_PACKGQA,
187
+ TILE_M=TILE_M,
188
+ TILE_N=TILE_N,
189
+ TILE_K=TILE_K,
190
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
191
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
192
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
193
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
194
+ )
195
+
196
+ # Create pointer scheduler
197
+ ptrs_sched = AttnFwdPointerScheduler.create(
198
+ config=config,
199
+ Q=Q,
200
+ K=K,
201
+ V=V,
202
+ Out=Out,
203
+ Lse=Lse,
204
+ batch_idx=grid_idx.batch_idx,
205
+ head_idx=grid_idx.head_idx,
206
+ head_kv_idx=grid_idx.head_kv_idx,
207
+ split_idx=grid_idx.split_idx,
208
+ stride_qb=stride_qb,
209
+ stride_qh=stride_qh,
210
+ stride_qm=stride_qm,
211
+ stride_kb=stride_kb,
212
+ stride_kh=stride_kh,
213
+ stride_kn=stride_kn,
214
+ stride_vb=stride_vb,
215
+ stride_vh=stride_vh,
216
+ stride_vn=stride_vn,
217
+ stride_ob=stride_ob,
218
+ stride_oh=stride_oh,
219
+ stride_om=stride_om,
220
+ stride_os=stride_os,
221
+ stride_lb=stride_lb,
222
+ stride_lh=stride_lh,
223
+ stride_ls=stride_ls,
224
+ IS_SPLIT_KV=IS_SPLIT_KV,
225
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
226
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
227
+ )
228
+
229
+ # Create block scheduler
230
+ block_sched = AttnFwdBlockScheduler.create(
231
+ config=config,
232
+ split_idx=grid_idx.split_idx,
233
+ num_splits=num_splits,
234
+ IS_CAUSAL=IS_CAUSAL,
235
+ IS_LOCAL=IS_LOCAL,
236
+ IS_SPLIT_KV=IS_SPLIT_KV,
237
+ )
238
+
239
+ # Create mask scheduler
240
+ mask_sched = AttnMaskScheduler.create(config)
241
+
242
+ # Create softmax scheduler
243
+ softmax_sched = SoftmaxScheduler.create(config)
244
+
245
+ # Create pointers
246
+ out_ptrs = ptrs_sched.make_out_ptrs(config)
247
+ lse_ptrs = ptrs_sched.make_lse_ptrs(config)
248
+
249
+ # Early exit if no n_blocks to process
250
+ if block_sched.is_empty():
251
+ ptrs_sched.store_empty(config, out_ptrs, lse_ptrs, IS_SPLIT_KV=IS_SPLIT_KV)
252
+ return
253
+
254
+ q_ptrs = ptrs_sched.make_q_ptrs(config)
255
+ k_ptrs = ptrs_sched.make_k_ptrs(config)
256
+ v_ptrs = ptrs_sched.make_v_ptrs(config)
257
+
258
+ # Initialize accumulators
259
+ row_max = tl.full((TILE_M,), float("-inf"), dtype=tl.float32)
260
+ row_sum = tl.zeros((TILE_M,), dtype=tl.float32)
261
+ acc_o = tl.zeros((TILE_M, TILE_K), dtype=tl.float32)
262
+
263
+ # Load query tile
264
+ q_tile = ptrs_sched.load_q(config, q_ptrs)
265
+
266
+ # Load key tile
267
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_max - 1)
268
+
269
+ # Process n_blocks with causal masking
270
+ if IS_CAUSAL or IS_LOCAL:
271
+ n_block_max_no_mask = block_sched.n_block_max_no_mask
272
+ for n_block in tl.range(
273
+ block_sched.n_block_max - 1, block_sched.n_block_max_no_mask - 1, -1
274
+ ):
275
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
276
+ config=config,
277
+ ptrs_sched=ptrs_sched,
278
+ mask_sched=mask_sched,
279
+ softmax_sched=softmax_sched,
280
+ q_tile=q_tile,
281
+ k_tile=k_tile,
282
+ k_ptrs=k_ptrs,
283
+ v_ptrs=v_ptrs,
284
+ acc_o=acc_o,
285
+ row_max=row_max,
286
+ row_sum=row_sum,
287
+ n_block=n_block,
288
+ n_block_min=block_sched.n_block_max_no_mask,
289
+ IS_MASK=True,
290
+ MASK_CAUSAL=IS_CAUSAL,
291
+ MASK_LOCAL=True if IS_LOCAL else False,
292
+ MASK_SINK=False,
293
+ CHECK_INF=True,
294
+ )
295
+ else:
296
+ # First iteration with seqlen masking
297
+ n_block = block_sched.n_block_max - 1
298
+ # Triton aggregate does not support attribute reassignment yet
299
+ # block_sched.n_block_max_no_mask = n_block
300
+ n_block_max_no_mask = n_block
301
+
302
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
303
+ config=config,
304
+ ptrs_sched=ptrs_sched,
305
+ mask_sched=mask_sched,
306
+ softmax_sched=softmax_sched,
307
+ q_tile=q_tile,
308
+ k_tile=k_tile,
309
+ k_ptrs=k_ptrs,
310
+ v_ptrs=v_ptrs,
311
+ acc_o=acc_o,
312
+ row_max=row_max,
313
+ row_sum=row_sum,
314
+ n_block=n_block,
315
+ n_block_min=n_block,
316
+ IS_MASK=True,
317
+ MASK_CAUSAL=False,
318
+ MASK_LOCAL=False,
319
+ MASK_SINK=False,
320
+ CHECK_INF=True,
321
+ )
322
+
323
+ # Process n_blocks without masking
324
+ if not IS_LOCAL and n_block_max_no_mask > block_sched.n_block_min:
325
+ # Load key tile
326
+ k_tile = ptrs_sched.load_k(config, k_ptrs, n_block_max_no_mask - 1)
327
+
328
+ for n_block in tl.range(
329
+ n_block_max_no_mask - 1, block_sched.n_block_min - 1, -1
330
+ ):
331
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
332
+ config=config,
333
+ ptrs_sched=ptrs_sched,
334
+ mask_sched=mask_sched,
335
+ softmax_sched=softmax_sched,
336
+ q_tile=q_tile,
337
+ k_tile=k_tile,
338
+ k_ptrs=k_ptrs,
339
+ v_ptrs=v_ptrs,
340
+ acc_o=acc_o,
341
+ row_max=row_max,
342
+ row_sum=row_sum,
343
+ n_block=n_block,
344
+ n_block_min=block_sched.n_block_min,
345
+ IS_MASK=False,
346
+ MASK_CAUSAL=False,
347
+ MASK_LOCAL=False,
348
+ MASK_SINK=False,
349
+ CHECK_INF=False,
350
+ )
351
+
352
+ if IS_LOCAL:
353
+ # Process n_blocks with local right masking
354
+ if block_sched.n_block_window_max > block_sched.n_block_window_max_no_mask:
355
+ # Load key tile
356
+ k_tile = ptrs_sched.load_k(
357
+ config, k_ptrs, block_sched.n_block_window_max - 1
358
+ )
359
+
360
+ for n_block in tl.range(
361
+ block_sched.n_block_window_max - 1,
362
+ block_sched.n_block_window_max_no_mask - 1,
363
+ -1,
364
+ ):
365
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
366
+ config=config,
367
+ ptrs_sched=ptrs_sched,
368
+ mask_sched=mask_sched,
369
+ softmax_sched=softmax_sched,
370
+ q_tile=q_tile,
371
+ k_tile=k_tile,
372
+ k_ptrs=k_ptrs,
373
+ v_ptrs=v_ptrs,
374
+ acc_o=acc_o,
375
+ row_max=row_max,
376
+ row_sum=row_sum,
377
+ n_block=n_block,
378
+ n_block_min=block_sched.n_block_window_max_no_mask,
379
+ IS_MASK=True,
380
+ MASK_CAUSAL=False,
381
+ MASK_LOCAL=True,
382
+ MASK_SINK=False,
383
+ CHECK_INF=True,
384
+ )
385
+
386
+ # Process n_blocks without masking
387
+ if (
388
+ block_sched.n_block_window_max_no_mask
389
+ > block_sched.n_block_window_min_no_mask
390
+ ):
391
+ # Load key tile
392
+ k_tile = ptrs_sched.load_k(
393
+ config, k_ptrs, block_sched.n_block_window_max_no_mask - 1
394
+ )
395
+
396
+ for n_block in tl.range(
397
+ block_sched.n_block_window_max_no_mask - 1,
398
+ block_sched.n_block_window_min_no_mask - 1,
399
+ -1,
400
+ ):
401
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
402
+ config=config,
403
+ ptrs_sched=ptrs_sched,
404
+ mask_sched=mask_sched,
405
+ softmax_sched=softmax_sched,
406
+ q_tile=q_tile,
407
+ k_tile=k_tile,
408
+ k_ptrs=k_ptrs,
409
+ v_ptrs=v_ptrs,
410
+ acc_o=acc_o,
411
+ row_max=row_max,
412
+ row_sum=row_sum,
413
+ n_block=n_block,
414
+ n_block_min=block_sched.n_block_window_min_no_mask,
415
+ IS_MASK=False,
416
+ MASK_CAUSAL=False,
417
+ MASK_LOCAL=False,
418
+ MASK_SINK=False,
419
+ CHECK_INF=False,
420
+ )
421
+
422
+ # Process n_blocks with local left masking
423
+ if block_sched.n_block_window_min_no_mask > block_sched.n_block_window_min:
424
+ # Load key tile
425
+ k_tile = ptrs_sched.load_k(
426
+ config, k_ptrs, block_sched.n_block_window_min_no_mask - 1
427
+ )
428
+
429
+ for n_block in tl.range(
430
+ block_sched.n_block_window_min_no_mask - 1,
431
+ block_sched.n_block_window_min - 1,
432
+ -1,
433
+ ):
434
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
435
+ config=config,
436
+ ptrs_sched=ptrs_sched,
437
+ mask_sched=mask_sched,
438
+ softmax_sched=softmax_sched,
439
+ q_tile=q_tile,
440
+ k_tile=k_tile,
441
+ k_ptrs=k_ptrs,
442
+ v_ptrs=v_ptrs,
443
+ acc_o=acc_o,
444
+ row_max=row_max,
445
+ row_sum=row_sum,
446
+ n_block=n_block,
447
+ n_block_min=block_sched.n_block_window_min,
448
+ IS_MASK=True,
449
+ MASK_CAUSAL=False,
450
+ MASK_LOCAL=True,
451
+ MASK_SINK=False,
452
+ CHECK_INF=True,
453
+ )
454
+
455
+ # Process n_blocks with local sink masking
456
+ if block_sched.n_block_sink_max > block_sched.n_block_sink_min:
457
+ # Load key tile
458
+ k_tile = ptrs_sched.load_k(config, k_ptrs, block_sched.n_block_sink_max - 1)
459
+
460
+ for n_block in tl.range(
461
+ block_sched.n_block_sink_max - 1,
462
+ block_sched.n_block_sink_min - 1,
463
+ -1,
464
+ ):
465
+ k_tile, acc_o, row_max, row_sum = _fwd_inner_sparse_kernel(
466
+ config=config,
467
+ ptrs_sched=ptrs_sched,
468
+ mask_sched=mask_sched,
469
+ softmax_sched=softmax_sched,
470
+ q_tile=q_tile,
471
+ k_tile=k_tile,
472
+ k_ptrs=k_ptrs,
473
+ v_ptrs=v_ptrs,
474
+ acc_o=acc_o,
475
+ row_max=row_max,
476
+ row_sum=row_sum,
477
+ n_block=n_block,
478
+ n_block_min=block_sched.n_block_sink_min,
479
+ IS_MASK=True,
480
+ MASK_CAUSAL=False,
481
+ MASK_LOCAL=True,
482
+ MASK_SINK=True,
483
+ CHECK_INF=True,
484
+ )
485
+
486
+ # Finalize softmax
487
+ row_scale, lse_tile = softmax_sched.finalize(
488
+ row_max=row_max,
489
+ row_sum=row_sum,
490
+ IS_LOG2=IS_SPLIT_KV,
491
+ )
492
+
493
+ # Store LSE
494
+ ptrs_sched.store_lse(config, lse_ptrs, lse_tile)
495
+
496
+ # Finalize rescale
497
+ acc_o = softmax_sched.rescale_o(
498
+ acc_o=acc_o,
499
+ row_scale=row_scale,
500
+ )
501
+
502
+ # Store output
503
+ ptrs_sched.store_out(config, out_ptrs, acc_o, IS_SPLIT_KV=IS_SPLIT_KV)
504
+
505
+
506
+ _fwd_sparse_kernel = cache_utils.wrap_kernel(_fwd_sparse_kernel)
507
+
508
+
509
+ _fwd_sparse_kernel_autotuned = None
510
+
511
+
512
+ def _get_autotuned_kernel():
513
+ global _fwd_sparse_kernel_autotuned
514
+ if _fwd_sparse_kernel_autotuned is None:
515
+ jit_kernel = _fwd_sparse_kernel._kernel
516
+ autotuned = autotuner.make_fwd_sparse_autotuned_kernel(jit_kernel)
517
+ _fwd_sparse_kernel_autotuned = autotuner.AutotunedKernel(autotuned)
518
+ return _fwd_sparse_kernel_autotuned
519
+
520
+
521
+ def _flash_sparse_attn_forward(
522
+ query: torch.Tensor,
523
+ key: torch.Tensor,
524
+ value: torch.Tensor,
525
+ is_causal: bool = False,
526
+ softmax_scale: float = None,
527
+ query_scale: Optional[torch.Tensor] = None,
528
+ key_scale: Optional[torch.Tensor] = None,
529
+ value_scale: Optional[torch.Tensor] = None,
530
+ window_sizes: Optional[torch.Tensor] = None,
531
+ softmax_threshold: float = None,
532
+ is_local: bool = False,
533
+ is_quant: bool = False,
534
+ is_split_kv: bool = False,
535
+ pack_gqa: bool = False,
536
+ out: Optional[torch.Tensor] = None,
537
+ lse: Optional[torch.Tensor] = None,
538
+ is_autotune: bool = False,
539
+ skip_checks: bool = False,
540
+ ) -> Tuple[torch.Tensor, torch.Tensor, float, float]:
541
+ device = query.device
542
+ num_SMs = cache_utils.get_device_num_sms(device)
543
+ batch_size, seqlen_q, num_heads_q, head_dim = query.shape
544
+ _, seqlen_k, num_heads_kv, _ = key.shape
545
+ softmax_scale = (
546
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
547
+ )
548
+ softmax_threshold = (
549
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
550
+ )
551
+ qhead_per_kvhead = num_heads_q // num_heads_kv
552
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
553
+ if is_local and window_sizes is None:
554
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
555
+ elif not is_local:
556
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
557
+
558
+ if not skip_checks:
559
+ assert_inputs.assert_fwd_inputs(
560
+ query,
561
+ key,
562
+ value,
563
+ query_scale=query_scale,
564
+ key_scale=key_scale,
565
+ value_scale=value_scale,
566
+ window_sizes=window_sizes,
567
+ cu_seqlens_q=None,
568
+ cu_seqlens_k=None,
569
+ seqused_q=None,
570
+ seqused_k=None,
571
+ num_heads_q=num_heads_q,
572
+ num_heads_kv=num_heads_kv,
573
+ head_dim=head_dim,
574
+ is_quant=is_quant,
575
+ device=device,
576
+ )
577
+
578
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
579
+
580
+ _kernel_name = f"fwd_sparse{'_split' if is_split_kv else ''}"
581
+ launch_config = launch_template.load_launch_config(
582
+ device=device,
583
+ kernel_name=_kernel_name,
584
+ seqlen_q=seqlen_q,
585
+ seqlen_k=seqlen_k,
586
+ tile_k=TILE_K,
587
+ is_local=is_local,
588
+ qhead_per_kvhead=qhead_per_kvhead,
589
+ is_causal=is_causal,
590
+ pack_gqa=pack_gqa,
591
+ is_quant=is_quant,
592
+ )
593
+ if launch_config is not None and not is_autotune:
594
+ kernel = _fwd_sparse_kernel
595
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
596
+ else:
597
+ kernel = _get_autotuned_kernel()
598
+ # Placeholder for pre-launch computations
599
+ TILE_M = TILE_N = 64
600
+ num_warps = num_stages = num_ctas = None
601
+
602
+ num_splits = (
603
+ utils.num_splits_heuristic(
604
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
605
+ seqlen_k=seqlen_k,
606
+ num_SMs=num_SMs,
607
+ TILE_M=TILE_M,
608
+ TILE_N=TILE_N,
609
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
610
+ window_sizes, TILE_N
611
+ )
612
+ if is_local
613
+ else None,
614
+ )
615
+ if is_split_kv
616
+ else 1
617
+ )
618
+
619
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
620
+ out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
621
+ lse = (
622
+ lse
623
+ if lse is not None
624
+ else torch.empty(
625
+ (batch_size, num_heads_q, seqlen_q),
626
+ dtype=torch.float32,
627
+ device=query.device,
628
+ )
629
+ )
630
+
631
+ if is_split_kv:
632
+ out_partial = torch.empty(
633
+ (num_splits, batch_size, seqlen_q, num_heads_q, head_dim),
634
+ dtype=torch.float32,
635
+ device=query.device,
636
+ )
637
+ lse_partial = torch.empty(
638
+ (num_splits, batch_size, num_heads_q, seqlen_q),
639
+ dtype=torch.float32,
640
+ device=query.device,
641
+ )
642
+
643
+ if not is_quant:
644
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
645
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
646
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
647
+
648
+ grid = launch_grid.get_fwd_grid(
649
+ batch_size=batch_size,
650
+ seqlen_q=seqlen_q,
651
+ num_heads_q=num_heads_q,
652
+ num_heads_kv=num_heads_kv,
653
+ pack_gqa=pack_gqa,
654
+ num_splits=num_splits,
655
+ )
656
+
657
+ triton.set_allocator(utils.alloc_fn)
658
+
659
+ kernel[grid](
660
+ query,
661
+ key,
662
+ value,
663
+ out if not is_split_kv else out_partial,
664
+ lse if not is_split_kv else lse_partial,
665
+ softmax_scale,
666
+ softmax_threshold,
667
+ query_scale,
668
+ key_scale,
669
+ value_scale,
670
+ window_sizes,
671
+ query.stride(0),
672
+ query.stride(-2),
673
+ query.stride(-3),
674
+ key.stride(0),
675
+ key.stride(-2),
676
+ key.stride(-3),
677
+ value.stride(0),
678
+ value.stride(-2),
679
+ value.stride(-3),
680
+ out.stride(0) if not is_split_kv else out_partial.stride(1),
681
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
682
+ out.stride(-3) if not is_split_kv else out_partial.stride(-3),
683
+ 0 if not is_split_kv else out_partial.stride(0),
684
+ lse.stride(0) if not is_split_kv else lse_partial.stride(1),
685
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
686
+ 0 if not is_split_kv else lse_partial.stride(0),
687
+ window_sizes.stride(0),
688
+ None,
689
+ None,
690
+ None,
691
+ None,
692
+ num_splits,
693
+ seqlen_q=seqlen_q,
694
+ seqlen_k=seqlen_k,
695
+ head_dim=head_dim,
696
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
697
+ SEQLEN_K_CACHE=seqlen_k // 1024,
698
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
699
+ PACK_GQA=pack_gqa,
700
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
701
+ TILE_M=TILE_M,
702
+ TILE_N=TILE_N,
703
+ TILE_K=TILE_K,
704
+ IS_CAUSAL=is_causal,
705
+ IS_LOCAL=is_local,
706
+ IS_SPLIT_KV=is_split_kv,
707
+ HAS_CU_SEQLENS_Q=False,
708
+ HAS_CU_SEQLENS_K=False,
709
+ HAS_SEQUSED_Q=False,
710
+ HAS_SEQUSED_K=False,
711
+ num_warps=num_warps,
712
+ num_stages=num_stages,
713
+ num_ctas=num_ctas,
714
+ )
715
+
716
+ if launch_config is None or is_autotune:
717
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
718
+ if best is not None:
719
+ launch_template.store_launch_config(
720
+ device=device,
721
+ kernel_name=_kernel_name,
722
+ seqlen_q=seqlen_q,
723
+ seqlen_k=seqlen_k,
724
+ tile_k=TILE_K,
725
+ config=best,
726
+ is_local=is_local,
727
+ qhead_per_kvhead=qhead_per_kvhead,
728
+ is_causal=is_causal,
729
+ pack_gqa=pack_gqa,
730
+ is_quant=is_quant,
731
+ )
732
+
733
+ if is_split_kv:
734
+ flash_fwd_combine._flash_attn_fwd_combine(
735
+ out_partial,
736
+ lse_partial,
737
+ out,
738
+ lse,
739
+ )
740
+
741
+ return out, lse, softmax_scale, softmax_threshold
742
+
743
+
744
+ def _flash_sparse_attn_varlen_forward(
745
+ query: torch.Tensor,
746
+ key: torch.Tensor,
747
+ value: torch.Tensor,
748
+ cu_seqlens_q: torch.Tensor,
749
+ cu_seqlens_k: torch.Tensor,
750
+ max_seqlen_q: int,
751
+ max_seqlen_k: int,
752
+ is_causal: bool = False,
753
+ softmax_scale: float = None,
754
+ query_scale: Optional[torch.Tensor] = None,
755
+ key_scale: Optional[torch.Tensor] = None,
756
+ value_scale: Optional[torch.Tensor] = None,
757
+ window_sizes: Optional[torch.Tensor] = None,
758
+ softmax_threshold: float = None,
759
+ is_local: bool = False,
760
+ is_quant: bool = False,
761
+ is_split_kv: bool = False,
762
+ pack_gqa: bool = False,
763
+ seqused_q: Optional[torch.Tensor] = None,
764
+ seqused_k: Optional[torch.Tensor] = None,
765
+ out: Optional[torch.Tensor] = None,
766
+ lse: Optional[torch.Tensor] = None,
767
+ is_autotune: bool = False,
768
+ skip_checks: bool = False,
769
+ ) -> Tuple[torch.Tensor, torch.Tensor, float, float]:
770
+ device = query.device
771
+ num_SMs = cache_utils.get_device_num_sms(device)
772
+ total_seqlen_q, num_heads_q, head_dim = query.shape
773
+ _, num_heads_kv, _ = key.shape
774
+ batch_size = cu_seqlens_q.shape[0] - 1
775
+ seqlen_q = max_seqlen_q
776
+ seqlen_k = max_seqlen_k
777
+ softmax_scale = (
778
+ softmax_scale if softmax_scale is not None else 1.0 / (head_dim**0.5)
779
+ )
780
+ softmax_threshold = (
781
+ softmax_threshold if softmax_threshold is not None else 1 / seqlen_k
782
+ )
783
+ qhead_per_kvhead = num_heads_q // num_heads_kv
784
+ qhead_per_kvhead_packgqa = num_heads_q // num_heads_kv if pack_gqa else 1
785
+ if is_local and window_sizes is None:
786
+ window_sizes = utils.window_sizes_heuristic(seqlen_k, num_heads_kv, device)
787
+ elif not is_local:
788
+ window_sizes = torch.zeros((num_heads_kv, 4), dtype=torch.int32, device=device)
789
+
790
+ if not skip_checks:
791
+ assert_inputs.assert_fwd_inputs(
792
+ query,
793
+ key,
794
+ value,
795
+ query_scale=query_scale,
796
+ key_scale=key_scale,
797
+ value_scale=value_scale,
798
+ window_sizes=window_sizes,
799
+ cu_seqlens_q=cu_seqlens_q,
800
+ cu_seqlens_k=cu_seqlens_k,
801
+ seqused_q=seqused_q,
802
+ seqused_k=seqused_k,
803
+ num_heads_q=num_heads_q,
804
+ num_heads_kv=num_heads_kv,
805
+ head_dim=head_dim,
806
+ is_quant=is_quant,
807
+ device=device,
808
+ )
809
+
810
+ TILE_K = max(triton.next_power_of_2(head_dim), 16)
811
+
812
+ _kernel_name = f"fwd_sparse{'_split' if is_split_kv else ''}"
813
+ launch_config = launch_template.load_launch_config(
814
+ device=device,
815
+ kernel_name=_kernel_name,
816
+ seqlen_q=seqlen_q,
817
+ seqlen_k=seqlen_k,
818
+ tile_k=TILE_K,
819
+ is_local=is_local,
820
+ qhead_per_kvhead=qhead_per_kvhead,
821
+ is_causal=is_causal,
822
+ pack_gqa=pack_gqa,
823
+ is_quant=is_quant,
824
+ )
825
+ if launch_config is not None and not is_autotune:
826
+ kernel = _fwd_sparse_kernel
827
+ TILE_M, TILE_N, num_warps, num_stages, num_ctas = launch_config
828
+ else:
829
+ kernel = _get_autotuned_kernel()
830
+ # Placeholder for pre-launch computations
831
+ TILE_M = TILE_N = 64
832
+ num_warps = num_stages = num_ctas = None
833
+
834
+ num_splits = (
835
+ utils.num_splits_heuristic(
836
+ seqlen_q=seqlen_q * qhead_per_kvhead_packgqa,
837
+ seqlen_k=seqlen_k,
838
+ num_SMs=num_SMs,
839
+ TILE_M=TILE_M,
840
+ TILE_N=TILE_N,
841
+ max_split_blocks=utils.max_split_blocks_from_window_sizes(
842
+ window_sizes, TILE_N
843
+ )
844
+ if is_local
845
+ else None,
846
+ )
847
+ if is_split_kv
848
+ else 1
849
+ )
850
+
851
+ out_dtype = torch.bfloat16 if is_quant else query.dtype
852
+ out = out if out is not None else torch.empty_like(query, dtype=out_dtype)
853
+ lse = (
854
+ lse
855
+ if lse is not None
856
+ else torch.empty(
857
+ (num_heads_q, total_seqlen_q),
858
+ dtype=torch.float32,
859
+ device=query.device,
860
+ )
861
+ )
862
+
863
+ if is_split_kv:
864
+ out_partial = torch.empty(
865
+ (num_splits, total_seqlen_q, num_heads_q, head_dim),
866
+ dtype=torch.float32,
867
+ device=query.device,
868
+ )
869
+ lse_partial = torch.empty(
870
+ (num_splits, num_heads_q, total_seqlen_q),
871
+ dtype=torch.float32,
872
+ device=query.device,
873
+ )
874
+
875
+ if not is_quant:
876
+ query_scale = torch.ones(1, device=device, dtype=query.dtype)
877
+ key_scale = torch.ones(1, device=device, dtype=query.dtype)
878
+ value_scale = torch.ones(1, device=device, dtype=query.dtype)
879
+
880
+ grid = launch_grid.get_fwd_grid(
881
+ batch_size=batch_size,
882
+ seqlen_q=seqlen_q,
883
+ num_heads_q=num_heads_q,
884
+ num_heads_kv=num_heads_kv,
885
+ pack_gqa=pack_gqa,
886
+ num_splits=num_splits,
887
+ )
888
+
889
+ triton.set_allocator(utils.alloc_fn)
890
+
891
+ kernel[grid](
892
+ query,
893
+ key,
894
+ value,
895
+ out if not is_split_kv else out_partial,
896
+ lse if not is_split_kv else lse_partial,
897
+ softmax_scale,
898
+ softmax_threshold,
899
+ query_scale,
900
+ key_scale,
901
+ value_scale,
902
+ window_sizes,
903
+ 0,
904
+ query.stride(-2),
905
+ query.stride(0),
906
+ 0,
907
+ key.stride(-2),
908
+ key.stride(0),
909
+ 0,
910
+ value.stride(-2),
911
+ value.stride(0),
912
+ 0,
913
+ out.stride(-2) if not is_split_kv else out_partial.stride(-2),
914
+ out.stride(0) if not is_split_kv else out_partial.stride(-3),
915
+ 0 if not is_split_kv else out_partial.stride(0),
916
+ 0,
917
+ lse.stride(-2) if not is_split_kv else lse_partial.stride(-2),
918
+ 0 if not is_split_kv else lse_partial.stride(0),
919
+ window_sizes.stride(0),
920
+ cu_seqlens_q,
921
+ cu_seqlens_k,
922
+ seqused_q,
923
+ seqused_k,
924
+ num_splits,
925
+ seqlen_q=seqlen_q,
926
+ seqlen_k=seqlen_k,
927
+ head_dim=head_dim,
928
+ SEQLEN_Q_CACHE=seqlen_q // 1024,
929
+ SEQLEN_K_CACHE=seqlen_k // 1024,
930
+ QHEAD_PER_KVHEAD=qhead_per_kvhead,
931
+ PACK_GQA=pack_gqa,
932
+ QHEAD_PER_KVHEAD_PACKGQA=qhead_per_kvhead_packgqa,
933
+ TILE_M=TILE_M,
934
+ TILE_N=TILE_N,
935
+ TILE_K=TILE_K,
936
+ IS_CAUSAL=is_causal,
937
+ IS_LOCAL=is_local,
938
+ IS_SPLIT_KV=is_split_kv,
939
+ HAS_CU_SEQLENS_Q=True,
940
+ HAS_CU_SEQLENS_K=True,
941
+ HAS_SEQUSED_Q=seqused_q is not None,
942
+ HAS_SEQUSED_K=seqused_k is not None,
943
+ num_warps=num_warps,
944
+ num_stages=num_stages,
945
+ num_ctas=num_ctas,
946
+ )
947
+
948
+ if launch_config is None or is_autotune:
949
+ best = launch_template.extract_best_config(_get_autotuned_kernel())
950
+ if best is not None:
951
+ launch_template.store_launch_config(
952
+ device=device,
953
+ kernel_name=_kernel_name,
954
+ seqlen_q=seqlen_q,
955
+ seqlen_k=seqlen_k,
956
+ tile_k=TILE_K,
957
+ config=best,
958
+ is_local=is_local,
959
+ qhead_per_kvhead=qhead_per_kvhead,
960
+ is_causal=is_causal,
961
+ pack_gqa=pack_gqa,
962
+ is_quant=is_quant,
963
+ )
964
+
965
+ if is_split_kv:
966
+ flash_fwd_combine._flash_attn_fwd_combine(
967
+ out_partial,
968
+ lse_partial,
969
+ out,
970
+ lse,
971
+ cu_seqlens_q=cu_seqlens_q,
972
+ )
973
+
974
+ return out, lse, softmax_scale, softmax_threshold
build/torch-cuda/interface.py ADDED
@@ -0,0 +1,1912 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple, Union
2
+
3
+ import torch
4
+
5
+ from .flash_dense_fwd import (
6
+ _flash_dense_attn_forward,
7
+ _flash_dense_attn_varlen_forward,
8
+ )
9
+ from .flash_dense_bwd import (
10
+ _flash_dense_attn_backward,
11
+ _flash_dense_attn_varlen_backward,
12
+ )
13
+ from .flash_dense_dec import (
14
+ _flash_dense_attn_decode,
15
+ _flash_dense_attn_varlen_decode,
16
+ )
17
+ from .flash_sparse_fwd import (
18
+ _flash_sparse_attn_forward,
19
+ _flash_sparse_attn_varlen_forward,
20
+ )
21
+ from .flash_sparse_bwd import (
22
+ _flash_sparse_attn_backward,
23
+ _flash_sparse_attn_varlen_backward,
24
+ )
25
+ from .flash_sparse_dec import (
26
+ _flash_sparse_attn_decode,
27
+ _flash_sparse_attn_varlen_decode,
28
+ )
29
+ from .flash_gated_fwd import (
30
+ _flash_gated_attn_forward,
31
+ _flash_gated_attn_varlen_forward,
32
+ )
33
+ from .flash_gated_bwd import (
34
+ _flash_gated_attn_backward,
35
+ _flash_gated_attn_varlen_backward,
36
+ )
37
+ from .flash_gated_dec import (
38
+ _flash_gated_attn_decode,
39
+ _flash_gated_attn_varlen_decode,
40
+ )
41
+ from .flash_proj_fwd import _flash_proj_forward
42
+ from .flash_proj_bwd import _flash_proj_backward
43
+ from .utils import ensure_contiguous
44
+ from . import quant
45
+
46
+
47
+ class FlashDenseAttnFunc(torch.autograd.Function):
48
+ @staticmethod
49
+ @ensure_contiguous
50
+ def forward(
51
+ ctx,
52
+ query: torch.Tensor,
53
+ key: torch.Tensor,
54
+ value: torch.Tensor,
55
+ is_causal: bool = False,
56
+ softmax_scale: Optional[float] = None,
57
+ query_scale: Optional[torch.Tensor] = None,
58
+ key_scale: Optional[torch.Tensor] = None,
59
+ value_scale: Optional[torch.Tensor] = None,
60
+ window_sizes: Optional[torch.Tensor] = None,
61
+ is_local: bool = False,
62
+ is_quant: bool = False,
63
+ is_split_kv: bool = False,
64
+ is_split_qo: bool = False,
65
+ pack_gqa: bool = False,
66
+ out: Optional[torch.Tensor] = None,
67
+ lse: Optional[torch.Tensor] = None,
68
+ is_autotune: bool = False,
69
+ skip_checks: bool = False,
70
+ return_lse: bool = False,
71
+ ):
72
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
73
+ is_causal = False if query.shape[1] == 1 else is_causal
74
+
75
+ if window_sizes is not None:
76
+ is_local = True
77
+
78
+ if is_quant and (
79
+ query_scale is None or key_scale is None or value_scale is None
80
+ ):
81
+ query, query_scale = quant.quantize_fp8(query)
82
+ key, key_scale = quant.quantize_fp8(key)
83
+ value, value_scale = quant.quantize_fp8(value)
84
+
85
+ out, lse, softmax_scale = _flash_dense_attn_forward(
86
+ query=query,
87
+ key=key,
88
+ value=value,
89
+ is_causal=is_causal,
90
+ softmax_scale=softmax_scale,
91
+ query_scale=query_scale,
92
+ key_scale=key_scale,
93
+ value_scale=value_scale,
94
+ window_sizes=window_sizes,
95
+ is_local=is_local,
96
+ is_quant=is_quant,
97
+ is_split_kv=is_split_kv,
98
+ pack_gqa=pack_gqa,
99
+ out=out,
100
+ lse=lse,
101
+ is_autotune=is_autotune,
102
+ skip_checks=skip_checks,
103
+ )
104
+
105
+ ctx.save_for_backward(query, key, value, out, lse)
106
+ ctx.is_causal = is_causal
107
+ ctx.softmax_scale = softmax_scale
108
+ ctx.query_scale = query_scale
109
+ ctx.key_scale = key_scale
110
+ ctx.value_scale = value_scale
111
+ ctx.window_sizes = window_sizes
112
+ ctx.is_local = is_local
113
+ ctx.is_quant = is_quant
114
+ ctx.is_split_qo = is_split_qo
115
+ ctx.is_autotune = is_autotune
116
+ ctx.skip_checks = skip_checks
117
+
118
+ if return_lse:
119
+ # LSE gradient is not supported yet
120
+ ctx.mark_non_differentiable(lse)
121
+ return out, lse
122
+ return out
123
+
124
+ @staticmethod
125
+ @ensure_contiguous
126
+ def backward(ctx, dout: torch.Tensor, *args):
127
+ query, key, value, out, lse = ctx.saved_tensors
128
+
129
+ dq, dk, dv = _flash_dense_attn_backward(
130
+ query=query,
131
+ key=key,
132
+ value=value,
133
+ out=out,
134
+ dout=dout,
135
+ lse=lse,
136
+ is_causal=ctx.is_causal,
137
+ softmax_scale=ctx.softmax_scale,
138
+ query_scale=ctx.query_scale,
139
+ key_scale=ctx.key_scale,
140
+ value_scale=ctx.value_scale,
141
+ window_sizes=ctx.window_sizes,
142
+ is_local=ctx.is_local,
143
+ is_quant=ctx.is_quant,
144
+ is_split_qo=ctx.is_split_qo,
145
+ is_autotune=ctx.is_autotune,
146
+ skip_checks=ctx.skip_checks,
147
+ )
148
+
149
+ return dq, dk, dv, *((None,) * 30)
150
+
151
+
152
+ class FlashDenseAttnVarlenFunc(torch.autograd.Function):
153
+ @staticmethod
154
+ @ensure_contiguous
155
+ def forward(
156
+ ctx,
157
+ query: torch.Tensor,
158
+ key: torch.Tensor,
159
+ value: torch.Tensor,
160
+ cu_seqlens_q: torch.Tensor,
161
+ cu_seqlens_k: torch.Tensor,
162
+ max_seqlen_q: int,
163
+ max_seqlen_k: int,
164
+ is_causal: bool = False,
165
+ softmax_scale: Optional[float] = None,
166
+ query_scale: Optional[torch.Tensor] = None,
167
+ key_scale: Optional[torch.Tensor] = None,
168
+ value_scale: Optional[torch.Tensor] = None,
169
+ window_sizes: Optional[torch.Tensor] = None,
170
+ is_local: bool = False,
171
+ is_quant: bool = False,
172
+ is_split_kv: bool = False,
173
+ is_split_qo: bool = False,
174
+ pack_gqa: bool = False,
175
+ seqused_q: Optional[torch.Tensor] = None,
176
+ seqused_k: Optional[torch.Tensor] = None,
177
+ out: Optional[torch.Tensor] = None,
178
+ lse: Optional[torch.Tensor] = None,
179
+ is_autotune: bool = False,
180
+ skip_checks: bool = False,
181
+ return_lse: bool = False,
182
+ ):
183
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
184
+ is_causal = False if max_seqlen_q == 1 else is_causal
185
+
186
+ if window_sizes is not None:
187
+ is_local = True
188
+
189
+ if is_quant and (
190
+ query_scale is None or key_scale is None or value_scale is None
191
+ ):
192
+ query, query_scale = quant.quantize_fp8(query)
193
+ key, key_scale = quant.quantize_fp8(key)
194
+ value, value_scale = quant.quantize_fp8(value)
195
+
196
+ out, lse, softmax_scale = _flash_dense_attn_varlen_forward(
197
+ query=query,
198
+ key=key,
199
+ value=value,
200
+ cu_seqlens_q=cu_seqlens_q,
201
+ cu_seqlens_k=cu_seqlens_k,
202
+ max_seqlen_q=max_seqlen_q,
203
+ max_seqlen_k=max_seqlen_k,
204
+ is_causal=is_causal,
205
+ softmax_scale=softmax_scale,
206
+ query_scale=query_scale,
207
+ key_scale=key_scale,
208
+ value_scale=value_scale,
209
+ window_sizes=window_sizes,
210
+ is_local=is_local,
211
+ is_quant=is_quant,
212
+ is_split_kv=is_split_kv,
213
+ pack_gqa=pack_gqa,
214
+ seqused_q=seqused_q,
215
+ seqused_k=seqused_k,
216
+ out=out,
217
+ lse=lse,
218
+ is_autotune=is_autotune,
219
+ skip_checks=skip_checks,
220
+ )
221
+
222
+ ctx.save_for_backward(
223
+ query,
224
+ key,
225
+ value,
226
+ out,
227
+ lse,
228
+ cu_seqlens_q,
229
+ cu_seqlens_k,
230
+ seqused_q,
231
+ seqused_k,
232
+ )
233
+ ctx.max_seqlen_q = max_seqlen_q
234
+ ctx.max_seqlen_k = max_seqlen_k
235
+ ctx.is_causal = is_causal
236
+ ctx.softmax_scale = softmax_scale
237
+ ctx.query_scale = query_scale
238
+ ctx.key_scale = key_scale
239
+ ctx.value_scale = value_scale
240
+ ctx.window_sizes = window_sizes
241
+ ctx.is_local = is_local
242
+ ctx.is_quant = is_quant
243
+ ctx.is_split_qo = is_split_qo
244
+ ctx.is_autotune = is_autotune
245
+ ctx.skip_checks = skip_checks
246
+
247
+ if return_lse:
248
+ # LSE gradient is not supported yet
249
+ ctx.mark_non_differentiable(lse)
250
+ return out, lse
251
+ return out
252
+
253
+ @staticmethod
254
+ @ensure_contiguous
255
+ def backward(ctx, dout: torch.Tensor, *args):
256
+ (
257
+ query,
258
+ key,
259
+ value,
260
+ out,
261
+ lse,
262
+ cu_seqlens_q,
263
+ cu_seqlens_k,
264
+ seqused_q,
265
+ seqused_k,
266
+ ) = ctx.saved_tensors
267
+
268
+ dq, dk, dv = _flash_dense_attn_varlen_backward(
269
+ query=query,
270
+ key=key,
271
+ value=value,
272
+ out=out,
273
+ dout=dout,
274
+ lse=lse,
275
+ cu_seqlens_q=cu_seqlens_q,
276
+ cu_seqlens_k=cu_seqlens_k,
277
+ max_seqlen_q=ctx.max_seqlen_q,
278
+ max_seqlen_k=ctx.max_seqlen_k,
279
+ is_causal=ctx.is_causal,
280
+ softmax_scale=ctx.softmax_scale,
281
+ query_scale=ctx.query_scale,
282
+ key_scale=ctx.key_scale,
283
+ value_scale=ctx.value_scale,
284
+ window_sizes=ctx.window_sizes,
285
+ is_local=ctx.is_local,
286
+ is_quant=ctx.is_quant,
287
+ is_split_qo=ctx.is_split_qo,
288
+ seqused_q=seqused_q,
289
+ seqused_k=seqused_k,
290
+ is_autotune=ctx.is_autotune,
291
+ skip_checks=ctx.skip_checks,
292
+ )
293
+
294
+ return dq, dk, dv, *((None,) * 30)
295
+
296
+
297
+ class FlashSparseAttnFunc(torch.autograd.Function):
298
+ @staticmethod
299
+ @ensure_contiguous
300
+ def forward(
301
+ ctx,
302
+ query: torch.Tensor,
303
+ key: torch.Tensor,
304
+ value: torch.Tensor,
305
+ is_causal: bool = False,
306
+ softmax_scale: Optional[float] = None,
307
+ query_scale: Optional[torch.Tensor] = None,
308
+ key_scale: Optional[torch.Tensor] = None,
309
+ value_scale: Optional[torch.Tensor] = None,
310
+ window_sizes: Optional[torch.Tensor] = None,
311
+ softmax_threshold: Optional[float] = None,
312
+ is_local: bool = False,
313
+ is_quant: bool = False,
314
+ is_split_kv: bool = False,
315
+ is_split_qo: bool = False,
316
+ pack_gqa: bool = False,
317
+ out: Optional[torch.Tensor] = None,
318
+ lse: Optional[torch.Tensor] = None,
319
+ is_autotune: bool = False,
320
+ skip_checks: bool = False,
321
+ return_lse: bool = False,
322
+ ):
323
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
324
+ is_causal = False if query.shape[1] == 1 else is_causal
325
+
326
+ if window_sizes is not None:
327
+ is_local = True
328
+
329
+ if is_quant and (
330
+ query_scale is None or key_scale is None or value_scale is None
331
+ ):
332
+ query, query_scale = quant.quantize_fp8(query)
333
+ key, key_scale = quant.quantize_fp8(key)
334
+ value, value_scale = quant.quantize_fp8(value)
335
+
336
+ out, lse, softmax_scale, softmax_threshold = _flash_sparse_attn_forward(
337
+ query=query,
338
+ key=key,
339
+ value=value,
340
+ is_causal=is_causal,
341
+ softmax_scale=softmax_scale,
342
+ query_scale=query_scale,
343
+ key_scale=key_scale,
344
+ value_scale=value_scale,
345
+ window_sizes=window_sizes,
346
+ softmax_threshold=softmax_threshold,
347
+ is_local=is_local,
348
+ is_quant=is_quant,
349
+ is_split_kv=is_split_kv,
350
+ pack_gqa=pack_gqa,
351
+ out=out,
352
+ lse=lse,
353
+ is_autotune=is_autotune,
354
+ skip_checks=skip_checks,
355
+ )
356
+
357
+ ctx.save_for_backward(query, key, value, out, lse)
358
+ ctx.is_causal = is_causal
359
+ ctx.softmax_scale = softmax_scale
360
+ ctx.query_scale = query_scale
361
+ ctx.key_scale = key_scale
362
+ ctx.value_scale = value_scale
363
+ ctx.window_sizes = window_sizes
364
+ ctx.softmax_threshold = softmax_threshold
365
+ ctx.is_local = is_local
366
+ ctx.is_quant = is_quant
367
+ ctx.is_split_qo = is_split_qo
368
+ ctx.is_autotune = is_autotune
369
+ ctx.skip_checks = skip_checks
370
+
371
+ if return_lse:
372
+ # LSE gradient is not supported yet
373
+ ctx.mark_non_differentiable(lse)
374
+ return out, lse
375
+ return out
376
+
377
+ @staticmethod
378
+ @ensure_contiguous
379
+ def backward(ctx, dout: torch.Tensor, *args):
380
+ query, key, value, out, lse = ctx.saved_tensors
381
+
382
+ dq, dk, dv = _flash_sparse_attn_backward(
383
+ query=query,
384
+ key=key,
385
+ value=value,
386
+ out=out,
387
+ dout=dout,
388
+ lse=lse,
389
+ is_causal=ctx.is_causal,
390
+ softmax_scale=ctx.softmax_scale,
391
+ query_scale=ctx.query_scale,
392
+ key_scale=ctx.key_scale,
393
+ value_scale=ctx.value_scale,
394
+ window_sizes=ctx.window_sizes,
395
+ softmax_threshold=ctx.softmax_threshold,
396
+ is_local=ctx.is_local,
397
+ is_quant=ctx.is_quant,
398
+ is_split_qo=ctx.is_split_qo,
399
+ is_autotune=ctx.is_autotune,
400
+ skip_checks=ctx.skip_checks,
401
+ )
402
+
403
+ return dq, dk, dv, *((None,) * 30)
404
+
405
+
406
+ class FlashSparseAttnVarlenFunc(torch.autograd.Function):
407
+ @staticmethod
408
+ @ensure_contiguous
409
+ def forward(
410
+ ctx,
411
+ query: torch.Tensor,
412
+ key: torch.Tensor,
413
+ value: torch.Tensor,
414
+ cu_seqlens_q: torch.Tensor,
415
+ cu_seqlens_k: torch.Tensor,
416
+ max_seqlen_q: int,
417
+ max_seqlen_k: int,
418
+ is_causal: bool = False,
419
+ softmax_scale: Optional[float] = None,
420
+ query_scale: Optional[torch.Tensor] = None,
421
+ key_scale: Optional[torch.Tensor] = None,
422
+ value_scale: Optional[torch.Tensor] = None,
423
+ window_sizes: Optional[torch.Tensor] = None,
424
+ softmax_threshold: Optional[float] = None,
425
+ is_local: bool = False,
426
+ is_quant: bool = False,
427
+ is_split_kv: bool = False,
428
+ is_split_qo: bool = False,
429
+ pack_gqa: bool = False,
430
+ seqused_q: Optional[torch.Tensor] = None,
431
+ seqused_k: Optional[torch.Tensor] = None,
432
+ out: Optional[torch.Tensor] = None,
433
+ lse: Optional[torch.Tensor] = None,
434
+ is_autotune: bool = False,
435
+ skip_checks: bool = False,
436
+ return_lse: bool = False,
437
+ ):
438
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
439
+ is_causal = False if max_seqlen_q == 1 else is_causal
440
+
441
+ if window_sizes is not None:
442
+ is_local = True
443
+
444
+ if is_quant and (
445
+ query_scale is None or key_scale is None or value_scale is None
446
+ ):
447
+ query, query_scale = quant.quantize_fp8(query)
448
+ key, key_scale = quant.quantize_fp8(key)
449
+ value, value_scale = quant.quantize_fp8(value)
450
+
451
+ out, lse, softmax_scale, softmax_threshold = _flash_sparse_attn_varlen_forward(
452
+ query=query,
453
+ key=key,
454
+ value=value,
455
+ cu_seqlens_q=cu_seqlens_q,
456
+ cu_seqlens_k=cu_seqlens_k,
457
+ max_seqlen_q=max_seqlen_q,
458
+ max_seqlen_k=max_seqlen_k,
459
+ is_causal=is_causal,
460
+ softmax_scale=softmax_scale,
461
+ query_scale=query_scale,
462
+ key_scale=key_scale,
463
+ value_scale=value_scale,
464
+ window_sizes=window_sizes,
465
+ softmax_threshold=softmax_threshold,
466
+ is_local=is_local,
467
+ is_quant=is_quant,
468
+ is_split_kv=is_split_kv,
469
+ pack_gqa=pack_gqa,
470
+ seqused_q=seqused_q,
471
+ seqused_k=seqused_k,
472
+ out=out,
473
+ lse=lse,
474
+ is_autotune=is_autotune,
475
+ skip_checks=skip_checks,
476
+ )
477
+
478
+ ctx.save_for_backward(
479
+ query,
480
+ key,
481
+ value,
482
+ out,
483
+ lse,
484
+ cu_seqlens_q,
485
+ cu_seqlens_k,
486
+ seqused_q,
487
+ seqused_k,
488
+ )
489
+ ctx.max_seqlen_q = max_seqlen_q
490
+ ctx.max_seqlen_k = max_seqlen_k
491
+ ctx.is_causal = is_causal
492
+ ctx.softmax_scale = softmax_scale
493
+ ctx.query_scale = query_scale
494
+ ctx.key_scale = key_scale
495
+ ctx.value_scale = value_scale
496
+ ctx.window_sizes = window_sizes
497
+ ctx.softmax_threshold = softmax_threshold
498
+ ctx.is_local = is_local
499
+ ctx.is_quant = is_quant
500
+ ctx.is_split_qo = is_split_qo
501
+ ctx.is_autotune = is_autotune
502
+ ctx.skip_checks = skip_checks
503
+
504
+ if return_lse:
505
+ # LSE gradient is not supported yet
506
+ ctx.mark_non_differentiable(lse)
507
+ return out, lse
508
+ return out
509
+
510
+ @staticmethod
511
+ @ensure_contiguous
512
+ def backward(ctx, dout: torch.Tensor, *args):
513
+ (
514
+ query,
515
+ key,
516
+ value,
517
+ out,
518
+ lse,
519
+ cu_seqlens_q,
520
+ cu_seqlens_k,
521
+ seqused_q,
522
+ seqused_k,
523
+ ) = ctx.saved_tensors
524
+
525
+ dq, dk, dv = _flash_sparse_attn_varlen_backward(
526
+ query=query,
527
+ key=key,
528
+ value=value,
529
+ out=out,
530
+ dout=dout,
531
+ lse=lse,
532
+ cu_seqlens_q=cu_seqlens_q,
533
+ cu_seqlens_k=cu_seqlens_k,
534
+ max_seqlen_q=ctx.max_seqlen_q,
535
+ max_seqlen_k=ctx.max_seqlen_k,
536
+ is_causal=ctx.is_causal,
537
+ softmax_scale=ctx.softmax_scale,
538
+ query_scale=ctx.query_scale,
539
+ key_scale=ctx.key_scale,
540
+ value_scale=ctx.value_scale,
541
+ window_sizes=ctx.window_sizes,
542
+ softmax_threshold=ctx.softmax_threshold,
543
+ is_local=ctx.is_local,
544
+ is_quant=ctx.is_quant,
545
+ seqused_q=seqused_q,
546
+ seqused_k=seqused_k,
547
+ is_split_qo=ctx.is_split_qo,
548
+ is_autotune=ctx.is_autotune,
549
+ skip_checks=ctx.skip_checks,
550
+ )
551
+
552
+ return dq, dk, dv, *((None,) * 30)
553
+
554
+
555
+ class FlashGatedAttnFunc(torch.autograd.Function):
556
+ @staticmethod
557
+ @ensure_contiguous
558
+ def forward(
559
+ ctx,
560
+ query: torch.Tensor,
561
+ key: torch.Tensor,
562
+ value: torch.Tensor,
563
+ alpha: torch.Tensor,
564
+ delta: torch.Tensor,
565
+ is_causal: bool = False,
566
+ softmax_scale: Optional[float] = None,
567
+ query_scale: Optional[torch.Tensor] = None,
568
+ key_scale: Optional[torch.Tensor] = None,
569
+ value_scale: Optional[torch.Tensor] = None,
570
+ window_sizes: Optional[torch.Tensor] = None,
571
+ softmax_threshold: Optional[float] = None,
572
+ gate_threshold: Optional[float] = None,
573
+ is_logsigmoid_gate: bool = True,
574
+ is_adapt_gate: bool = True,
575
+ is_local: bool = False,
576
+ is_quant: bool = False,
577
+ is_split_kv: bool = False,
578
+ is_split_qo: bool = False,
579
+ pack_gqa: bool = False,
580
+ out: Optional[torch.Tensor] = None,
581
+ lse: Optional[torch.Tensor] = None,
582
+ is_autotune: bool = False,
583
+ skip_checks: bool = False,
584
+ return_lse: bool = False,
585
+ ):
586
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
587
+ is_causal = False if query.shape[1] == 1 else is_causal
588
+
589
+ if window_sizes is not None:
590
+ is_local = True
591
+
592
+ if is_quant and (
593
+ query_scale is None or key_scale is None or value_scale is None
594
+ ):
595
+ query, query_scale = quant.quantize_fp8(query)
596
+ key, key_scale = quant.quantize_fp8(key)
597
+ value, value_scale = quant.quantize_fp8(value)
598
+
599
+ # Transpose for make_tensor_descriptor
600
+ alpha = alpha.transpose(-2, -1).contiguous()
601
+ delta = delta.transpose(-2, -1).contiguous()
602
+
603
+ out, lse, softmax_scale, softmax_threshold, gate_threshold = (
604
+ _flash_gated_attn_forward(
605
+ query=query,
606
+ key=key,
607
+ value=value,
608
+ alpha=alpha,
609
+ delta=delta,
610
+ is_causal=is_causal,
611
+ softmax_scale=softmax_scale,
612
+ query_scale=query_scale,
613
+ key_scale=key_scale,
614
+ value_scale=value_scale,
615
+ window_sizes=window_sizes,
616
+ softmax_threshold=softmax_threshold,
617
+ gate_threshold=gate_threshold,
618
+ is_logsigmoid_gate=is_logsigmoid_gate,
619
+ is_adapt_gate=is_adapt_gate,
620
+ is_local=is_local,
621
+ is_quant=is_quant,
622
+ is_split_kv=is_split_kv,
623
+ pack_gqa=pack_gqa,
624
+ out=out,
625
+ lse=lse,
626
+ is_autotune=is_autotune,
627
+ skip_checks=skip_checks,
628
+ )
629
+ )
630
+
631
+ ctx.save_for_backward(query, key, value, alpha, delta, out, lse)
632
+ ctx.is_causal = is_causal
633
+ ctx.softmax_scale = softmax_scale
634
+ ctx.query_scale = query_scale
635
+ ctx.key_scale = key_scale
636
+ ctx.value_scale = value_scale
637
+ ctx.window_sizes = window_sizes
638
+ ctx.softmax_threshold = softmax_threshold
639
+ ctx.gate_threshold = gate_threshold
640
+ ctx.is_logsigmoid_gate = is_logsigmoid_gate
641
+ ctx.is_adapt_gate = is_adapt_gate
642
+ ctx.is_local = is_local
643
+ ctx.is_quant = is_quant
644
+ ctx.is_split_qo = is_split_qo
645
+ ctx.is_autotune = is_autotune
646
+ ctx.skip_checks = skip_checks
647
+
648
+ if return_lse:
649
+ # LSE gradient is not supported yet
650
+ ctx.mark_non_differentiable(lse)
651
+ return out, lse
652
+ return out
653
+
654
+ @staticmethod
655
+ @ensure_contiguous
656
+ def backward(ctx, dout: torch.Tensor, *args):
657
+ query, key, value, alpha, delta, out, lse = ctx.saved_tensors
658
+
659
+ dq, dk, dv, da, dd = _flash_gated_attn_backward(
660
+ query=query,
661
+ key=key,
662
+ value=value,
663
+ alpha=alpha,
664
+ delta=delta,
665
+ out=out,
666
+ dout=dout,
667
+ lse=lse,
668
+ is_causal=ctx.is_causal,
669
+ softmax_scale=ctx.softmax_scale,
670
+ query_scale=ctx.query_scale,
671
+ key_scale=ctx.key_scale,
672
+ value_scale=ctx.value_scale,
673
+ window_sizes=ctx.window_sizes,
674
+ softmax_threshold=ctx.softmax_threshold,
675
+ gate_threshold=ctx.gate_threshold,
676
+ is_logsigmoid_gate=ctx.is_logsigmoid_gate,
677
+ is_adapt_gate=ctx.is_adapt_gate,
678
+ is_local=ctx.is_local,
679
+ is_quant=ctx.is_quant,
680
+ is_split_qo=ctx.is_split_qo,
681
+ is_autotune=ctx.is_autotune,
682
+ skip_checks=ctx.skip_checks,
683
+ )
684
+
685
+ # Transpose back to original layout
686
+ da = da.transpose(-2, -1).contiguous()
687
+ dd = dd.transpose(-2, -1).contiguous()
688
+
689
+ return dq, dk, dv, da, dd, *((None,) * 30)
690
+
691
+
692
+ class FlashGatedAttnVarlenFunc(torch.autograd.Function):
693
+ @staticmethod
694
+ @ensure_contiguous
695
+ def forward(
696
+ ctx,
697
+ query: torch.Tensor,
698
+ key: torch.Tensor,
699
+ value: torch.Tensor,
700
+ alpha: torch.Tensor,
701
+ delta: torch.Tensor,
702
+ cu_seqlens_q: torch.Tensor,
703
+ cu_seqlens_k: torch.Tensor,
704
+ max_seqlen_q: int,
705
+ max_seqlen_k: int,
706
+ is_causal: bool = False,
707
+ softmax_scale: Optional[float] = None,
708
+ query_scale: Optional[torch.Tensor] = None,
709
+ key_scale: Optional[torch.Tensor] = None,
710
+ value_scale: Optional[torch.Tensor] = None,
711
+ window_sizes: Optional[torch.Tensor] = None,
712
+ softmax_threshold: Optional[float] = None,
713
+ gate_threshold: Optional[float] = None,
714
+ is_logsigmoid_gate: bool = True,
715
+ is_adapt_gate: bool = True,
716
+ is_local: bool = False,
717
+ is_quant: bool = False,
718
+ is_split_kv: bool = False,
719
+ is_split_qo: bool = False,
720
+ pack_gqa: bool = False,
721
+ seqused_q: Optional[torch.Tensor] = None,
722
+ seqused_k: Optional[torch.Tensor] = None,
723
+ out: Optional[torch.Tensor] = None,
724
+ lse: Optional[torch.Tensor] = None,
725
+ is_autotune: bool = False,
726
+ skip_checks: bool = False,
727
+ return_lse: bool = False,
728
+ ):
729
+ # Set is_causal to False if sequence length is 1 to avoid unnecessary masking overhead
730
+ is_causal = False if max_seqlen_q == 1 else is_causal
731
+
732
+ if window_sizes is not None:
733
+ is_local = True
734
+
735
+ if is_quant and (
736
+ query_scale is None or key_scale is None or value_scale is None
737
+ ):
738
+ query, query_scale = quant.quantize_fp8(query)
739
+ key, key_scale = quant.quantize_fp8(key)
740
+ value, value_scale = quant.quantize_fp8(value)
741
+
742
+ # Transpose for make_tensor_descriptor
743
+ alpha = alpha.transpose(-2, -1).contiguous()
744
+ delta = delta.transpose(-2, -1).contiguous()
745
+
746
+ out, lse, softmax_scale, softmax_threshold, gate_threshold = (
747
+ _flash_gated_attn_varlen_forward(
748
+ query=query,
749
+ key=key,
750
+ value=value,
751
+ alpha=alpha,
752
+ delta=delta,
753
+ cu_seqlens_q=cu_seqlens_q,
754
+ cu_seqlens_k=cu_seqlens_k,
755
+ max_seqlen_q=max_seqlen_q,
756
+ max_seqlen_k=max_seqlen_k,
757
+ is_causal=is_causal,
758
+ softmax_scale=softmax_scale,
759
+ query_scale=query_scale,
760
+ key_scale=key_scale,
761
+ value_scale=value_scale,
762
+ window_sizes=window_sizes,
763
+ softmax_threshold=softmax_threshold,
764
+ gate_threshold=gate_threshold,
765
+ is_logsigmoid_gate=is_logsigmoid_gate,
766
+ is_adapt_gate=is_adapt_gate,
767
+ is_local=is_local,
768
+ is_quant=is_quant,
769
+ is_split_kv=is_split_kv,
770
+ pack_gqa=pack_gqa,
771
+ seqused_q=seqused_q,
772
+ seqused_k=seqused_k,
773
+ out=out,
774
+ lse=lse,
775
+ is_autotune=is_autotune,
776
+ skip_checks=skip_checks,
777
+ )
778
+ )
779
+
780
+ ctx.save_for_backward(
781
+ query,
782
+ key,
783
+ value,
784
+ alpha,
785
+ delta,
786
+ out,
787
+ lse,
788
+ cu_seqlens_q,
789
+ cu_seqlens_k,
790
+ seqused_q,
791
+ seqused_k,
792
+ )
793
+ ctx.max_seqlen_q = max_seqlen_q
794
+ ctx.max_seqlen_k = max_seqlen_k
795
+ ctx.is_causal = is_causal
796
+ ctx.softmax_scale = softmax_scale
797
+ ctx.query_scale = query_scale
798
+ ctx.key_scale = key_scale
799
+ ctx.value_scale = value_scale
800
+ ctx.window_sizes = window_sizes
801
+ ctx.softmax_threshold = softmax_threshold
802
+ ctx.gate_threshold = gate_threshold
803
+ ctx.is_logsigmoid_gate = is_logsigmoid_gate
804
+ ctx.is_adapt_gate = is_adapt_gate
805
+ ctx.is_local = is_local
806
+ ctx.is_quant = is_quant
807
+ ctx.is_split_qo = is_split_qo
808
+ ctx.is_autotune = is_autotune
809
+ ctx.skip_checks = skip_checks
810
+
811
+ if return_lse:
812
+ # LSE gradient is not supported yet
813
+ ctx.mark_non_differentiable(lse)
814
+ return out, lse
815
+ return out
816
+
817
+ @staticmethod
818
+ @ensure_contiguous
819
+ def backward(ctx, dout: torch.Tensor, *args):
820
+ (
821
+ query,
822
+ key,
823
+ value,
824
+ alpha,
825
+ delta,
826
+ out,
827
+ lse,
828
+ cu_seqlens_q,
829
+ cu_seqlens_k,
830
+ seqused_q,
831
+ seqused_k,
832
+ ) = ctx.saved_tensors
833
+
834
+ dq, dk, dv, da, dd = _flash_gated_attn_varlen_backward(
835
+ query=query,
836
+ key=key,
837
+ value=value,
838
+ alpha=alpha,
839
+ delta=delta,
840
+ out=out,
841
+ dout=dout,
842
+ lse=lse,
843
+ softmax_scale=ctx.softmax_scale,
844
+ query_scale=ctx.query_scale,
845
+ key_scale=ctx.key_scale,
846
+ value_scale=ctx.value_scale,
847
+ window_sizes=ctx.window_sizes,
848
+ softmax_threshold=ctx.softmax_threshold,
849
+ cu_seqlens_q=cu_seqlens_q,
850
+ cu_seqlens_k=cu_seqlens_k,
851
+ max_seqlen_q=ctx.max_seqlen_q,
852
+ max_seqlen_k=ctx.max_seqlen_k,
853
+ is_causal=ctx.is_causal,
854
+ gate_threshold=ctx.gate_threshold,
855
+ is_logsigmoid_gate=ctx.is_logsigmoid_gate,
856
+ is_adapt_gate=ctx.is_adapt_gate,
857
+ is_local=ctx.is_local,
858
+ is_quant=ctx.is_quant,
859
+ seqused_q=seqused_q,
860
+ seqused_k=seqused_k,
861
+ is_split_qo=ctx.is_split_qo,
862
+ is_autotune=ctx.is_autotune,
863
+ skip_checks=ctx.skip_checks,
864
+ )
865
+
866
+ # Transpose back to original layout
867
+ da = da.transpose(-2, -1).contiguous()
868
+ dd = dd.transpose(-2, -1).contiguous()
869
+
870
+ return dq, dk, dv, da, dd, *((None,) * 30)
871
+
872
+
873
+ class FlashProjFunc(torch.autograd.Function):
874
+ @staticmethod
875
+ def forward(
876
+ ctx,
877
+ input: torch.Tensor,
878
+ weight: torch.Tensor,
879
+ out_dtype: torch.dtype = torch.float8_e4m3fn,
880
+ prev_scale: Optional[torch.Tensor] = None,
881
+ ):
882
+ out, descale = _flash_proj_forward(
883
+ input=input,
884
+ weight=weight,
885
+ out_dtype=out_dtype,
886
+ prev_scale=prev_scale,
887
+ )
888
+
889
+ ctx.save_for_backward(input, weight)
890
+
891
+ return out, descale
892
+
893
+ @staticmethod
894
+ def backward(ctx, dout: torch.Tensor, _dscale: torch.Tensor):
895
+ input, weight = ctx.saved_tensors
896
+ dout = dout.to(input.dtype)
897
+
898
+ dinput, dweight = _flash_proj_backward(dout=dout, input=input, weight=weight)
899
+
900
+ return dinput, dweight, None, None
901
+
902
+
903
+ def flash_dense_attn_func(
904
+ query: torch.Tensor,
905
+ key: torch.Tensor,
906
+ value: torch.Tensor,
907
+ is_causal: bool = False,
908
+ softmax_scale: Optional[float] = None,
909
+ query_scale: Optional[torch.Tensor] = None,
910
+ key_scale: Optional[torch.Tensor] = None,
911
+ value_scale: Optional[torch.Tensor] = None,
912
+ window_sizes: Optional[torch.Tensor] = None,
913
+ is_local: bool = False,
914
+ is_quant: bool = False,
915
+ is_split_kv: bool = False,
916
+ is_split_qo: bool = False,
917
+ pack_gqa: bool = False,
918
+ out: Optional[torch.Tensor] = None,
919
+ lse: Optional[torch.Tensor] = None,
920
+ is_autotune: bool = False,
921
+ skip_checks: bool = False,
922
+ return_lse: bool = False,
923
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
924
+ """
925
+ Flash dense attention function that computes the attention output and optionally the logsumexp.
926
+
927
+ :param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
928
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
929
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
930
+ :param is_causal: Whether to apply a causal mask.
931
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
932
+ :param query_scale: Optional per-tensor scale for query dequantization.
933
+ :param key_scale: Optional per-tensor scale for key dequantization.
934
+ :param value_scale: Optional per-tensor scale for value dequantization.
935
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
936
+ :param is_local: Whether to apply a local mask.
937
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
938
+ :param is_split_kv: Whether to enable split-KV for forward occupancy.
939
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
940
+ :param pack_gqa: Whether to pack grouped-query attention.
941
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
942
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
943
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
944
+ :param skip_checks: Whether to skip input validation checks for faster performance.
945
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
946
+
947
+ :returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
948
+ """
949
+ return FlashDenseAttnFunc.apply(
950
+ query,
951
+ key,
952
+ value,
953
+ is_causal,
954
+ softmax_scale,
955
+ query_scale,
956
+ key_scale,
957
+ value_scale,
958
+ window_sizes,
959
+ is_local,
960
+ is_quant,
961
+ is_split_kv,
962
+ is_split_qo,
963
+ pack_gqa,
964
+ out,
965
+ lse,
966
+ is_autotune,
967
+ skip_checks,
968
+ return_lse,
969
+ )
970
+
971
+
972
+ def flash_dense_attn_with_kvcache_func(
973
+ query: torch.Tensor,
974
+ key: torch.Tensor,
975
+ value: torch.Tensor,
976
+ softmax_scale: Optional[float] = None,
977
+ query_scale: Optional[torch.Tensor] = None,
978
+ key_scale: Optional[torch.Tensor] = None,
979
+ value_scale: Optional[torch.Tensor] = None,
980
+ window_sizes: Optional[torch.Tensor] = None,
981
+ is_local: bool = False,
982
+ is_quant: bool = False,
983
+ out: Optional[torch.Tensor] = None,
984
+ lse: Optional[torch.Tensor] = None,
985
+ is_autotune: bool = False,
986
+ skip_checks: bool = False,
987
+ return_lse: bool = False,
988
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
989
+ """
990
+ Flash dense attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
991
+
992
+ :param query: Query tensor of shape [batch_size, num_heads, head_dim].
993
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
994
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
995
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
996
+ :param query_scale: Optional per-tensor scale for query dequantization.
997
+ :param key_scale: Optional per-tensor scale for key dequantization.
998
+ :param value_scale: Optional per-tensor scale for value dequantization.
999
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1000
+ :param is_local: Whether to apply a local mask.
1001
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1002
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
1003
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
1004
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1005
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1006
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1007
+
1008
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
1009
+ """
1010
+
1011
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1012
+ query, query_scale = quant.quantize_fp8(query)
1013
+ key, key_scale = quant.quantize_fp8(key)
1014
+ value, value_scale = quant.quantize_fp8(value)
1015
+
1016
+ if window_sizes is not None:
1017
+ is_local = True
1018
+
1019
+ out, lse = _flash_dense_attn_decode(
1020
+ query=query,
1021
+ key=key,
1022
+ value=value,
1023
+ softmax_scale=softmax_scale,
1024
+ query_scale=query_scale,
1025
+ key_scale=key_scale,
1026
+ value_scale=value_scale,
1027
+ window_sizes=window_sizes,
1028
+ is_local=is_local,
1029
+ is_quant=is_quant,
1030
+ out=out,
1031
+ lse=lse,
1032
+ is_autotune=is_autotune,
1033
+ skip_checks=skip_checks,
1034
+ )
1035
+
1036
+ if return_lse:
1037
+ return out, lse
1038
+ return out
1039
+
1040
+
1041
+ def flash_dense_attn_varlen_func(
1042
+ query: torch.Tensor,
1043
+ key: torch.Tensor,
1044
+ value: torch.Tensor,
1045
+ cu_seqlens_q: torch.Tensor,
1046
+ cu_seqlens_k: torch.Tensor,
1047
+ max_seqlen_q: int,
1048
+ max_seqlen_k: int,
1049
+ is_causal: bool = False,
1050
+ softmax_scale: Optional[float] = None,
1051
+ query_scale: Optional[torch.Tensor] = None,
1052
+ key_scale: Optional[torch.Tensor] = None,
1053
+ value_scale: Optional[torch.Tensor] = None,
1054
+ window_sizes: Optional[torch.Tensor] = None,
1055
+ is_local: bool = False,
1056
+ is_quant: bool = False,
1057
+ is_split_kv: bool = False,
1058
+ is_split_qo: bool = False,
1059
+ pack_gqa: bool = False,
1060
+ seqused_q: Optional[torch.Tensor] = None,
1061
+ seqused_k: Optional[torch.Tensor] = None,
1062
+ out: Optional[torch.Tensor] = None,
1063
+ lse: Optional[torch.Tensor] = None,
1064
+ is_autotune: bool = False,
1065
+ skip_checks: bool = False,
1066
+ return_lse: bool = False,
1067
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1068
+ """
1069
+ Flash dense attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
1070
+
1071
+ :param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
1072
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1073
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1074
+ :param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
1075
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1076
+ :param max_seqlen_q: Maximum sequence length for queries.
1077
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1078
+ :param is_causal: Whether to apply a causal mask.
1079
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1080
+ :param query_scale: Optional per-tensor scale for query dequantization.
1081
+ :param key_scale: Optional per-tensor scale for key dequantization.
1082
+ :param value_scale: Optional per-tensor scale for value dequantization.
1083
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1084
+ :param is_local: Whether to apply a local mask.
1085
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
1086
+ :param is_split_kv: Whether to enable split-KV for forward occupancy.
1087
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
1088
+ :param pack_gqa: Whether to pack grouped-query attention.
1089
+ :param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
1090
+ :param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
1091
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
1092
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
1093
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1094
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1095
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1096
+
1097
+ :returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
1098
+ """
1099
+ return FlashDenseAttnVarlenFunc.apply(
1100
+ query,
1101
+ key,
1102
+ value,
1103
+ cu_seqlens_q,
1104
+ cu_seqlens_k,
1105
+ max_seqlen_q,
1106
+ max_seqlen_k,
1107
+ is_causal,
1108
+ softmax_scale,
1109
+ query_scale,
1110
+ key_scale,
1111
+ value_scale,
1112
+ window_sizes,
1113
+ is_local,
1114
+ is_quant,
1115
+ is_split_kv,
1116
+ is_split_qo,
1117
+ pack_gqa,
1118
+ seqused_q,
1119
+ seqused_k,
1120
+ out,
1121
+ lse,
1122
+ is_autotune,
1123
+ skip_checks,
1124
+ return_lse,
1125
+ )
1126
+
1127
+
1128
+ def flash_dense_attn_varlen_with_kvcache_func(
1129
+ query: torch.Tensor,
1130
+ key: torch.Tensor,
1131
+ value: torch.Tensor,
1132
+ cu_seqlens_k: torch.Tensor,
1133
+ max_seqlen_k: int,
1134
+ softmax_scale: Optional[float] = None,
1135
+ query_scale: Optional[torch.Tensor] = None,
1136
+ key_scale: Optional[torch.Tensor] = None,
1137
+ value_scale: Optional[torch.Tensor] = None,
1138
+ window_sizes: Optional[torch.Tensor] = None,
1139
+ is_local: bool = False,
1140
+ is_quant: bool = False,
1141
+ seqused_k: Optional[torch.Tensor] = None,
1142
+ out: Optional[torch.Tensor] = None,
1143
+ lse: Optional[torch.Tensor] = None,
1144
+ is_autotune: bool = False,
1145
+ skip_checks: bool = False,
1146
+ return_lse: bool = False,
1147
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1148
+ """
1149
+ Flash dense attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
1150
+
1151
+ :param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
1152
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1153
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1154
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1155
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1156
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1157
+ :param query_scale: Optional per-tensor scale for query dequantization.
1158
+ :param key_scale: Optional per-tensor scale for key dequantization.
1159
+ :param value_scale: Optional per-tensor scale for value dequantization.
1160
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1161
+ :param is_local: Whether to apply a local mask.
1162
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1163
+ :param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
1164
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
1165
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
1166
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1167
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1168
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1169
+
1170
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
1171
+ """
1172
+
1173
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1174
+ query, query_scale = quant.quantize_fp8(query)
1175
+ key, key_scale = quant.quantize_fp8(key)
1176
+ value, value_scale = quant.quantize_fp8(value)
1177
+
1178
+ if window_sizes is not None:
1179
+ is_local = True
1180
+
1181
+ out, lse = _flash_dense_attn_varlen_decode(
1182
+ query=query,
1183
+ key=key,
1184
+ value=value,
1185
+ cu_seqlens_k=cu_seqlens_k,
1186
+ max_seqlen_k=max_seqlen_k,
1187
+ softmax_scale=softmax_scale,
1188
+ query_scale=query_scale,
1189
+ key_scale=key_scale,
1190
+ value_scale=value_scale,
1191
+ window_sizes=window_sizes,
1192
+ is_local=is_local,
1193
+ is_quant=is_quant,
1194
+ seqused_k=seqused_k,
1195
+ out=out,
1196
+ lse=lse,
1197
+ is_autotune=is_autotune,
1198
+ skip_checks=skip_checks,
1199
+ )
1200
+
1201
+ if return_lse:
1202
+ return out, lse
1203
+ return out
1204
+
1205
+
1206
+ def flash_sparse_attn_func(
1207
+ query: torch.Tensor,
1208
+ key: torch.Tensor,
1209
+ value: torch.Tensor,
1210
+ is_causal: bool = False,
1211
+ softmax_scale: Optional[float] = None,
1212
+ query_scale: Optional[torch.Tensor] = None,
1213
+ key_scale: Optional[torch.Tensor] = None,
1214
+ value_scale: Optional[torch.Tensor] = None,
1215
+ window_sizes: Optional[torch.Tensor] = None,
1216
+ softmax_threshold: Optional[float] = None,
1217
+ is_local: bool = False,
1218
+ is_quant: bool = False,
1219
+ is_split_kv: bool = False,
1220
+ is_split_qo: bool = False,
1221
+ pack_gqa: bool = False,
1222
+ out: Optional[torch.Tensor] = None,
1223
+ lse: Optional[torch.Tensor] = None,
1224
+ is_autotune: bool = False,
1225
+ skip_checks: bool = False,
1226
+ return_lse: bool = False,
1227
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1228
+ """
1229
+ Flash sparse attention function that computes the attention output and optionally the logsumexp.
1230
+
1231
+ :param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
1232
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1233
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1234
+ :param is_causal: Whether to apply a causal mask.
1235
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1236
+ :param query_scale: Optional per-tensor scale for query dequantization.
1237
+ :param key_scale: Optional per-tensor scale for key dequantization.
1238
+ :param value_scale: Optional per-tensor scale for value dequantization.
1239
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1240
+ :param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / seqlen_k.
1241
+ :param is_local: Whether to apply a local mask.
1242
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
1243
+ :param is_split_kv: Whether to enable split-KV for forward ccupancy.
1244
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
1245
+ :param pack_gqa: Whether to pack grouped-query attention.
1246
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
1247
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
1248
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1249
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1250
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1251
+
1252
+ :returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
1253
+ """
1254
+ return FlashSparseAttnFunc.apply(
1255
+ query,
1256
+ key,
1257
+ value,
1258
+ is_causal,
1259
+ softmax_scale,
1260
+ query_scale,
1261
+ key_scale,
1262
+ value_scale,
1263
+ window_sizes,
1264
+ softmax_threshold,
1265
+ is_local,
1266
+ is_quant,
1267
+ is_split_kv,
1268
+ is_split_qo,
1269
+ pack_gqa,
1270
+ out,
1271
+ lse,
1272
+ is_autotune,
1273
+ skip_checks,
1274
+ return_lse,
1275
+ )
1276
+
1277
+
1278
+ def flash_sparse_attn_with_kvcache_func(
1279
+ query: torch.Tensor,
1280
+ key: torch.Tensor,
1281
+ value: torch.Tensor,
1282
+ softmax_scale: Optional[float] = None,
1283
+ softmax_threshold: Optional[float] = None,
1284
+ query_scale: Optional[torch.Tensor] = None,
1285
+ key_scale: Optional[torch.Tensor] = None,
1286
+ value_scale: Optional[torch.Tensor] = None,
1287
+ window_sizes: Optional[torch.Tensor] = None,
1288
+ is_local: bool = False,
1289
+ is_quant: bool = False,
1290
+ out: Optional[torch.Tensor] = None,
1291
+ lse: Optional[torch.Tensor] = None,
1292
+ is_autotune: bool = False,
1293
+ skip_checks: bool = False,
1294
+ return_lse: bool = False,
1295
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1296
+ """
1297
+ Flash sparse attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
1298
+
1299
+ :param query: Query tensor of shape [batch_size, num_heads, head_dim].
1300
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1301
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1302
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1303
+ :param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / seqlen_k.
1304
+ :param query_scale: Optional per-tensor scale for query dequantization.
1305
+ :param key_scale: Optional per-tensor scale for key dequantization.
1306
+ :param value_scale: Optional per-tensor scale for value dequantization.
1307
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1308
+ :param is_local: Whether to apply a local mask.
1309
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1310
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
1311
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
1312
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1313
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1314
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1315
+
1316
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
1317
+ """
1318
+
1319
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1320
+ query, query_scale = quant.quantize_fp8(query)
1321
+ key, key_scale = quant.quantize_fp8(key)
1322
+ value, value_scale = quant.quantize_fp8(value)
1323
+
1324
+ if window_sizes is not None:
1325
+ is_local = True
1326
+
1327
+ out, lse = _flash_sparse_attn_decode(
1328
+ query=query,
1329
+ key=key,
1330
+ value=value,
1331
+ softmax_scale=softmax_scale,
1332
+ softmax_threshold=softmax_threshold,
1333
+ query_scale=query_scale,
1334
+ key_scale=key_scale,
1335
+ value_scale=value_scale,
1336
+ window_sizes=window_sizes,
1337
+ is_local=is_local,
1338
+ is_quant=is_quant,
1339
+ out=out,
1340
+ lse=lse,
1341
+ is_autotune=is_autotune,
1342
+ skip_checks=skip_checks,
1343
+ )
1344
+
1345
+ if return_lse:
1346
+ return out, lse
1347
+ return out
1348
+
1349
+
1350
+ def flash_sparse_attn_varlen_func(
1351
+ query: torch.Tensor,
1352
+ key: torch.Tensor,
1353
+ value: torch.Tensor,
1354
+ cu_seqlens_q: torch.Tensor,
1355
+ cu_seqlens_k: torch.Tensor,
1356
+ max_seqlen_q: int,
1357
+ max_seqlen_k: int,
1358
+ is_causal: bool = False,
1359
+ softmax_scale: Optional[float] = None,
1360
+ query_scale: Optional[torch.Tensor] = None,
1361
+ key_scale: Optional[torch.Tensor] = None,
1362
+ value_scale: Optional[torch.Tensor] = None,
1363
+ window_sizes: Optional[torch.Tensor] = None,
1364
+ softmax_threshold: Optional[float] = None,
1365
+ is_local: bool = False,
1366
+ is_quant: bool = False,
1367
+ is_split_kv: bool = False,
1368
+ is_split_qo: bool = False,
1369
+ pack_gqa: bool = False,
1370
+ seqused_q: Optional[torch.Tensor] = None,
1371
+ seqused_k: Optional[torch.Tensor] = None,
1372
+ out: Optional[torch.Tensor] = None,
1373
+ lse: Optional[torch.Tensor] = None,
1374
+ is_autotune: bool = False,
1375
+ skip_checks: bool = False,
1376
+ return_lse: bool = False,
1377
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1378
+ """
1379
+ Flash sparse attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
1380
+
1381
+ :param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
1382
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1383
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1384
+ :param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
1385
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1386
+ :param max_seqlen_q: Maximum sequence length for queries.
1387
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1388
+ :param is_causal: Whether to apply a causal mask.
1389
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1390
+ :param query_scale: Optional per-tensor scale for query dequantization.
1391
+ :param key_scale: Optional per-tensor scale for key dequantization.
1392
+ :param value_scale: Optional per-tensor scale for value dequantization.
1393
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1394
+ :param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / max_seqlen_k.
1395
+ :param is_local: Whether to apply a local mask.
1396
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
1397
+ :param is_split_kv: Whether to enable split-KV for forward occupancy.
1398
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
1399
+ :param pack_gqa: Whether to pack grouped-query attention.
1400
+ :param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
1401
+ :param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
1402
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
1403
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
1404
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1405
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1406
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1407
+
1408
+ :returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
1409
+ """
1410
+ return FlashSparseAttnVarlenFunc.apply(
1411
+ query,
1412
+ key,
1413
+ value,
1414
+ cu_seqlens_q,
1415
+ cu_seqlens_k,
1416
+ max_seqlen_q,
1417
+ max_seqlen_k,
1418
+ is_causal,
1419
+ softmax_scale,
1420
+ query_scale,
1421
+ key_scale,
1422
+ value_scale,
1423
+ window_sizes,
1424
+ softmax_threshold,
1425
+ is_local,
1426
+ is_quant,
1427
+ is_split_kv,
1428
+ is_split_qo,
1429
+ pack_gqa,
1430
+ seqused_q,
1431
+ seqused_k,
1432
+ out,
1433
+ lse,
1434
+ is_autotune,
1435
+ skip_checks,
1436
+ return_lse,
1437
+ )
1438
+
1439
+
1440
+ def flash_sparse_attn_varlen_with_kvcache_func(
1441
+ query: torch.Tensor,
1442
+ key: torch.Tensor,
1443
+ value: torch.Tensor,
1444
+ cu_seqlens_k: torch.Tensor,
1445
+ max_seqlen_k: int,
1446
+ softmax_scale: Optional[float] = None,
1447
+ softmax_threshold: Optional[float] = None,
1448
+ query_scale: Optional[torch.Tensor] = None,
1449
+ key_scale: Optional[torch.Tensor] = None,
1450
+ value_scale: Optional[torch.Tensor] = None,
1451
+ window_sizes: Optional[torch.Tensor] = None,
1452
+ is_local: bool = False,
1453
+ is_quant: bool = False,
1454
+ seqused_k: Optional[torch.Tensor] = None,
1455
+ out: Optional[torch.Tensor] = None,
1456
+ lse: Optional[torch.Tensor] = None,
1457
+ is_autotune: bool = False,
1458
+ skip_checks: bool = False,
1459
+ return_lse: bool = False,
1460
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1461
+ """
1462
+ Flash sparse attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
1463
+
1464
+ :param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
1465
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1466
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1467
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1468
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1469
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1470
+ :param softmax_threshold: Optional threshold for the sparse softmax. If None, defaults to head_dim / max_seqlen_k.
1471
+ :param query_scale: Optional per-tensor scale for query dequantization.
1472
+ :param key_scale: Optional per-tensor scale for key dequantization.
1473
+ :param value_scale: Optional per-tensor scale for value dequantization.
1474
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1475
+ :param is_local: Whether to apply a local mask.
1476
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1477
+ :param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
1478
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
1479
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
1480
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1481
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1482
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1483
+
1484
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
1485
+ """
1486
+
1487
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1488
+ query, query_scale = quant.quantize_fp8(query)
1489
+ key, key_scale = quant.quantize_fp8(key)
1490
+ value, value_scale = quant.quantize_fp8(value)
1491
+
1492
+ if window_sizes is not None:
1493
+ is_local = True
1494
+
1495
+ out, lse = _flash_sparse_attn_varlen_decode(
1496
+ query=query,
1497
+ key=key,
1498
+ value=value,
1499
+ cu_seqlens_k=cu_seqlens_k,
1500
+ max_seqlen_k=max_seqlen_k,
1501
+ softmax_scale=softmax_scale,
1502
+ softmax_threshold=softmax_threshold,
1503
+ query_scale=query_scale,
1504
+ key_scale=key_scale,
1505
+ value_scale=value_scale,
1506
+ window_sizes=window_sizes,
1507
+ is_local=is_local,
1508
+ is_quant=is_quant,
1509
+ seqused_k=seqused_k,
1510
+ out=out,
1511
+ lse=lse,
1512
+ is_autotune=is_autotune,
1513
+ skip_checks=skip_checks,
1514
+ )
1515
+
1516
+ if return_lse:
1517
+ return out, lse
1518
+ return out
1519
+
1520
+
1521
+ def flash_gated_attn_func(
1522
+ query: torch.Tensor,
1523
+ key: torch.Tensor,
1524
+ value: torch.Tensor,
1525
+ alpha: torch.Tensor,
1526
+ delta: torch.Tensor,
1527
+ is_causal: bool = False,
1528
+ softmax_scale: Optional[float] = None,
1529
+ query_scale: Optional[torch.Tensor] = None,
1530
+ key_scale: Optional[torch.Tensor] = None,
1531
+ value_scale: Optional[torch.Tensor] = None,
1532
+ window_sizes: Optional[torch.Tensor] = None,
1533
+ softmax_threshold: Optional[float] = None,
1534
+ gate_threshold: Optional[float] = None,
1535
+ is_logsigmoid_gate: bool = True,
1536
+ is_adapt_gate: bool = True,
1537
+ is_local: bool = False,
1538
+ is_quant: bool = False,
1539
+ is_split_kv: bool = False,
1540
+ is_split_qo: bool = False,
1541
+ pack_gqa: bool = False,
1542
+ out: Optional[torch.Tensor] = None,
1543
+ lse: Optional[torch.Tensor] = None,
1544
+ is_autotune: bool = False,
1545
+ skip_checks: bool = False,
1546
+ return_lse: bool = False,
1547
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1548
+ """
1549
+ Flash gated attention function that computes the attention output and optionally the logsumexp.
1550
+
1551
+ :param query: Query tensor of shape [batch_size, seqlen_q, num_heads, head_dim].
1552
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1553
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1554
+ :param alpha: Tensor of shape [batch_size, seqlen_q, num_heads] representing the sparsity pattern for queries.
1555
+ :param delta: Tensor of shape [batch_size, seqlen_k, num_kv_heads] representing the sparsity pattern for keys/values.
1556
+ :param is_causal: Whether to apply a causal mask.
1557
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1558
+ :param query_scale: Optional per-tensor scale for query dequantization.
1559
+ :param key_scale: Optional per-tensor scale for key dequantization.
1560
+ :param value_scale: Optional per-tensor scale for value dequantization.
1561
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1562
+ :param softmax_threshold: Optional threshold for the sparse softmax.
1563
+ :param gate_threshold: Optional threshold for the sparsity gate.
1564
+ :param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
1565
+ :param is_adapt_gate: Whether to adapt the gate threshold based on sequence length.
1566
+ :param is_local: Whether to apply a local mask.
1567
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
1568
+ :param is_split_kv: Whether to enable split-KV for forward occupancy.
1569
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
1570
+ :param pack_gqa: Whether to pack grouped-query attention.
1571
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
1572
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
1573
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1574
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1575
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1576
+
1577
+ :returns: If return_lse is False, returns out with shape [batch_size, seqlen_q, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads, seqlen_q].
1578
+ """
1579
+ return FlashGatedAttnFunc.apply(
1580
+ query,
1581
+ key,
1582
+ value,
1583
+ alpha,
1584
+ delta,
1585
+ is_causal,
1586
+ softmax_scale,
1587
+ query_scale,
1588
+ key_scale,
1589
+ value_scale,
1590
+ window_sizes,
1591
+ softmax_threshold,
1592
+ gate_threshold,
1593
+ is_logsigmoid_gate,
1594
+ is_adapt_gate,
1595
+ is_local,
1596
+ is_quant,
1597
+ is_split_kv,
1598
+ is_split_qo,
1599
+ pack_gqa,
1600
+ out,
1601
+ lse,
1602
+ is_autotune,
1603
+ skip_checks,
1604
+ return_lse,
1605
+ )
1606
+
1607
+
1608
+ def flash_gated_attn_with_kvcache_func(
1609
+ query: torch.Tensor,
1610
+ key: torch.Tensor,
1611
+ value: torch.Tensor,
1612
+ alpha: torch.Tensor,
1613
+ delta: torch.Tensor,
1614
+ softmax_scale: Optional[float] = None,
1615
+ softmax_threshold: Optional[float] = None,
1616
+ gate_threshold: Optional[float] = None,
1617
+ is_logsigmoid_gate: bool = True,
1618
+ query_scale: Optional[torch.Tensor] = None,
1619
+ key_scale: Optional[torch.Tensor] = None,
1620
+ value_scale: Optional[torch.Tensor] = None,
1621
+ window_sizes: Optional[torch.Tensor] = None,
1622
+ is_local: bool = False,
1623
+ is_quant: bool = False,
1624
+ out: Optional[torch.Tensor] = None,
1625
+ lse: Optional[torch.Tensor] = None,
1626
+ is_autotune: bool = False,
1627
+ skip_checks: bool = False,
1628
+ return_lse: bool = False,
1629
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1630
+ """
1631
+ Flash gated attention function for decoding with KV cache that computes the attention output and optionally the logsumexp.
1632
+
1633
+ :param query: Query tensor of shape [batch_size, num_heads, head_dim].
1634
+ :param key: Key tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1635
+ :param value: Value tensor of shape [batch_size, seqlen_k, num_kv_heads, head_dim].
1636
+ :param alpha: Tensor of shape [batch_size, num_heads] representing the sparsity pattern for queries.
1637
+ :param delta: Tensor of shape [batch_size, seqlen_k, num_kv_heads] representing the sparsity pattern for keys/values.
1638
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1639
+ :param softmax_threshold: Optional threshold for the sparse softmax.
1640
+ :param gate_threshold: Optional threshold for the sparsity gate.
1641
+ :param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
1642
+ :param query_scale: Optional per-tensor scale for query dequantization.
1643
+ :param key_scale: Optional per-tensor scale for key dequantization.
1644
+ :param value_scale: Optional per-tensor scale for value dequantization.
1645
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1646
+ :param is_local: Whether to apply a local mask.
1647
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1648
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads, head_dim].
1649
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads].
1650
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1651
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1652
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1653
+
1654
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads].
1655
+ """
1656
+
1657
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1658
+ query, query_scale = quant.quantize_fp8(query)
1659
+ key, key_scale = quant.quantize_fp8(key)
1660
+ value, value_scale = quant.quantize_fp8(value)
1661
+
1662
+ if window_sizes is not None:
1663
+ is_local = True
1664
+
1665
+ # Transpose for make_tensor_descriptor
1666
+ delta = delta.transpose(-2, -1).contiguous()
1667
+
1668
+ out, lse = _flash_gated_attn_decode(
1669
+ query=query,
1670
+ key=key,
1671
+ value=value,
1672
+ alpha=alpha,
1673
+ delta=delta,
1674
+ softmax_scale=softmax_scale,
1675
+ softmax_threshold=softmax_threshold,
1676
+ gate_threshold=gate_threshold,
1677
+ is_logsigmoid_gate=is_logsigmoid_gate,
1678
+ query_scale=query_scale,
1679
+ key_scale=key_scale,
1680
+ value_scale=value_scale,
1681
+ window_sizes=window_sizes,
1682
+ is_local=is_local,
1683
+ is_quant=is_quant,
1684
+ out=out,
1685
+ lse=lse,
1686
+ is_autotune=is_autotune,
1687
+ skip_checks=skip_checks,
1688
+ )
1689
+
1690
+ if return_lse:
1691
+ return out, lse
1692
+ return out
1693
+
1694
+
1695
+ def flash_gated_attn_varlen_func(
1696
+ query: torch.Tensor,
1697
+ key: torch.Tensor,
1698
+ value: torch.Tensor,
1699
+ alpha: torch.Tensor,
1700
+ delta: torch.Tensor,
1701
+ cu_seqlens_q: torch.Tensor,
1702
+ cu_seqlens_k: torch.Tensor,
1703
+ max_seqlen_q: int,
1704
+ max_seqlen_k: int,
1705
+ is_causal: bool = False,
1706
+ softmax_scale: Optional[float] = None,
1707
+ query_scale: Optional[torch.Tensor] = None,
1708
+ key_scale: Optional[torch.Tensor] = None,
1709
+ value_scale: Optional[torch.Tensor] = None,
1710
+ window_sizes: Optional[torch.Tensor] = None,
1711
+ softmax_threshold: Optional[float] = None,
1712
+ gate_threshold: Optional[float] = None,
1713
+ is_logsigmoid_gate: bool = True,
1714
+ is_adapt_gate: bool = True,
1715
+ is_local: bool = False,
1716
+ is_quant: bool = False,
1717
+ is_split_kv: bool = False,
1718
+ is_split_qo: bool = False,
1719
+ pack_gqa: bool = False,
1720
+ seqused_q: Optional[torch.Tensor] = None,
1721
+ seqused_k: Optional[torch.Tensor] = None,
1722
+ out: Optional[torch.Tensor] = None,
1723
+ lse: Optional[torch.Tensor] = None,
1724
+ is_autotune: bool = False,
1725
+ skip_checks: bool = False,
1726
+ return_lse: bool = False,
1727
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1728
+ """
1729
+ Flash gated attention function for variable-length sequences that computes the attention output and optionally the logsumexp.
1730
+
1731
+ :param query: Query tensor of shape [total_seqlen_q, num_heads_q, head_dim].
1732
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1733
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1734
+ :param alpha: Tensor of shape [total_seqlen_q, num_heads_q] representing the sparsity pattern for queries.
1735
+ :param delta: Tensor of shape [total_seqlen_k, num_heads_kv] representing the sparsity pattern for keys/values.
1736
+ :param cu_seqlens_q: Cumulative sequence lengths for queries, shape [batch_size + 1].
1737
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1738
+ :param max_seqlen_q: Maximum sequence length for queries.
1739
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1740
+ :param is_causal: Whether to apply a causal mask.
1741
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1742
+ :param query_scale: Optional per-tensor scale for query dequantization.
1743
+ :param key_scale: Optional per-tensor scale for key dequantization.
1744
+ :param value_scale: Optional per-tensor scale for value dequantization.
1745
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1746
+ :param softmax_threshold: Optional threshold for the sparse softmax.
1747
+ :param gate_threshold: Optional threshold for the sparsity gate.
1748
+ :param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
1749
+ :param is_adapt_gate: Whether to adapt the gate threshold based on sequence length.
1750
+ :param is_local: Whether to apply a local mask.
1751
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided or will be computed from the input tensors.
1752
+ :param is_split_kv: Whether to enable split-KV for forward occupancy.
1753
+ :param is_split_qo: Whether to enable split-QO for backward occupancy.
1754
+ :param pack_gqa: Whether to pack grouped-query attention.
1755
+ :param seqused_q: Optional tensor of shape [total_seqlen_q] indicating the actual sequence lengths for queries. If provided, overrides cu_seqlens_q for masking.
1756
+ :param seqused_k: Optional tensor of shape [total_seqlen_k] indicating the actual sequence lengths for keys/values. If provided, overrides cu_seqlens_k for masking.
1757
+ :param out: Optional preallocated output tensor with shape [batch_size, seqlen_q, num_heads, head_dim].
1758
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads, seqlen_q].
1759
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1760
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1761
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1762
+
1763
+ :returns: If return_lse is False, returns out with shape [total_seqlen_q, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [total_seqlen_q, num_heads_q].
1764
+ """
1765
+ return FlashGatedAttnVarlenFunc.apply(
1766
+ query,
1767
+ key,
1768
+ value,
1769
+ alpha,
1770
+ delta,
1771
+ cu_seqlens_q,
1772
+ cu_seqlens_k,
1773
+ max_seqlen_q,
1774
+ max_seqlen_k,
1775
+ is_causal,
1776
+ softmax_scale,
1777
+ query_scale,
1778
+ key_scale,
1779
+ value_scale,
1780
+ window_sizes,
1781
+ softmax_threshold,
1782
+ gate_threshold,
1783
+ is_logsigmoid_gate,
1784
+ is_adapt_gate,
1785
+ is_local,
1786
+ is_quant,
1787
+ is_split_kv,
1788
+ is_split_qo,
1789
+ pack_gqa,
1790
+ seqused_q,
1791
+ seqused_k,
1792
+ out,
1793
+ lse,
1794
+ is_autotune,
1795
+ skip_checks,
1796
+ return_lse,
1797
+ )
1798
+
1799
+
1800
+ def flash_gated_attn_varlen_with_kvcache_func(
1801
+ query: torch.Tensor,
1802
+ key: torch.Tensor,
1803
+ value: torch.Tensor,
1804
+ alpha: torch.Tensor,
1805
+ delta: torch.Tensor,
1806
+ cu_seqlens_k: torch.Tensor,
1807
+ max_seqlen_k: int,
1808
+ softmax_scale: Optional[float] = None,
1809
+ softmax_threshold: Optional[float] = None,
1810
+ gate_threshold: Optional[float] = None,
1811
+ is_logsigmoid_gate: bool = True,
1812
+ query_scale: Optional[torch.Tensor] = None,
1813
+ key_scale: Optional[torch.Tensor] = None,
1814
+ value_scale: Optional[torch.Tensor] = None,
1815
+ window_sizes: Optional[torch.Tensor] = None,
1816
+ is_local: bool = False,
1817
+ is_quant: bool = False,
1818
+ seqused_k: Optional[torch.Tensor] = None,
1819
+ out: Optional[torch.Tensor] = None,
1820
+ lse: Optional[torch.Tensor] = None,
1821
+ is_autotune: bool = False,
1822
+ skip_checks: bool = False,
1823
+ return_lse: bool = False,
1824
+ ) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
1825
+ """
1826
+ Flash gated attention function for variable-length decoding with KV cache that computes the attention output and optionally the logsumexp.
1827
+
1828
+ :param query: Query tensor of shape [batch_size, num_heads_q, head_dim].
1829
+ :param key: Key tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1830
+ :param value: Value tensor of shape [total_seqlen_k, num_heads_kv, head_dim].
1831
+ :param alpha: Tensor of shape [batch_size, num_heads_q] representing the sparsity pattern for queries.
1832
+ :param delta: Tensor of shape [total_seqlen_k, num_heads_kv] representing the sparsity pattern for keys/values.
1833
+ :param cu_seqlens_k: Cumulative sequence lengths for keys/values, shape [batch_size + 1].
1834
+ :param max_seqlen_k: Maximum sequence length for keys/values.
1835
+ :param softmax_scale: Optional scaling factor for the softmax. If None, defaults to 1/sqrt(head_dim).
1836
+ :param softmax_threshold: Optional threshold for the sparse softmax.
1837
+ :param gate_threshold: Optional threshold for the sparsity gate.
1838
+ :param is_logsigmoid_gate: Whether to use a log-sigmoid function for the sparsity gate. If False, uses a linear function.
1839
+ :param query_scale: Optional per-tensor scale for query dequantization.
1840
+ :param key_scale: Optional per-tensor scale for key dequantization.
1841
+ :param value_scale: Optional per-tensor scale for value dequantization.
1842
+ :param window_sizes: Optional window sizes tensor for flexible local attention. Must be shape [num_kv_heads, 4] with dtype int32, with columns [window_sink, window_left, window_right, window_dist]. If provided, is_local is automatically set to True.
1843
+ :param is_local: Whether to apply a local mask.
1844
+ :param is_quant: Whether the inputs are quantized. If True, query_scale, key_scale, and value_scale must be provided for dequantization.
1845
+ :param seqused_k: Optional tensor indicating the actual sequence lengths for keys/values.
1846
+ :param out: Optional preallocated output tensor with shape [batch_size, num_heads_q, head_dim].
1847
+ :param lse: Optional preallocated logsumexp tensor with shape [batch_size, num_heads_q].
1848
+ :param is_autotune: Force re-run Triton autotuner and overwrite cache. Default False uses cached config.
1849
+ :param skip_checks: Whether to skip input validation checks for faster performance.
1850
+ :param return_lse: Whether to return the logsumexp tensor for numerical stability analysis. If True, returns a tuple (out, lse). If False, returns only out.
1851
+
1852
+ :returns: If return_lse is False, returns out with shape [batch_size, num_heads_q, head_dim]. If return_lse is True, returns a tuple (out, lse), where lse has shape [batch_size, num_heads_q].
1853
+ """
1854
+
1855
+ if is_quant and (query_scale is None or key_scale is None or value_scale is None):
1856
+ query, query_scale = quant.quantize_fp8(query)
1857
+ key, key_scale = quant.quantize_fp8(key)
1858
+ value, value_scale = quant.quantize_fp8(value)
1859
+
1860
+ if window_sizes is not None:
1861
+ is_local = True
1862
+
1863
+ # Transpose for make_tensor_descriptor
1864
+ delta = delta.transpose(-2, -1).contiguous()
1865
+
1866
+ out, lse = _flash_gated_attn_varlen_decode(
1867
+ query=query,
1868
+ key=key,
1869
+ value=value,
1870
+ alpha=alpha,
1871
+ delta=delta,
1872
+ cu_seqlens_k=cu_seqlens_k,
1873
+ max_seqlen_k=max_seqlen_k,
1874
+ softmax_scale=softmax_scale,
1875
+ softmax_threshold=softmax_threshold,
1876
+ gate_threshold=gate_threshold,
1877
+ is_logsigmoid_gate=is_logsigmoid_gate,
1878
+ query_scale=query_scale,
1879
+ key_scale=key_scale,
1880
+ value_scale=value_scale,
1881
+ window_sizes=window_sizes,
1882
+ is_local=is_local,
1883
+ is_quant=is_quant,
1884
+ seqused_k=seqused_k,
1885
+ out=out,
1886
+ lse=lse,
1887
+ is_autotune=is_autotune,
1888
+ skip_checks=skip_checks,
1889
+ )
1890
+
1891
+ if return_lse:
1892
+ return out, lse
1893
+ return out
1894
+
1895
+
1896
+ def flash_proj_func(
1897
+ input: torch.Tensor,
1898
+ weight: torch.Tensor,
1899
+ out_dtype: torch.dtype = torch.float8_e5m2,
1900
+ prev_scale: Optional[torch.Tensor] = None,
1901
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
1902
+ """
1903
+ Fused linear projection with quantized output and per-tensor scale.
1904
+
1905
+ :param input: Input tensor of shape [batch_size, seqlen, in_features].
1906
+ :param weight: Weights tensor of shape [out_features, in_features].
1907
+ :param out_dtype: Target output dtype.
1908
+ :param prev_scale: Per-tensor scale from the previous forward.
1909
+
1910
+ :returns: Tuple of (out, actual_scale), where out has shape [batch_size, seqlen, out_features].
1911
+ """
1912
+ return FlashProjFunc.apply(input, weight, out_dtype, prev_scale)
build/torch-cuda/kernel_repr.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def _get(c, key, default=None):
2
+ try:
3
+ return c[key]
4
+ except (KeyError, TypeError):
5
+ return default
6
+
7
+
8
+ def fwd_dense_repr(specialization):
9
+ c = specialization.constants
10
+ mask = (
11
+ "causal"
12
+ if _get(c, "IS_CAUSAL")
13
+ else ("local" if _get(c, "IS_LOCAL") else "full")
14
+ )
15
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
16
+ split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
17
+ return f"flash_dense_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
18
+
19
+
20
+ def fwd_sparse_repr(specialization):
21
+ c = specialization.constants
22
+ mask = (
23
+ "causal"
24
+ if _get(c, "IS_CAUSAL")
25
+ else ("local" if _get(c, "IS_LOCAL") else "full")
26
+ )
27
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
28
+ split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
29
+ return f"flash_sparse_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
30
+
31
+
32
+ def fwd_gated_repr(specialization):
33
+ c = specialization.constants
34
+ mask = (
35
+ "causal"
36
+ if _get(c, "IS_CAUSAL")
37
+ else ("local" if _get(c, "IS_LOCAL") else "full")
38
+ )
39
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
40
+ split = "_splitkv" if _get(c, "IS_SPLIT_KV") else ""
41
+ return f"flash_gated_fwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{split}{varlen}"
42
+
43
+
44
+ def bwd_dense_repr(specialization):
45
+ c = specialization.constants
46
+ mask = (
47
+ "causal"
48
+ if _get(c, "IS_CAUSAL")
49
+ else ("local" if _get(c, "IS_LOCAL") else "full")
50
+ )
51
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
52
+ return f"flash_dense_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
53
+
54
+
55
+ def bwd_sparse_repr(specialization):
56
+ c = specialization.constants
57
+ mask = (
58
+ "causal"
59
+ if _get(c, "IS_CAUSAL")
60
+ else ("local" if _get(c, "IS_LOCAL") else "full")
61
+ )
62
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
63
+ return f"flash_sparse_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
64
+
65
+
66
+ def bwd_gated_repr(specialization):
67
+ c = specialization.constants
68
+ mask = (
69
+ "causal"
70
+ if _get(c, "IS_CAUSAL")
71
+ else ("local" if _get(c, "IS_LOCAL") else "full")
72
+ )
73
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
74
+ return f"flash_gated_bwd_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
75
+
76
+
77
+ def dec_dense_repr(specialization):
78
+ c = specialization.constants
79
+ mask = "local" if _get(c, "IS_LOCAL") else "full"
80
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
81
+ return f"flash_dense_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
82
+
83
+
84
+ def dec_sparse_repr(specialization):
85
+ c = specialization.constants
86
+ mask = "local" if _get(c, "IS_LOCAL") else "full"
87
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
88
+ return f"flash_sparse_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
89
+
90
+
91
+ def dec_gated_repr(specialization):
92
+ c = specialization.constants
93
+ mask = "local" if _get(c, "IS_LOCAL") else "full"
94
+ varlen = "_varlen" if _get(c, "HAS_CU_SEQLENS_Q") else ""
95
+ return f"flash_gated_dec_{mask}_{c['TILE_M']}x{c['TILE_N']}{varlen}"
96
+
97
+
98
+ def fwd_combine_repr(specialization):
99
+ c = specialization.constants
100
+ return f"flash_fwd_combine_{c['TILE_M']}"
101
+
102
+
103
+ def dec_combine_repr(specialization):
104
+ c = specialization.constants
105
+ return f"flash_dec_combine_{c['TILE_K']}"
106
+
107
+
108
+ def bwd_preprocess_repr(specialization):
109
+ c = specialization.constants
110
+ return f"flash_bwd_preprocess_{c['TILE_M']}"
111
+
112
+
113
+ def bwd_postprocess_repr(specialization):
114
+ c = specialization.constants
115
+ return f"flash_bwd_postprocess_{c['TILE_M']}"
build/torch-cuda/launch_grid.py ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+
3
+ from . import cache_utils
4
+
5
+
6
+ def get_fwd_grid(
7
+ batch_size: int,
8
+ seqlen_q: int,
9
+ num_heads_q: int,
10
+ num_heads_kv: int,
11
+ pack_gqa: bool,
12
+ num_splits: int,
13
+ ):
14
+ """
15
+ Get the grid function for the forward kernel.
16
+
17
+ :param batch_size: Batch size
18
+ :param seqlen_q: Sequence length of queries
19
+ :param num_heads_q: Number of query heads
20
+ :param num_heads_kv: Number of key/value heads
21
+ :param pack_gqa: Whether GQA packing is used
22
+ :param num_splits: Number of KV splits
23
+
24
+ :return grid: Grid function
25
+ """
26
+
27
+ def grid(META):
28
+ return (
29
+ triton.cdiv(
30
+ seqlen_q * (num_heads_q // num_heads_kv) if pack_gqa else seqlen_q,
31
+ META["TILE_M"],
32
+ ),
33
+ num_heads_kv if pack_gqa else num_heads_q,
34
+ batch_size * num_splits,
35
+ )
36
+
37
+ return grid
38
+
39
+
40
+ get_fwd_grid = cache_utils.cache_launch_grid(get_fwd_grid)
41
+
42
+
43
+ def get_bwd_grid(
44
+ batch_size: int,
45
+ seqlen_k: int,
46
+ num_heads_q: int,
47
+ num_splits: int = 1,
48
+ ):
49
+ """
50
+ Get the grid function for the backward kernel.
51
+
52
+ :param batch_size: Batch size
53
+ :param seqlen_k: Sequence length of keys
54
+ :param num_heads_q: Number of query heads
55
+ :param num_splits: Number of QO splits
56
+
57
+ :return grid: Grid function
58
+ """
59
+
60
+ def grid(META):
61
+ return (
62
+ triton.cdiv(seqlen_k, META["TILE_N"]),
63
+ num_heads_q,
64
+ batch_size * num_splits,
65
+ )
66
+
67
+ return grid
68
+
69
+
70
+ get_bwd_grid = cache_utils.cache_launch_grid(get_bwd_grid)
71
+
72
+
73
+ def get_dec_grid(
74
+ batch_size: int,
75
+ num_heads_kv: int,
76
+ num_splits: int,
77
+ ):
78
+ """
79
+ Get the grid function for the decode kernel.
80
+
81
+ :param batch_size: Batch size
82
+ :param num_heads_kv: Number of key/value heads
83
+ :param num_splits: Number of KV splits
84
+
85
+ :return grid: Grid function
86
+ """
87
+
88
+ def grid(META):
89
+ return (
90
+ num_heads_kv,
91
+ batch_size * num_splits,
92
+ )
93
+
94
+ return grid
95
+
96
+
97
+ get_dec_grid = cache_utils.cache_launch_grid(get_dec_grid)
98
+
99
+
100
+ def get_fwd_combine_grid(
101
+ batch_size: int,
102
+ seqlen_q: int,
103
+ num_heads_q: int,
104
+ ):
105
+ """
106
+ Get the grid function for the forward combine kernel.
107
+
108
+ :param batch_size: Batch size
109
+ :param seqlen_q: Sequence length of queries
110
+ :param num_heads_q: Number of query heads
111
+
112
+ :return grid: Grid function
113
+ """
114
+
115
+ def grid(META):
116
+ return (
117
+ triton.cdiv(seqlen_q, META["TILE_M"]),
118
+ batch_size * num_heads_q,
119
+ )
120
+
121
+ return grid
122
+
123
+
124
+ get_fwd_combine_grid = cache_utils.cache_launch_grid(get_fwd_combine_grid)
125
+
126
+
127
+ def get_bwd_preprocess_grid(
128
+ batch_size: int,
129
+ seqlen_q: int,
130
+ num_heads_q: int,
131
+ ):
132
+ """
133
+ Get the grid function for the backward preprocess kernel.
134
+
135
+ :param batch_size: Batch size
136
+ :param seqlen_q: Sequence length of queries
137
+ :param num_heads_q: Number of query heads
138
+
139
+ :return grid: Grid function
140
+ """
141
+
142
+ def grid(META):
143
+ return (
144
+ triton.cdiv(seqlen_q, META["TILE_M"]),
145
+ num_heads_q,
146
+ batch_size,
147
+ )
148
+
149
+ return grid
150
+
151
+
152
+ get_bwd_preprocess_grid = cache_utils.cache_launch_grid(get_bwd_preprocess_grid)
153
+
154
+
155
+ def get_bwd_postprocess_grid(
156
+ batch_size: int,
157
+ seqlen_q: int,
158
+ num_heads_q: int,
159
+ ):
160
+ """
161
+ Get the grid function for the backward postprocess kernel.
162
+
163
+ :param batch_size: Batch size
164
+ :param seqlen_q: Sequence length of queries
165
+ :param num_heads_q: Number of query heads
166
+
167
+ :return grid: Grid function
168
+ """
169
+
170
+ def grid(META):
171
+ return (
172
+ triton.cdiv(seqlen_q, META["TILE_M"]),
173
+ num_heads_q,
174
+ batch_size,
175
+ )
176
+
177
+ return grid
178
+
179
+
180
+ get_bwd_postprocess_grid = cache_utils.cache_launch_grid(get_bwd_postprocess_grid)
181
+
182
+
183
+ def get_dec_combine_grid(
184
+ batch_size: int,
185
+ num_heads_q: int,
186
+ ):
187
+ """
188
+ Get the grid function for the decode combine kernel.
189
+
190
+ :param batch_size: Batch size
191
+ :param num_heads_q: Number of query heads
192
+
193
+ :return grid: Grid function
194
+ """
195
+
196
+ def grid(META):
197
+ return (batch_size * num_heads_q,)
198
+
199
+ return grid
200
+
201
+
202
+ get_dec_combine_grid = cache_utils.cache_launch_grid(get_dec_combine_grid)
build/torch-cuda/launch_template.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ NOTE: Setting num_ctas=2 for the kernel can trigger Triton's PlanCTA assertion
3
+ Setting num_ctas=1 for now to avoid this issue, but we may want to revisit this in the future
4
+ """
5
+
6
+ import functools
7
+ import hashlib
8
+ import json
9
+ import os
10
+ import sys
11
+ import tempfile
12
+ from pathlib import Path
13
+ from filelock import ReadWriteLock
14
+
15
+ import torch
16
+ import triton
17
+
18
+ from . import cache_utils
19
+
20
+
21
+ TRITON_CACHE_DIR: str = os.getenv(
22
+ "FLASH_SPARSE_ATTENTION_LAUNCH_CONFIG_DIR",
23
+ str(Path(__file__).resolve().parent / "launch_config"),
24
+ )
25
+
26
+
27
+ @functools.lru_cache(maxsize=1)
28
+ def _compute_source_fingerprint() -> str:
29
+ """
30
+ Hash all Triton Python sources plus runtime ABI stamps into a short fingerprint.
31
+ The fingerprint changes whenever any .py file under flash_sparse_attn/ops/triton is
32
+ modified, the Python minor version changes, or the triton package version changes.
33
+
34
+ :return: SHA-256 hex digest string.
35
+ """
36
+ triton_root = Path(__file__).resolve().parent
37
+ h = hashlib.sha256()
38
+ h.update(f"py{sys.version_info.major}.{sys.version_info.minor}".encode())
39
+ h.update(f"triton={getattr(triton, '__version__', 'unknown')}".encode())
40
+ for src in sorted(triton_root.rglob("*.py")):
41
+ if not src.is_file():
42
+ continue
43
+ h.update(src.relative_to(triton_root).as_posix().encode())
44
+ content = src.read_bytes()
45
+ h.update(len(content).to_bytes(8, "little"))
46
+ h.update(content)
47
+ return h.hexdigest()
48
+
49
+
50
+ def _seqlen_bucket(seqlen: int) -> int:
51
+ if seqlen <= 0:
52
+ return 1
53
+ return triton.next_power_of_2(seqlen)
54
+
55
+
56
+ def _sanitize_device_name(name: str) -> str:
57
+ return name.replace(" ", "_").replace("/", "_")
58
+
59
+
60
+ class LaunchConfigCache:
61
+ """
62
+ Persistent per-device cache for Triton kernel launch configurations.
63
+ Stores tuned (TILE_M, TILE_N, num_warps, num_stages, num_ctas) keyed by
64
+ kernel name, sequence length buckets, and dispatch flags. Cache is namespaced
65
+ under a source fingerprint directory for automatic invalidation on code changes.
66
+ """
67
+
68
+ __slots__ = ("_memory", "_fingerprint")
69
+
70
+ def __init__(self):
71
+ self._memory: dict[str, dict[tuple, tuple]] = {}
72
+ self._fingerprint: str = _compute_source_fingerprint()
73
+
74
+ def _get_cache_dir(self) -> Path:
75
+ cache_dir = Path(TRITON_CACHE_DIR) / self._fingerprint
76
+ cache_dir.mkdir(parents=True, exist_ok=True)
77
+ return cache_dir
78
+
79
+ def _json_path(self, device_name: str) -> Path:
80
+ return self._get_cache_dir() / f"{_sanitize_device_name(device_name)}.json"
81
+
82
+ def _lock(self, device_name: str) -> ReadWriteLock:
83
+ lock_path = self._get_cache_dir() / f"{_sanitize_device_name(device_name)}"
84
+ return ReadWriteLock(str(lock_path), timeout=15)
85
+
86
+ def _read_unlocked(self, device_name: str) -> dict[tuple, tuple]:
87
+ json_path = self._json_path(device_name)
88
+ result: dict[tuple, tuple] = {}
89
+ if not json_path.exists():
90
+ return result
91
+ try:
92
+ data = json.loads(json_path.read_text())
93
+ for entry in data:
94
+ key = (
95
+ entry["kernel"],
96
+ entry["seqlen_q_bucket"],
97
+ entry["seqlen_k_bucket"],
98
+ entry["tile_k"],
99
+ entry["is_local"],
100
+ entry["qhead_per_kvhead"],
101
+ entry.get("is_causal", False),
102
+ entry.get("pack_gqa", False),
103
+ entry.get("is_quant", False),
104
+ )
105
+ result[key] = tuple(entry["config"])
106
+ except (OSError, json.JSONDecodeError, KeyError, TypeError):
107
+ result = {}
108
+ return result
109
+
110
+ def _write_unlocked(self, device_name: str, cache: dict[tuple, tuple]) -> None:
111
+ json_path = self._json_path(device_name)
112
+ data = []
113
+ for (k_name, sq, sk, tk, local, qpk, causal, pgqa, quant), config in sorted(
114
+ cache.items(), key=lambda x: x[0]
115
+ ):
116
+ data.append(
117
+ {
118
+ "kernel": k_name,
119
+ "seqlen_q_bucket": sq,
120
+ "seqlen_k_bucket": sk,
121
+ "tile_k": tk,
122
+ "is_local": local,
123
+ "qhead_per_kvhead": qpk,
124
+ "is_causal": causal,
125
+ "pack_gqa": pgqa,
126
+ "is_quant": quant,
127
+ "config": list(config),
128
+ }
129
+ )
130
+
131
+ fd, tmp_path = tempfile.mkstemp(
132
+ dir=str(json_path.parent), suffix=".json.tmp", prefix=json_path.stem
133
+ )
134
+ try:
135
+ with os.fdopen(fd, "w") as f:
136
+ json.dump(data, f)
137
+ os.replace(tmp_path, str(json_path))
138
+ except BaseException:
139
+ try:
140
+ os.unlink(tmp_path)
141
+ except OSError:
142
+ pass
143
+ raise
144
+
145
+ def _load_device(self, device_name: str) -> dict[tuple, tuple]:
146
+ if device_name in self._memory:
147
+ return self._memory[device_name]
148
+ with self._lock(device_name).read_lock():
149
+ result = self._read_unlocked(device_name)
150
+ self._memory[device_name] = result
151
+ return result
152
+
153
+ def get(
154
+ self,
155
+ device: torch.device,
156
+ kernel_name: str,
157
+ seqlen_q: int,
158
+ seqlen_k: int,
159
+ tile_k: int,
160
+ is_local: bool = False,
161
+ qhead_per_kvhead: int = 1,
162
+ is_causal: bool = False,
163
+ pack_gqa: bool = False,
164
+ is_quant: bool = False,
165
+ ) -> tuple[int, int, int, int, int] | None:
166
+ """
167
+ Load a cached launch config for the given kernel specialization.
168
+
169
+ :param device: The device to run the kernel on
170
+ :param kernel_name: Name of the Triton kernel
171
+ :param seqlen_q: Sequence length of queries
172
+ :param seqlen_k: Sequence length of keys
173
+ :param tile_k: Tile size along the K dimension
174
+ :param is_local: Whether local mask is applied
175
+ :param qhead_per_kvhead: Ratio of query heads to key/value heads
176
+ :param is_causal: Whether causal mask is applied
177
+ :param pack_gqa: Whether GQA packing is enabled
178
+ :param is_quant: Whether quantization is used
179
+
180
+ :return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None on cache miss
181
+ """
182
+ device_name = torch.cuda.get_device_name(device)
183
+ cache = self._load_device(device_name)
184
+ key = (
185
+ kernel_name,
186
+ _seqlen_bucket(seqlen_q),
187
+ _seqlen_bucket(seqlen_k),
188
+ tile_k,
189
+ is_local,
190
+ qhead_per_kvhead,
191
+ is_causal,
192
+ pack_gqa,
193
+ is_quant,
194
+ )
195
+ return cache.get(key)
196
+
197
+ def put(
198
+ self,
199
+ device: torch.device,
200
+ kernel_name: str,
201
+ seqlen_q: int,
202
+ seqlen_k: int,
203
+ tile_k: int,
204
+ is_local: bool = False,
205
+ qhead_per_kvhead: int = 1,
206
+ is_causal: bool = False,
207
+ pack_gqa: bool = False,
208
+ is_quant: bool = False,
209
+ *,
210
+ config: tuple[int, int, int, int, int],
211
+ ) -> None:
212
+ """
213
+ Store a launch config to the per-device cache.
214
+
215
+ :param device: The device to run the kernel on
216
+ :param kernel_name: Name of the Triton kernel
217
+ :param seqlen_q: Sequence length of queries
218
+ :param seqlen_k: Sequence length of keys
219
+ :param tile_k: Tile size along the K dimension
220
+ :param is_local: Whether local mask is applied
221
+ :param qhead_per_kvhead: Ratio of query heads to key/value heads
222
+ :param is_causal: Whether causal mask is applied
223
+ :param pack_gqa: Whether GQA packing is enabled
224
+ :param is_quant: Whether quantization is used
225
+ :param config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas)
226
+ """
227
+ device_name = torch.cuda.get_device_name(device)
228
+ key = (
229
+ kernel_name,
230
+ _seqlen_bucket(seqlen_q),
231
+ _seqlen_bucket(seqlen_k),
232
+ tile_k,
233
+ is_local,
234
+ qhead_per_kvhead,
235
+ is_causal,
236
+ pack_gqa,
237
+ is_quant,
238
+ )
239
+ # Skip disk write if memory cache already has the same config
240
+ mem = self._memory.get(device_name)
241
+ if mem is not None and mem.get(key) == config:
242
+ return
243
+ with self._lock(device_name).write_lock():
244
+ disk_cache = self._read_unlocked(device_name)
245
+ disk_cache[key] = config
246
+ self._write_unlocked(device_name, disk_cache)
247
+ self._memory[device_name] = disk_cache
248
+
249
+ def clear(self) -> None:
250
+ self._memory.clear()
251
+
252
+ def stats(self) -> dict:
253
+ total_entries = sum(len(v) for v in self._memory.values())
254
+ return {
255
+ "devices": len(self._memory),
256
+ "entries": total_entries,
257
+ "fingerprint": self._fingerprint,
258
+ }
259
+
260
+
261
+ _cache_instance: LaunchConfigCache | None = None
262
+
263
+
264
+ def get_launch_config_cache() -> LaunchConfigCache:
265
+ global _cache_instance
266
+ if _cache_instance is None:
267
+ _cache_instance = LaunchConfigCache()
268
+ return _cache_instance
269
+
270
+
271
+ def load_launch_config(
272
+ device: torch.device,
273
+ kernel_name: str,
274
+ seqlen_q: int,
275
+ seqlen_k: int,
276
+ tile_k: int,
277
+ is_local: bool = False,
278
+ qhead_per_kvhead: int = 1,
279
+ is_causal: bool = False,
280
+ pack_gqa: bool = False,
281
+ is_quant: bool = False,
282
+ ) -> tuple[int, int, int, int, int] | None:
283
+ """
284
+ Load cached launch config for a kernel specialization.
285
+
286
+ :param device: The device to run the kernel on
287
+ :param kernel_name: Name of the Triton kernel
288
+ :param seqlen_q: Sequence length of queries
289
+ :param seqlen_k: Sequence length of keys
290
+ :param tile_k: Tile size along the K dimension
291
+ :param is_local: Whether local mask is applied
292
+ :param qhead_per_kvhead: Ratio of query heads to key/value heads
293
+ :param is_causal: Whether causal mask is applied
294
+ :param pack_gqa: Whether GQA packing is enabled
295
+ :param is_quant: Whether quantization is used
296
+
297
+ :return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None on cache miss
298
+ """
299
+ return get_launch_config_cache().get(
300
+ device,
301
+ kernel_name,
302
+ seqlen_q,
303
+ seqlen_k,
304
+ tile_k,
305
+ is_local,
306
+ qhead_per_kvhead,
307
+ is_causal,
308
+ pack_gqa,
309
+ is_quant,
310
+ )
311
+
312
+
313
+ def store_launch_config(
314
+ device: torch.device,
315
+ kernel_name: str,
316
+ seqlen_q: int,
317
+ seqlen_k: int,
318
+ tile_k: int,
319
+ *,
320
+ config: tuple[int, int, int, int, int],
321
+ is_local: bool = False,
322
+ qhead_per_kvhead: int = 1,
323
+ is_causal: bool = False,
324
+ pack_gqa: bool = False,
325
+ is_quant: bool = False,
326
+ ) -> None:
327
+ """
328
+ Store a launch config to the per-device JSON cache.
329
+
330
+ :param device: The device to run the kernel on
331
+ :param kernel_name: Name of the Triton kernel
332
+ :param seqlen_q: Sequence length of queries
333
+ :param seqlen_k: Sequence length of keys
334
+ :param tile_k: Tile size along the K dimension
335
+ :param config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas)
336
+ :param is_local: Whether local mask is applied
337
+ :param qhead_per_kvhead: Ratio of query heads to key/value heads
338
+ :param is_causal: Whether causal mask is applied
339
+ :param pack_gqa: Whether GQA packing is enabled
340
+ :param is_quant: Whether quantization is used
341
+ """
342
+ get_launch_config_cache().put(
343
+ device,
344
+ kernel_name,
345
+ seqlen_q,
346
+ seqlen_k,
347
+ tile_k,
348
+ is_local,
349
+ qhead_per_kvhead,
350
+ is_causal,
351
+ pack_gqa,
352
+ is_quant,
353
+ config=config,
354
+ )
355
+
356
+
357
+ def extract_best_config(autotuned_kernel) -> tuple[int, int, int, int, int] | None:
358
+ """
359
+ Extract best config from a Triton autotuned kernel after it has run.
360
+
361
+ :param autotuned_kernel: A Triton autotuned kernel instance
362
+
363
+ :return launch_config: Tuple of (TILE_M, TILE_N, num_warps, num_stages, num_ctas) or None if no best config found
364
+ """
365
+ raw = getattr(autotuned_kernel, "_autotuned", autotuned_kernel)
366
+ best = getattr(raw, "best_config", None)
367
+ if best is None:
368
+ return None
369
+ return (
370
+ best.kwargs.get("TILE_M", 64),
371
+ best.kwargs.get("TILE_N", 64),
372
+ best.num_warps,
373
+ best.num_stages,
374
+ getattr(best, "num_ctas", 1),
375
+ )
376
+
377
+
378
+ def get_fwd_combine_launch_config(
379
+ tile_k: int,
380
+ device: torch.device,
381
+ arch: int,
382
+ ) -> tuple[int, int, int, int]:
383
+ """
384
+ Get launch configuration for forward combine kernel based on input parameters and device architecture.
385
+
386
+ :param tile_k: Tile size along the K dimension
387
+ :param device: The device to run the kernel on
388
+ :param arch: The architecture of the device
389
+
390
+ :return launch_config: Tuple of (tile_m, num_warps, num_stages, num_ctas) for launching the kernel
391
+ """
392
+ if arch == -1:
393
+ raise NotImplementedError(f"Unsupported device: {device} with arch {arch}")
394
+
395
+ if device.type == "cuda":
396
+ if arch // 10 == 8:
397
+ tile_m = 4 if tile_k % 128 == 0 else (8 if tile_k % 64 == 0 else 16)
398
+ return (tile_m, 4, 1, 1)
399
+ elif arch // 10 == 9:
400
+ tile_m = 8 if tile_k % 128 == 0 else (16 if tile_k % 64 == 0 else 32)
401
+ return (tile_m, 4, 1, 1)
402
+ elif arch // 10 == 10:
403
+ tile_m = 16 if tile_k % 128 == 0 else (32 if tile_k % 64 == 0 else 64)
404
+ return (tile_m, 4, 1, 1)
405
+ elif arch // 10 == 12:
406
+ tile_m = 4 if tile_k % 128 == 0 else (8 if tile_k % 64 == 0 else 16)
407
+ return (tile_m, 4, 1, 1)
408
+ else:
409
+ raise NotImplementedError(f"Unsupported CUDA architecture: {arch}")
410
+ else:
411
+ raise NotImplementedError(f"Unsupported device type: {device.type}")
412
+
413
+
414
+ get_fwd_combine_launch_config = cache_utils.cache_launch_config(
415
+ get_fwd_combine_launch_config
416
+ )
417
+
418
+
419
+ def get_dec_combine_launch_config(
420
+ tile_k: int,
421
+ device: torch.device,
422
+ arch: int,
423
+ ) -> tuple[int, int, int]:
424
+ """
425
+ Get launch configuration for decode combine kernel based on input parameters and device architecture.
426
+
427
+ :param tile_k: Tile size along the K dimension
428
+ :param device: The device to run the kernel on
429
+ :param arch: The architecture of the device
430
+
431
+ :return launch_config: Tuple of (num_warps, num_stages, num_ctas) for launching the kernel
432
+ """
433
+ if arch == -1:
434
+ raise NotImplementedError(f"Unsupported device: {device} with arch {arch}")
435
+
436
+ if device.type == "cuda":
437
+ if arch // 10 == 8:
438
+ num_stages = max(min(8, 512 // tile_k), 1)
439
+ return (4, num_stages, 1)
440
+ elif arch // 10 == 9:
441
+ num_stages = max(min(8, 512 // tile_k), 1)
442
+ return (4, num_stages, 1)
443
+ elif arch // 10 == 10:
444
+ num_stages = max(min(16, 512 // tile_k), 1)
445
+ return (4, num_stages, 1)
446
+ elif arch // 10 == 12:
447
+ num_stages = max(min(4, 512 // tile_k), 1)
448
+ return (4, num_stages, 1)
449
+ else:
450
+ raise NotImplementedError(f"Unsupported CUDA architecture: {arch}")
451
+ else:
452
+ raise NotImplementedError(f"Unsupported device type: {device.type}")
453
+
454
+
455
+ get_dec_combine_launch_config = cache_utils.cache_launch_config(
456
+ get_dec_combine_launch_config
457
+ )
build/torch-cuda/mask.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+ import triton.language as tl
3
+
4
+
5
+ @triton.jit
6
+ def apply_mask(
7
+ acc_s,
8
+ m_block,
9
+ n_block,
10
+ seqlen_q,
11
+ seqlen_k,
12
+ window_size_sink,
13
+ window_size_left,
14
+ window_size_right,
15
+ window_size_dist,
16
+ MASK_SEQLEN: tl.constexpr,
17
+ MASK_CAUSAL: tl.constexpr,
18
+ MASK_LOCAL: tl.constexpr,
19
+ MASK_SINK: tl.constexpr,
20
+ TILE_M: tl.constexpr,
21
+ TILE_N: tl.constexpr,
22
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
23
+ SWAP_AB: tl.constexpr,
24
+ ):
25
+ """
26
+ Apply seqlen, causal, and local masks to the attention scores.
27
+
28
+ :param acc_s: Attention scores tensor of shape [BLOCK_M, BLOCK_N].
29
+ :param m_block: Current block index along the M dimension.
30
+ :param n_block: Current block index along the N dimension.
31
+ :param seqlen_q: The sequence length of the query.
32
+ :param seqlen_k: The sequence length of the key.
33
+ :param window_size_sink: Prefix sink token count.
34
+ :param window_size_left: Distant local band token count.
35
+ :param window_size_right: Gap token count after the near-diagonal window before the distant band.
36
+ :param window_size_dist: Near-diagonal local token count.
37
+ :param MASK_SEQLEN: Boolean flag indicating if seqlen masking should be applied.
38
+ :param MASK_CAUSAL: Boolean flag indicating if causal masking should be applied.
39
+ :param MASK_LOCAL: Boolean flag indicating if local masking should be applied.
40
+ :param MASK_SINK: Boolean flag indicating if sink masking should be applied.
41
+ :param TILE_M: Tile size along the M dimension.
42
+ :param TILE_N: Tile size along the N dimension.
43
+ :param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
44
+ :param SWAP_AB: Boolean flag indicating if query and key dimensions are swapped.
45
+
46
+ :return acc_s: Masked attention scores tensor of shape [BLOCK_M, BLOCK_N].
47
+ """
48
+ offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
49
+ offs_n = n_block * TILE_N + tl.arange(0, TILE_N)
50
+
51
+ if SWAP_AB:
52
+ tl.static_assert(
53
+ QHEAD_PER_KVHEAD_PACKGQA == 1, "SWAP_AB with PACKGQA > 1 not supported"
54
+ )
55
+ q_idx = offs_m[None, :]
56
+ k_idx = offs_n[:, None]
57
+ else:
58
+ q_idx = offs_m[:, None]
59
+ k_idx = offs_n[None, :]
60
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
61
+ q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
62
+
63
+ if MASK_SEQLEN:
64
+ acc_s = tl.where(
65
+ (k_idx < seqlen_k) & (q_idx < seqlen_q),
66
+ acc_s,
67
+ float("-inf"),
68
+ )
69
+
70
+ if MASK_CAUSAL or MASK_LOCAL or MASK_SINK:
71
+ causal_offset = seqlen_k - seqlen_q
72
+ dist = q_idx + causal_offset - k_idx
73
+
74
+ if MASK_LOCAL or MASK_SINK:
75
+ allowed = (dist >= 0) & (dist < 0)
76
+ if MASK_LOCAL:
77
+ allowed = allowed | ((dist >= 0) & (dist < window_size_dist))
78
+ allowed = allowed | (
79
+ (dist >= window_size_dist + window_size_right)
80
+ & (dist < window_size_dist + window_size_right + window_size_left)
81
+ )
82
+ if MASK_SINK:
83
+ allowed = allowed | ((k_idx < window_size_sink) & (dist >= 0))
84
+ acc_s = tl.where(
85
+ allowed,
86
+ acc_s,
87
+ float("-inf"),
88
+ )
89
+ elif MASK_CAUSAL:
90
+ acc_s = tl.where(
91
+ dist >= 0,
92
+ acc_s,
93
+ float("-inf"),
94
+ )
95
+ else:
96
+ acc_s = tl.where(
97
+ ((dist >= 0) & (dist < window_size_dist))
98
+ | (
99
+ (dist >= window_size_dist + window_size_right)
100
+ & (dist < window_size_dist + window_size_right + window_size_left)
101
+ ),
102
+ acc_s,
103
+ float("-inf"),
104
+ )
105
+
106
+ return acc_s
build/torch-cuda/metadata.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "flash-sparse-attention",
3
+ "id": "_flash_sparse_attention_cuda_7db628a_dirty",
4
+ "version": 1,
5
+ "license": "BSD-3-Clause",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda"
9
+ },
10
+ "digest": {
11
+ "algorithm": "sha256",
12
+ "files": {
13
+ "__init__.py": "OXYzVpCpaU4x34L+qzPOSlycSDR+2i/CsF04RT/tBL4=",
14
+ "_ops.py": "Ds3MXWORK2pW+eBqjHYPr5gUiUF/1eJs3P2ryUEnuH4=",
15
+ "activations.py": "bAa6rHqI0noI/8zSGNrY2uzM5VHQmnsS4d2b+V0e5eU=",
16
+ "assert_inputs.py": "eLNN5h2Qhq3hdg2nnSSOAqLUqMZcYW2QqFvPo1FGmLg=",
17
+ "autotuner.py": "rpAY5s+/E6gSau/zZrNJjVOmvteEHsOjDNX/qAsc1Z0=",
18
+ "block_info.py": "JCm1Pp0jCHJOFGpwK6eXdiNhr2ZospuXx8bAX7Bez1E=",
19
+ "cache_utils.py": "+PzgjtFoef+lFNqrVvst06iOLmkTOXeW/y95kFOEi64=",
20
+ "flash_bwd_postprocess.py": "HqYiH4lrwEph+EifxSJqG3jACZLASSBUcuHGq/2ZgyM=",
21
+ "flash_bwd_preprocess.py": "A5/CJhk0Zg6mMb0PU4Lk9mwjDhr3/WaxlFaBtNOhBac=",
22
+ "flash_dec_combine.py": "7ZHMacWY+QPPjgSezKmQzhogibUZfb7abIwQW+2U6ak=",
23
+ "flash_dense_bwd.py": "sSRrZs5y0VB5Ga/9KQCAmuYaKwxeoVJfhwrumzby7xw=",
24
+ "flash_dense_dec.py": "9/JfVxX+4hz3Bq/4liJih6pDwjEZiOyHAZeUYYlMyjM=",
25
+ "flash_dense_fwd.py": "ydJnTHmzMX52WOyGilqNf7pj+6qimeCjc6NmSDJs9BY=",
26
+ "flash_fwd_combine.py": "8iYebjXMgL3fWzR1hiA6/PNLVLjCnze6PmgrI+D/cOA=",
27
+ "flash_gated_bwd.py": "i9+fAUdp0xxpk7A/V6KnLwMVThdrOlFpaWwurQDXyDU=",
28
+ "flash_gated_dec.py": "fLJHd8ii4lV5SCcLYvuDwZVT3Q+x7ANgsQ3xojZgHrw=",
29
+ "flash_gated_fwd.py": "W/9zB7NdC8Ap/dNuMYSNGMMaTxpyQTeyUk2AAKL8vvU=",
30
+ "flash_proj_bwd.py": "a+CpQe6DsiNeVZlq+AJDTjdSeqquUAhege37vrvckDo=",
31
+ "flash_proj_fwd.py": "TVuL7nkXcTi+R5j1tTd5gwFPWl1ddbvnupER5QhUQtY=",
32
+ "flash_sparse_attention/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY=",
33
+ "flash_sparse_bwd.py": "sPh3T1XiomcbSHb/+fUvYZBYgWZNCuwMJW5UoabQUPE=",
34
+ "flash_sparse_dec.py": "yqOVdw3aVkSoD1aDtqng1bmNn3eWQkTLphVRm+3vBOA=",
35
+ "flash_sparse_fwd.py": "7cUeJrayY/yu/0+FpTqG2foJVfdweaq7KuZsge7/rno=",
36
+ "interface.py": "0b2Ya0TXU/6QS5plzsK6FOStUJNGl49fH7EsspTMkoo=",
37
+ "kernel_repr.py": "GdLRDHO7bMvfWt52vCDqO9HLxQeaK7/sr3iMeeSTJnA=",
38
+ "launch_grid.py": "NKGkqIro3SDRikGl1C7Om0/2DVH0eN8ecepkd6h8A98=",
39
+ "launch_template.py": "CbrryDz3BPY8zpWcTo6hNpI0DKfW16BkY66f0oNNAYs=",
40
+ "mask.py": "aF/KiRajGsdsLdQllFO99NBrOQeTjCPK9aCIunxen/0=",
41
+ "quant.py": "B47A0bWsIb5E5dynB0NrcJjg1l1F/rj/7y6yVXIWrNI=",
42
+ "scheduler.py": "4sgkUQyAuijIyfIk88/kyD4a+dI2LWeptB28Pfe2nko=",
43
+ "seqlen_info.py": "BCd/B9qu9DfS2+VxX9uBdh8qxrdDYgjJzwfp7Q7i6Bc=",
44
+ "utils.py": "g8U91hmiF+yFwiGEVpYzq1k1CMH+3sIrHq0nrOaNrMI="
45
+ }
46
+ }
47
+ }
build/torch-cuda/quant.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import triton
3
+ import triton.language as tl
4
+
5
+
6
+ @triton.jit
7
+ def _amax_kernel(
8
+ X,
9
+ Amax,
10
+ n_elements,
11
+ BLOCK: tl.constexpr,
12
+ ):
13
+ pid = tl.program_id(0)
14
+ offsets = pid * BLOCK + tl.arange(0, BLOCK)
15
+ mask = offsets < n_elements
16
+
17
+ x = tl.load(X + offsets, mask=mask, other=0.0, cache_modifier=".cg")
18
+ local_amax = tl.max(tl.abs(x))
19
+ tl.atomic_max(Amax, local_amax, sem="relaxed")
20
+
21
+
22
+ @triton.jit
23
+ def _quantize_fp8_kernel(
24
+ X,
25
+ Out,
26
+ Scale,
27
+ n_elements,
28
+ BLOCK: tl.constexpr,
29
+ FP8_DTYPE: tl.constexpr,
30
+ ):
31
+ pid = tl.program_id(0)
32
+ offsets = pid * BLOCK + tl.arange(0, BLOCK)
33
+ mask = offsets < n_elements
34
+
35
+ x = tl.load(X + offsets, mask=mask, other=0.0, cache_modifier=".cg")
36
+ scale = tl.load(Scale)
37
+
38
+ x_fp8 = (x * scale).to(FP8_DTYPE)
39
+ tl.store(Out + offsets, x_fp8, mask=mask)
40
+
41
+
42
+ def quantize_fp8(x: torch.Tensor, dtype: torch.dtype = torch.float8_e5m2):
43
+ assert x.is_cuda and x.dtype in (torch.float16, torch.bfloat16, torch.float32)
44
+ assert dtype in (torch.float8_e5m2, torch.float8_e4m3fn)
45
+
46
+ tl_dtype = tl.float8e5 if dtype == torch.float8_e5m2 else tl.float8e4nv
47
+ FP8_MAX = 57344.0 if dtype == torch.float8_e5m2 else 448.0
48
+
49
+ n_elements = x.numel()
50
+ amax = torch.zeros(1, device=x.device, dtype=torch.float32)
51
+ x_fp8 = torch.empty_like(x, dtype=dtype)
52
+
53
+ BLOCK = 512
54
+
55
+ def grid(META):
56
+ return (triton.cdiv(n_elements, META["BLOCK"]),)
57
+
58
+ _amax_kernel[grid](x, amax, n_elements, BLOCK=BLOCK)
59
+
60
+ scale = FP8_MAX / amax
61
+
62
+ _quantize_fp8_kernel[grid](
63
+ x, x_fp8, scale, n_elements, BLOCK=BLOCK, FP8_DTYPE=tl_dtype
64
+ )
65
+
66
+ return x_fp8, (amax / FP8_MAX).to(x.dtype)
build/torch-cuda/scheduler.py ADDED
@@ -0,0 +1,2609 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+ import triton.language as tl
3
+ from triton.language.core import _aggregate as aggregate
4
+ from triton.runtime.jit import constexpr_function
5
+
6
+ from . import mask
7
+ from . import activations
8
+ from . import seqlen_info, block_info
9
+
10
+
11
+ @aggregate
12
+ class AttnFwdGridIndex:
13
+ m_block: tl.tensor
14
+ batch_idx: tl.tensor
15
+ head_idx: tl.tensor
16
+ head_kv_idx: tl.tensor
17
+ split_idx: tl.tensor
18
+
19
+ @constexpr_function
20
+ def __init__(self, m_block, batch_idx, head_idx, head_kv_idx, split_idx):
21
+ self.m_block = m_block
22
+ self.batch_idx = batch_idx
23
+ self.head_idx = head_idx
24
+ self.head_kv_idx = head_kv_idx
25
+ self.split_idx = split_idx
26
+
27
+ @staticmethod
28
+ @triton.jit
29
+ def create(
30
+ num_splits,
31
+ QHEAD_PER_KVHEAD: tl.constexpr,
32
+ IS_SPLIT_KV: tl.constexpr,
33
+ PACK_GQA: tl.constexpr,
34
+ ):
35
+ m_block = tl.program_id(0)
36
+ head_idx = tl.program_id(1)
37
+ batch_split_idx = tl.program_id(2)
38
+ if IS_SPLIT_KV:
39
+ batch_idx = batch_split_idx // num_splits
40
+ split_idx = batch_split_idx - batch_idx * num_splits
41
+ else:
42
+ batch_idx = batch_split_idx
43
+ split_idx = 0
44
+ if PACK_GQA:
45
+ head_kv_idx = head_idx
46
+ else:
47
+ head_kv_idx = head_idx // QHEAD_PER_KVHEAD
48
+ return AttnFwdGridIndex(m_block, batch_idx, head_idx, head_kv_idx, split_idx)
49
+
50
+ @triton.jit
51
+ def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
52
+ if IS_LOCAL:
53
+ window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
54
+ window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
55
+ window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
56
+ window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
57
+ else:
58
+ window_size_sink = 0
59
+ window_size_left = 0
60
+ window_size_right = 0
61
+ window_size_dist = 0
62
+ return window_size_sink, window_size_left, window_size_right, window_size_dist
63
+
64
+
65
+ @aggregate
66
+ class AttnBwdGridIndex:
67
+ n_block: tl.tensor
68
+ batch_idx: tl.tensor
69
+ head_idx: tl.tensor
70
+ head_kv_idx: tl.tensor
71
+ split_idx: tl.tensor
72
+
73
+ @constexpr_function
74
+ def __init__(self, n_block, batch_idx, head_idx, head_kv_idx, split_idx):
75
+ self.n_block = n_block
76
+ self.batch_idx = batch_idx
77
+ self.head_idx = head_idx
78
+ self.head_kv_idx = head_kv_idx
79
+ self.split_idx = split_idx
80
+
81
+ @staticmethod
82
+ @triton.jit
83
+ def create(
84
+ num_splits,
85
+ QHEAD_PER_KVHEAD: tl.constexpr,
86
+ IS_SPLIT_QO: tl.constexpr,
87
+ ):
88
+ n_block = tl.program_id(0)
89
+ head_idx = tl.program_id(1)
90
+ batch_split_idx = tl.program_id(2)
91
+ if IS_SPLIT_QO:
92
+ batch_idx = batch_split_idx // num_splits
93
+ split_idx = batch_split_idx - batch_idx * num_splits
94
+ else:
95
+ batch_idx = batch_split_idx
96
+ split_idx = 0
97
+ head_kv_idx = head_idx // QHEAD_PER_KVHEAD
98
+ return AttnBwdGridIndex(n_block, batch_idx, head_idx, head_kv_idx, split_idx)
99
+
100
+ @triton.jit
101
+ def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
102
+ if IS_LOCAL:
103
+ window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
104
+ window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
105
+ window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
106
+ window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
107
+ else:
108
+ window_size_sink = 0
109
+ window_size_left = 0
110
+ window_size_right = 0
111
+ window_size_dist = 0
112
+ return window_size_sink, window_size_left, window_size_right, window_size_dist
113
+
114
+
115
+ @aggregate
116
+ class AttnDecGridIndex:
117
+ batch_idx: tl.tensor
118
+ head_idx: tl.tensor
119
+ head_kv_idx: tl.tensor
120
+ split_idx: tl.tensor
121
+
122
+ @constexpr_function
123
+ def __init__(self, batch_idx, head_idx, head_kv_idx, split_idx):
124
+ self.batch_idx = batch_idx
125
+ self.head_idx = head_idx
126
+ self.head_kv_idx = head_kv_idx
127
+ self.split_idx = split_idx
128
+
129
+ @staticmethod
130
+ @triton.jit
131
+ def create(
132
+ num_splits,
133
+ ):
134
+ head_idx = tl.program_id(0)
135
+ batch_split_idx = tl.program_id(1)
136
+ batch_idx = batch_split_idx // num_splits
137
+ split_idx = batch_split_idx - batch_idx * num_splits
138
+ head_kv_idx = head_idx
139
+ return AttnDecGridIndex(batch_idx, head_idx, head_kv_idx, split_idx)
140
+
141
+ @triton.jit
142
+ def load_window_sizes(self, window_sizes, stride_wh, IS_LOCAL: tl.constexpr):
143
+ if IS_LOCAL:
144
+ window_size_sink = tl.load(window_sizes + self.head_kv_idx * stride_wh)
145
+ window_size_left = tl.load(window_sizes + self.head_kv_idx * stride_wh + 1)
146
+ window_size_right = tl.load(window_sizes + self.head_kv_idx * stride_wh + 2)
147
+ window_size_dist = tl.load(window_sizes + self.head_kv_idx * stride_wh + 3)
148
+ else:
149
+ window_size_sink = 0
150
+ window_size_left = 0
151
+ window_size_right = 0
152
+ window_size_dist = 0
153
+ return window_size_sink, window_size_left, window_size_right, window_size_dist
154
+
155
+
156
+ @aggregate
157
+ class AttnFwdConfig:
158
+ softmax_scale_log2: tl.tensor
159
+ softmax_threshold_log2: tl.tensor
160
+ gate_threshold_log2: tl.tensor
161
+ value_scale: tl.tensor
162
+ m_block: tl.tensor
163
+ actual_seqlen_q: tl.tensor
164
+ actual_seqlen_k: tl.tensor
165
+ offset_q: tl.tensor
166
+ offset_k: tl.tensor
167
+ padded_offset_q: tl.tensor
168
+ padded_offset_k: tl.tensor
169
+ window_size_sink: tl.tensor
170
+ window_size_left: tl.tensor
171
+ window_size_right: tl.tensor
172
+ window_size_dist: tl.tensor
173
+ head_dim: tl.tensor
174
+ PACK_GQA: tl.constexpr
175
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
176
+ TILE_M: tl.constexpr
177
+ TILE_N: tl.constexpr
178
+ TILE_K: tl.constexpr
179
+ IS_LOGSIGMOID_GATE: tl.constexpr
180
+
181
+ @constexpr_function
182
+ def __init__(
183
+ self,
184
+ softmax_scale_log2,
185
+ softmax_threshold_log2,
186
+ gate_threshold_log2,
187
+ value_scale,
188
+ m_block,
189
+ actual_seqlen_q,
190
+ actual_seqlen_k,
191
+ offset_q,
192
+ offset_k,
193
+ padded_offset_q,
194
+ padded_offset_k,
195
+ window_size_sink,
196
+ window_size_left,
197
+ window_size_right,
198
+ window_size_dist,
199
+ head_dim,
200
+ PACK_GQA,
201
+ QHEAD_PER_KVHEAD_PACKGQA,
202
+ TILE_M,
203
+ TILE_N,
204
+ TILE_K,
205
+ IS_LOGSIGMOID_GATE,
206
+ ):
207
+ self.softmax_scale_log2 = softmax_scale_log2
208
+ self.softmax_threshold_log2 = softmax_threshold_log2
209
+ self.gate_threshold_log2 = gate_threshold_log2
210
+ self.value_scale = value_scale
211
+ self.m_block = m_block
212
+ self.actual_seqlen_q = actual_seqlen_q
213
+ self.actual_seqlen_k = actual_seqlen_k
214
+ self.offset_q = offset_q
215
+ self.offset_k = offset_k
216
+ self.padded_offset_q = padded_offset_q
217
+ self.padded_offset_k = padded_offset_k
218
+ self.window_size_sink = window_size_sink
219
+ self.window_size_left = window_size_left
220
+ self.window_size_right = window_size_right
221
+ self.window_size_dist = window_size_dist
222
+ self.head_dim = head_dim
223
+ self.PACK_GQA = tl.constexpr(PACK_GQA)
224
+ self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
225
+ self.TILE_M = tl.constexpr(TILE_M)
226
+ self.TILE_N = tl.constexpr(TILE_N)
227
+ self.TILE_K = tl.constexpr(TILE_K)
228
+ self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
229
+
230
+ @staticmethod
231
+ @triton.jit
232
+ def create(
233
+ softmax_scale=0.0,
234
+ softmax_threshold=0.0,
235
+ gate_threshold=0.0,
236
+ query_scale=None,
237
+ key_scale=None,
238
+ value_scale=None,
239
+ m_block=0,
240
+ batch_idx=0,
241
+ window_size_sink=0,
242
+ window_size_left=0,
243
+ window_size_right=0,
244
+ window_size_dist=0,
245
+ head_dim=0,
246
+ cu_seqlens_q=None,
247
+ cu_seqlens_k=None,
248
+ seqused_q=None,
249
+ seqused_k=None,
250
+ seqlen_q=0,
251
+ seqlen_k=0,
252
+ PACK_GQA: tl.constexpr = False,
253
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr = 1,
254
+ TILE_M: tl.constexpr = 64,
255
+ TILE_N: tl.constexpr = 64,
256
+ TILE_K: tl.constexpr = 64,
257
+ IS_CAUSAL: tl.constexpr = False,
258
+ IS_LOGSIGMOID_GATE: tl.constexpr = False,
259
+ IS_ADAPT_GATE: tl.constexpr = False,
260
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
261
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
262
+ HAS_SEQUSED_Q: tl.constexpr = False,
263
+ HAS_SEQUSED_K: tl.constexpr = False,
264
+ ):
265
+ # Get seqlen info for this batch
266
+ (
267
+ offset_q,
268
+ offset_k,
269
+ padded_offset_q,
270
+ padded_offset_k,
271
+ actual_seqlen_q,
272
+ actual_seqlen_k,
273
+ ) = seqlen_info.get_seqlen_info_qk(
274
+ batch_idx=batch_idx,
275
+ seqlen_q_static=seqlen_q,
276
+ seqlen_k_static=seqlen_k,
277
+ cu_seqlens_q=cu_seqlens_q,
278
+ cu_seqlens_k=cu_seqlens_k,
279
+ seqused_q=seqused_q,
280
+ seqused_k=seqused_k,
281
+ TILE_M=TILE_M,
282
+ TILE_N=TILE_N,
283
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
284
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
285
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
286
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
287
+ )
288
+ # Load query scale
289
+ q_scale = tl.load(query_scale)
290
+ # Load key scale
291
+ k_scale = tl.load(key_scale)
292
+ # Load value scale
293
+ v_scale = tl.load(value_scale)
294
+ LOG2E: tl.constexpr = 1.44269504089
295
+ softmax_scale_log2 = softmax_scale * LOG2E * q_scale * k_scale
296
+ softmax_threshold_log2 = seqlen_info.get_softmax_threshold(
297
+ softmax_threshold,
298
+ m_block,
299
+ actual_seqlen_q,
300
+ actual_seqlen_k,
301
+ IS_CAUSAL,
302
+ TILE_M,
303
+ QHEAD_PER_KVHEAD_PACKGQA,
304
+ )
305
+ gate_threshold_log2 = seqlen_info.get_gate_threshold(
306
+ gate_threshold,
307
+ m_block,
308
+ actual_seqlen_q,
309
+ actual_seqlen_k,
310
+ IS_CAUSAL,
311
+ TILE_M,
312
+ QHEAD_PER_KVHEAD_PACKGQA,
313
+ IS_ADAPT_GATE,
314
+ )
315
+ return AttnFwdConfig(
316
+ softmax_scale_log2,
317
+ softmax_threshold_log2,
318
+ gate_threshold_log2,
319
+ v_scale,
320
+ m_block,
321
+ actual_seqlen_q,
322
+ actual_seqlen_k,
323
+ offset_q,
324
+ offset_k,
325
+ padded_offset_q,
326
+ padded_offset_k,
327
+ window_size_sink,
328
+ window_size_left,
329
+ window_size_right,
330
+ window_size_dist,
331
+ head_dim,
332
+ PACK_GQA,
333
+ QHEAD_PER_KVHEAD_PACKGQA,
334
+ TILE_M,
335
+ TILE_N,
336
+ TILE_K,
337
+ IS_LOGSIGMOID_GATE,
338
+ )
339
+
340
+ @triton.jit
341
+ def get_offs_m(self):
342
+ return self.m_block * self.TILE_M + tl.arange(0, self.TILE_M)
343
+
344
+ @triton.jit
345
+ def get_offs_k(self):
346
+ return tl.arange(0, self.TILE_K)
347
+
348
+
349
+ @aggregate
350
+ class AttnBwdConfig:
351
+ softmax_scale_log2: tl.tensor
352
+ softmax_threshold: tl.tensor
353
+ gate_threshold: tl.tensor
354
+ query_scale: tl.tensor
355
+ key_scale: tl.tensor
356
+ value_scale: tl.tensor
357
+ n_block: tl.tensor
358
+ actual_seqlen_q: tl.tensor
359
+ actual_seqlen_k: tl.tensor
360
+ offset_q: tl.tensor
361
+ offset_k: tl.tensor
362
+ padded_offset_q: tl.tensor
363
+ padded_offset_k: tl.tensor
364
+ window_size_sink: tl.tensor
365
+ window_size_left: tl.tensor
366
+ window_size_right: tl.tensor
367
+ window_size_dist: tl.tensor
368
+ head_dim: tl.tensor
369
+ QHEAD_PER_KVHEAD: tl.constexpr
370
+ TILE_M: tl.constexpr
371
+ TILE_N: tl.constexpr
372
+ TILE_K: tl.constexpr
373
+ IS_CAUSAL: tl.constexpr
374
+ IS_LOGSIGMOID_GATE: tl.constexpr
375
+ IS_ADAPT_GATE: tl.constexpr
376
+
377
+ @constexpr_function
378
+ def __init__(
379
+ self,
380
+ softmax_scale_log2,
381
+ softmax_threshold,
382
+ gate_threshold,
383
+ query_scale,
384
+ key_scale,
385
+ value_scale,
386
+ n_block,
387
+ actual_seqlen_q,
388
+ actual_seqlen_k,
389
+ offset_q,
390
+ offset_k,
391
+ padded_offset_q,
392
+ padded_offset_k,
393
+ window_size_sink,
394
+ window_size_left,
395
+ window_size_right,
396
+ window_size_dist,
397
+ head_dim,
398
+ QHEAD_PER_KVHEAD,
399
+ TILE_M,
400
+ TILE_N,
401
+ TILE_K,
402
+ IS_CAUSAL,
403
+ IS_LOGSIGMOID_GATE,
404
+ IS_ADAPT_GATE,
405
+ ):
406
+ self.softmax_scale_log2 = softmax_scale_log2
407
+ self.softmax_threshold = softmax_threshold
408
+ self.gate_threshold = gate_threshold
409
+ self.query_scale = query_scale
410
+ self.key_scale = key_scale
411
+ self.value_scale = value_scale
412
+ self.n_block = n_block
413
+ self.actual_seqlen_q = actual_seqlen_q
414
+ self.actual_seqlen_k = actual_seqlen_k
415
+ self.offset_q = offset_q
416
+ self.offset_k = offset_k
417
+ self.padded_offset_q = padded_offset_q
418
+ self.padded_offset_k = padded_offset_k
419
+ self.window_size_sink = window_size_sink
420
+ self.window_size_left = window_size_left
421
+ self.window_size_right = window_size_right
422
+ self.window_size_dist = window_size_dist
423
+ self.head_dim = head_dim
424
+ self.QHEAD_PER_KVHEAD = tl.constexpr(QHEAD_PER_KVHEAD)
425
+ self.TILE_M = tl.constexpr(TILE_M)
426
+ self.TILE_N = tl.constexpr(TILE_N)
427
+ self.TILE_K = tl.constexpr(TILE_K)
428
+ self.IS_CAUSAL = tl.constexpr(IS_CAUSAL)
429
+ self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
430
+ self.IS_ADAPT_GATE = tl.constexpr(IS_ADAPT_GATE)
431
+
432
+ @staticmethod
433
+ @triton.jit
434
+ def create(
435
+ softmax_scale=0.0,
436
+ softmax_threshold=0.0,
437
+ gate_threshold=0.0,
438
+ query_scale=None,
439
+ key_scale=None,
440
+ value_scale=None,
441
+ n_block=0,
442
+ batch_idx=0,
443
+ window_size_sink=0,
444
+ window_size_left=0,
445
+ window_size_right=0,
446
+ window_size_dist=0,
447
+ head_dim=0,
448
+ cu_seqlens_q=None,
449
+ cu_seqlens_k=None,
450
+ seqused_q=None,
451
+ seqused_k=None,
452
+ seqlen_q=0,
453
+ seqlen_k=0,
454
+ QHEAD_PER_KVHEAD: tl.constexpr = 1,
455
+ TILE_M: tl.constexpr = 64,
456
+ TILE_N: tl.constexpr = 64,
457
+ TILE_K: tl.constexpr = 64,
458
+ IS_CAUSAL: tl.constexpr = False,
459
+ IS_LOGSIGMOID_GATE: tl.constexpr = False,
460
+ IS_ADAPT_GATE: tl.constexpr = False,
461
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
462
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
463
+ HAS_SEQUSED_Q: tl.constexpr = False,
464
+ HAS_SEQUSED_K: tl.constexpr = False,
465
+ ):
466
+ # Get seqlen info for this batch
467
+ (
468
+ offset_q,
469
+ offset_k,
470
+ padded_offset_q,
471
+ padded_offset_k,
472
+ actual_seqlen_q,
473
+ actual_seqlen_k,
474
+ ) = seqlen_info.get_seqlen_info_qk(
475
+ batch_idx=batch_idx,
476
+ seqlen_q_static=seqlen_q,
477
+ seqlen_k_static=seqlen_k,
478
+ cu_seqlens_q=cu_seqlens_q,
479
+ cu_seqlens_k=cu_seqlens_k,
480
+ seqused_q=seqused_q,
481
+ seqused_k=seqused_k,
482
+ TILE_M=TILE_M,
483
+ TILE_N=TILE_N,
484
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
485
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
486
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
487
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
488
+ )
489
+ # Load query scale
490
+ q_scale = tl.load(query_scale)
491
+ # Load key scale
492
+ k_scale = tl.load(key_scale)
493
+ # Load value scale
494
+ v_scale = tl.load(value_scale)
495
+ LOG2E: tl.constexpr = 1.44269504089
496
+ softmax_scale_log2 = softmax_scale * LOG2E
497
+ softmax_threshold = tl.full((), softmax_threshold, tl.float32)
498
+ gate_threshold = tl.full((), gate_threshold, tl.float32)
499
+ return AttnBwdConfig(
500
+ softmax_scale_log2,
501
+ softmax_threshold,
502
+ gate_threshold,
503
+ q_scale,
504
+ k_scale,
505
+ v_scale,
506
+ n_block,
507
+ actual_seqlen_q,
508
+ actual_seqlen_k,
509
+ offset_q,
510
+ offset_k,
511
+ padded_offset_q,
512
+ padded_offset_k,
513
+ window_size_sink,
514
+ window_size_left,
515
+ window_size_right,
516
+ window_size_dist,
517
+ head_dim,
518
+ QHEAD_PER_KVHEAD,
519
+ TILE_M,
520
+ TILE_N,
521
+ TILE_K,
522
+ IS_CAUSAL,
523
+ IS_LOGSIGMOID_GATE,
524
+ IS_ADAPT_GATE,
525
+ )
526
+
527
+ @triton.jit
528
+ def get_softmax_threshold_log2(
529
+ self,
530
+ m_block,
531
+ ):
532
+ return seqlen_info.get_softmax_threshold(
533
+ self.softmax_threshold,
534
+ m_block,
535
+ self.actual_seqlen_q,
536
+ self.actual_seqlen_k,
537
+ self.IS_CAUSAL,
538
+ self.TILE_M,
539
+ QHEAD_PER_KVHEAD_PACKGQA=1,
540
+ )
541
+
542
+ @triton.jit
543
+ def get_gate_threshold_log2(
544
+ self,
545
+ m_block,
546
+ ):
547
+ return seqlen_info.get_gate_threshold(
548
+ self.gate_threshold,
549
+ m_block,
550
+ self.actual_seqlen_q,
551
+ self.actual_seqlen_k,
552
+ self.IS_CAUSAL,
553
+ self.TILE_M,
554
+ QHEAD_PER_KVHEAD_PACKGQA=1,
555
+ IS_ADAPT_GATE=self.IS_ADAPT_GATE,
556
+ )
557
+
558
+
559
+ @aggregate
560
+ class AttnDecConfig:
561
+ softmax_scale_log2: tl.tensor
562
+ softmax_threshold_log2: tl.tensor
563
+ gate_threshold_log2: tl.tensor
564
+ value_scale: tl.tensor
565
+ m_block: tl.tensor
566
+ actual_seqlen_q: tl.tensor
567
+ actual_seqlen_k: tl.tensor
568
+ offset_q: tl.tensor
569
+ offset_k: tl.tensor
570
+ padded_offset_q: tl.tensor
571
+ padded_offset_k: tl.tensor
572
+ window_size_sink: tl.tensor
573
+ window_size_left: tl.tensor
574
+ window_size_right: tl.tensor
575
+ window_size_dist: tl.tensor
576
+ head_dim: tl.tensor
577
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
578
+ TILE_M: tl.constexpr
579
+ TILE_N: tl.constexpr
580
+ TILE_K: tl.constexpr
581
+ IS_LOGSIGMOID_GATE: tl.constexpr
582
+
583
+ @constexpr_function
584
+ def __init__(
585
+ self,
586
+ softmax_scale_log2,
587
+ softmax_threshold_log2,
588
+ gate_threshold_log2,
589
+ value_scale,
590
+ m_block,
591
+ actual_seqlen_q,
592
+ actual_seqlen_k,
593
+ offset_q,
594
+ offset_k,
595
+ padded_offset_q,
596
+ padded_offset_k,
597
+ window_size_sink,
598
+ window_size_left,
599
+ window_size_right,
600
+ window_size_dist,
601
+ head_dim,
602
+ QHEAD_PER_KVHEAD_PACKGQA,
603
+ TILE_M,
604
+ TILE_N,
605
+ TILE_K,
606
+ IS_LOGSIGMOID_GATE,
607
+ ):
608
+ self.softmax_scale_log2 = softmax_scale_log2
609
+ self.softmax_threshold_log2 = softmax_threshold_log2
610
+ self.gate_threshold_log2 = gate_threshold_log2
611
+ self.value_scale = value_scale
612
+ self.m_block = m_block
613
+ self.actual_seqlen_q = actual_seqlen_q
614
+ self.actual_seqlen_k = actual_seqlen_k
615
+ self.offset_q = offset_q
616
+ self.offset_k = offset_k
617
+ self.padded_offset_q = padded_offset_q
618
+ self.padded_offset_k = padded_offset_k
619
+ self.window_size_sink = window_size_sink
620
+ self.window_size_left = window_size_left
621
+ self.window_size_right = window_size_right
622
+ self.window_size_dist = window_size_dist
623
+ self.head_dim = head_dim
624
+ self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
625
+ self.TILE_M = tl.constexpr(TILE_M)
626
+ self.TILE_N = tl.constexpr(TILE_N)
627
+ self.TILE_K = tl.constexpr(TILE_K)
628
+ self.IS_LOGSIGMOID_GATE = tl.constexpr(IS_LOGSIGMOID_GATE)
629
+
630
+ @staticmethod
631
+ @triton.jit
632
+ def create(
633
+ softmax_scale=0.0,
634
+ softmax_threshold=0.0,
635
+ gate_threshold=0.0,
636
+ query_scale=None,
637
+ key_scale=None,
638
+ value_scale=None,
639
+ batch_idx=0,
640
+ window_size_sink=0,
641
+ window_size_left=0,
642
+ window_size_right=0,
643
+ window_size_dist=0,
644
+ head_dim=0,
645
+ cu_seqlens_q=None,
646
+ cu_seqlens_k=None,
647
+ seqused_q=None,
648
+ seqused_k=None,
649
+ seqlen_q=0,
650
+ seqlen_k=0,
651
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr = 1,
652
+ TILE_M: tl.constexpr = 16,
653
+ TILE_N: tl.constexpr = 64,
654
+ TILE_K: tl.constexpr = 64,
655
+ IS_LOGSIGMOID_GATE: tl.constexpr = False,
656
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
657
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
658
+ HAS_SEQUSED_Q: tl.constexpr = False,
659
+ HAS_SEQUSED_K: tl.constexpr = False,
660
+ ):
661
+ # Get seqlen info for this batch
662
+ (
663
+ offset_q,
664
+ offset_k,
665
+ padded_offset_q,
666
+ padded_offset_k,
667
+ actual_seqlen_q,
668
+ actual_seqlen_k,
669
+ ) = seqlen_info.get_seqlen_info_qk(
670
+ batch_idx=batch_idx,
671
+ seqlen_q_static=seqlen_q,
672
+ seqlen_k_static=seqlen_k,
673
+ cu_seqlens_q=cu_seqlens_q,
674
+ cu_seqlens_k=cu_seqlens_k,
675
+ seqused_q=seqused_q,
676
+ seqused_k=seqused_k,
677
+ TILE_M=TILE_M,
678
+ TILE_N=TILE_N,
679
+ HAS_CU_SEQLENS_Q=HAS_CU_SEQLENS_Q,
680
+ HAS_CU_SEQLENS_K=HAS_CU_SEQLENS_K,
681
+ HAS_SEQUSED_Q=HAS_SEQUSED_Q,
682
+ HAS_SEQUSED_K=HAS_SEQUSED_K,
683
+ )
684
+ # Load query scale
685
+ q_scale = tl.load(query_scale)
686
+ # Load key scale
687
+ k_scale = tl.load(key_scale)
688
+ # Load value scale
689
+ v_scale = tl.load(value_scale)
690
+ LOG2E: tl.constexpr = 1.44269504089
691
+ softmax_scale_log2 = softmax_scale * LOG2E * q_scale * k_scale
692
+ softmax_threshold_log2 = seqlen_info.get_softmax_threshold(
693
+ softmax_threshold,
694
+ 0,
695
+ actual_seqlen_q,
696
+ actual_seqlen_k,
697
+ False,
698
+ TILE_M,
699
+ QHEAD_PER_KVHEAD_PACKGQA,
700
+ )
701
+ gate_threshold_log2 = seqlen_info.get_gate_threshold(
702
+ gate_threshold,
703
+ 0,
704
+ actual_seqlen_q,
705
+ actual_seqlen_k,
706
+ False,
707
+ TILE_M,
708
+ QHEAD_PER_KVHEAD_PACKGQA,
709
+ False,
710
+ )
711
+ return AttnDecConfig(
712
+ softmax_scale_log2,
713
+ softmax_threshold_log2,
714
+ gate_threshold_log2,
715
+ v_scale,
716
+ tl.full((), 0, tl.int32),
717
+ actual_seqlen_q,
718
+ actual_seqlen_k,
719
+ offset_q,
720
+ offset_k,
721
+ padded_offset_q,
722
+ padded_offset_k,
723
+ window_size_sink,
724
+ window_size_left,
725
+ window_size_right,
726
+ window_size_dist,
727
+ head_dim,
728
+ QHEAD_PER_KVHEAD_PACKGQA,
729
+ TILE_M,
730
+ TILE_N,
731
+ TILE_K,
732
+ IS_LOGSIGMOID_GATE,
733
+ )
734
+
735
+
736
+ @aggregate
737
+ class AttnFwdBlockScheduler:
738
+ n_block_min: tl.tensor
739
+ n_block_max: tl.tensor
740
+ n_block_max_no_mask: tl.tensor
741
+ n_block_window_min: tl.tensor
742
+ n_block_window_max: tl.tensor
743
+ n_block_window_min_no_mask: tl.tensor
744
+ n_block_window_max_no_mask: tl.tensor
745
+ n_block_sink_min: tl.tensor
746
+ n_block_sink_max: tl.tensor
747
+
748
+ @constexpr_function
749
+ def __init__(
750
+ self,
751
+ n_block_min,
752
+ n_block_max,
753
+ n_block_max_no_mask,
754
+ n_block_window_min,
755
+ n_block_window_max,
756
+ n_block_window_min_no_mask,
757
+ n_block_window_max_no_mask,
758
+ n_block_sink_min,
759
+ n_block_sink_max,
760
+ ):
761
+ self.n_block_min = n_block_min
762
+ self.n_block_max = n_block_max
763
+ self.n_block_max_no_mask = n_block_max_no_mask
764
+ self.n_block_window_min = n_block_window_min
765
+ self.n_block_window_max = n_block_window_max
766
+ self.n_block_window_min_no_mask = n_block_window_min_no_mask
767
+ self.n_block_window_max_no_mask = n_block_window_max_no_mask
768
+ self.n_block_sink_min = n_block_sink_min
769
+ self.n_block_sink_max = n_block_sink_max
770
+
771
+ @triton.jit
772
+ def is_empty(self):
773
+ return (
774
+ (self.n_block_max <= self.n_block_min)
775
+ & (self.n_block_window_max <= self.n_block_window_min)
776
+ & (self.n_block_sink_max <= self.n_block_sink_min)
777
+ )
778
+
779
+ @staticmethod
780
+ @triton.jit
781
+ def create(
782
+ config: AttnFwdConfig,
783
+ split_idx,
784
+ num_splits,
785
+ IS_CAUSAL: tl.constexpr,
786
+ IS_LOCAL: tl.constexpr,
787
+ IS_SPLIT_KV: tl.constexpr,
788
+ ):
789
+ # Compute causal n_block range for this m_block
790
+ (
791
+ n_block_min,
792
+ n_block_max,
793
+ n_block_window_min,
794
+ n_block_window_max,
795
+ n_block_sink_min,
796
+ n_block_sink_max,
797
+ ) = block_info.get_n_block_min_max(
798
+ seqlen_q=config.actual_seqlen_q,
799
+ seqlen_k=config.actual_seqlen_k,
800
+ m_block=config.m_block,
801
+ split_idx=split_idx,
802
+ num_splits=num_splits,
803
+ window_size_sink=config.window_size_sink,
804
+ window_size_left=config.window_size_left,
805
+ window_size_right=config.window_size_right,
806
+ window_size_dist=config.window_size_dist,
807
+ TILE_N=config.TILE_N,
808
+ TILE_M=config.TILE_M,
809
+ IS_CAUSAL=IS_CAUSAL,
810
+ IS_LOCAL=IS_LOCAL,
811
+ IS_SPLIT_KV=IS_SPLIT_KV,
812
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
813
+ )
814
+ n_block_max_no_mask = block_info.get_n_block_min_causal_local_mask(
815
+ seqlen_q=config.actual_seqlen_q,
816
+ seqlen_k=config.actual_seqlen_k,
817
+ m_block=config.m_block,
818
+ n_block_min=n_block_min,
819
+ window_size_right=0,
820
+ window_size_dist=0,
821
+ TILE_N=config.TILE_N,
822
+ TILE_M=config.TILE_M,
823
+ IS_LOCAL=False,
824
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
825
+ )
826
+
827
+ # Clamp to split's range so the no-mask loop stays within bounds
828
+ if IS_SPLIT_KV:
829
+ n_block_max_no_mask = tl.minimum(n_block_max_no_mask, n_block_max)
830
+
831
+ if IS_LOCAL:
832
+ # Compute local n_block range for this m_block
833
+ if IS_SPLIT_KV:
834
+ n_block_max_no_mask = tl.where(
835
+ split_idx >= num_splits - 1,
836
+ n_block_min,
837
+ n_block_max_no_mask,
838
+ )
839
+ else:
840
+ n_block_max_no_mask = n_block_min
841
+ if IS_SPLIT_KV:
842
+ n_block_window_min = tl.maximum(n_block_window_min, n_block_min)
843
+ n_block_window_max = tl.minimum(n_block_window_max, n_block_max_no_mask)
844
+ n_block_window_max_no_mask = block_info.get_n_block_min_causal_local_mask(
845
+ seqlen_q=config.actual_seqlen_q,
846
+ seqlen_k=config.actual_seqlen_k,
847
+ m_block=config.m_block,
848
+ n_block_min=n_block_window_min,
849
+ window_size_right=config.window_size_right,
850
+ window_size_dist=config.window_size_dist,
851
+ TILE_N=config.TILE_N,
852
+ TILE_M=config.TILE_M,
853
+ IS_LOCAL=True,
854
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
855
+ )
856
+ n_block_window_min_no_mask = block_info.get_n_block_min_before_local_mask(
857
+ seqlen_q=config.actual_seqlen_q,
858
+ seqlen_k=config.actual_seqlen_k,
859
+ m_block=config.m_block,
860
+ n_block_min=n_block_window_min,
861
+ window_size_left=config.window_size_left,
862
+ window_size_right=config.window_size_right,
863
+ window_size_dist=config.window_size_dist,
864
+ TILE_N=config.TILE_N,
865
+ TILE_M=config.TILE_M,
866
+ IS_LOCAL=True,
867
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
868
+ )
869
+ n_block_window_min_no_mask = tl.minimum(
870
+ n_block_window_min_no_mask, n_block_window_max_no_mask
871
+ )
872
+
873
+ # Clamp window no-mask boundaries to the window's range
874
+ if IS_SPLIT_KV:
875
+ n_block_window_max_no_mask = tl.maximum(
876
+ tl.minimum(n_block_window_max_no_mask, n_block_window_max),
877
+ n_block_window_min,
878
+ )
879
+ n_block_window_min_no_mask = tl.maximum(
880
+ tl.minimum(n_block_window_min_no_mask, n_block_window_max),
881
+ n_block_window_min,
882
+ )
883
+ else:
884
+ n_block_window_min = 0
885
+ n_block_window_max = 0
886
+ n_block_window_min_no_mask = 0
887
+ n_block_window_max_no_mask = 0
888
+
889
+ return AttnFwdBlockScheduler(
890
+ n_block_min,
891
+ n_block_max,
892
+ n_block_max_no_mask,
893
+ n_block_window_min,
894
+ n_block_window_max,
895
+ n_block_window_min_no_mask,
896
+ n_block_window_max_no_mask,
897
+ n_block_sink_min,
898
+ n_block_sink_max,
899
+ )
900
+
901
+
902
+ @aggregate
903
+ class AttnBwdBlockScheduler:
904
+ m_block_min: tl.tensor
905
+ m_block_max: tl.tensor
906
+ m_block_min_no_mask: tl.tensor
907
+ m_block_window_min: tl.tensor
908
+ m_block_window_max: tl.tensor
909
+ m_block_window_min_no_mask: tl.tensor
910
+ m_block_window_max_no_mask: tl.tensor
911
+ m_block_sink_min: tl.tensor
912
+ m_block_sink_max: tl.tensor
913
+
914
+ @constexpr_function
915
+ def __init__(
916
+ self,
917
+ m_block_min,
918
+ m_block_max,
919
+ m_block_min_no_mask,
920
+ m_block_window_min,
921
+ m_block_window_max,
922
+ m_block_window_min_no_mask,
923
+ m_block_window_max_no_mask,
924
+ m_block_sink_min,
925
+ m_block_sink_max,
926
+ ):
927
+ self.m_block_min = m_block_min
928
+ self.m_block_max = m_block_max
929
+ self.m_block_min_no_mask = m_block_min_no_mask
930
+ self.m_block_window_min = m_block_window_min
931
+ self.m_block_window_max = m_block_window_max
932
+ self.m_block_window_min_no_mask = m_block_window_min_no_mask
933
+ self.m_block_window_max_no_mask = m_block_window_max_no_mask
934
+ self.m_block_sink_min = m_block_sink_min
935
+ self.m_block_sink_max = m_block_sink_max
936
+
937
+ @triton.jit
938
+ def is_empty(self):
939
+ return (
940
+ (self.m_block_max <= self.m_block_min)
941
+ & (self.m_block_window_max <= self.m_block_window_min)
942
+ & (self.m_block_sink_max <= self.m_block_sink_min)
943
+ )
944
+
945
+ @staticmethod
946
+ @triton.jit
947
+ def create(
948
+ config: AttnBwdConfig,
949
+ split_idx,
950
+ num_splits,
951
+ IS_CAUSAL: tl.constexpr,
952
+ IS_LOCAL: tl.constexpr,
953
+ IS_SPLIT_QO: tl.constexpr,
954
+ ):
955
+ # Compute causal m_block range for this n_block
956
+ (
957
+ m_block_min,
958
+ m_block_max,
959
+ m_block_window_min,
960
+ m_block_window_max,
961
+ m_block_sink_min,
962
+ m_block_sink_max,
963
+ ) = block_info.get_m_block_min_max(
964
+ seqlen_q=config.actual_seqlen_q,
965
+ seqlen_k=config.actual_seqlen_k,
966
+ n_block=config.n_block,
967
+ split_idx=split_idx,
968
+ num_splits=num_splits,
969
+ window_size_sink=config.window_size_sink,
970
+ window_size_left=config.window_size_left,
971
+ window_size_right=config.window_size_right,
972
+ window_size_dist=config.window_size_dist,
973
+ TILE_N=config.TILE_N,
974
+ TILE_M=config.TILE_M,
975
+ IS_CAUSAL=IS_CAUSAL,
976
+ IS_LOCAL=IS_LOCAL,
977
+ IS_SPLIT_QO=IS_SPLIT_QO,
978
+ )
979
+ m_block_min_no_mask = block_info.get_m_block_min_causal_local_mask(
980
+ seqlen_q=config.actual_seqlen_q,
981
+ seqlen_k=config.actual_seqlen_k,
982
+ n_block=config.n_block,
983
+ m_block_min=m_block_min,
984
+ window_size_right=0,
985
+ window_size_dist=0,
986
+ TILE_N=config.TILE_N,
987
+ TILE_M=config.TILE_M,
988
+ IS_CAUSAL=IS_CAUSAL or IS_LOCAL,
989
+ IS_LOCAL=False,
990
+ )
991
+
992
+ # Clamp to split's range so the no-mask loop stays within bounds
993
+ if IS_SPLIT_QO:
994
+ m_block_min_no_mask = tl.minimum(m_block_min_no_mask, m_block_max)
995
+
996
+ if IS_LOCAL:
997
+ # Compute local m_block range for this n_block
998
+ m_block_min_no_mask = m_block_max
999
+ m_block_window_min = tl.maximum(m_block_window_min, m_block_min_no_mask)
1000
+ m_block_window_min_no_mask = block_info.get_m_block_min_causal_local_mask(
1001
+ seqlen_q=config.actual_seqlen_q,
1002
+ seqlen_k=config.actual_seqlen_k,
1003
+ n_block=config.n_block,
1004
+ m_block_min=m_block_window_min,
1005
+ window_size_right=config.window_size_right,
1006
+ window_size_dist=config.window_size_dist,
1007
+ TILE_N=config.TILE_N,
1008
+ TILE_M=config.TILE_M,
1009
+ IS_CAUSAL=False,
1010
+ IS_LOCAL=True,
1011
+ )
1012
+ m_block_window_min_no_mask = tl.maximum(
1013
+ m_block_window_min_no_mask, m_block_window_min
1014
+ )
1015
+ m_block_window_max_no_mask = block_info.get_m_block_max_before_local_mask(
1016
+ seqlen_q=config.actual_seqlen_q,
1017
+ seqlen_k=config.actual_seqlen_k,
1018
+ n_block=config.n_block,
1019
+ m_block_max=m_block_window_max,
1020
+ window_size_left=config.window_size_left,
1021
+ window_size_right=config.window_size_right,
1022
+ window_size_dist=config.window_size_dist,
1023
+ TILE_N=config.TILE_N,
1024
+ TILE_M=config.TILE_M,
1025
+ IS_LOCAL=True,
1026
+ )
1027
+ m_block_window_max_no_mask = tl.maximum(
1028
+ m_block_window_max_no_mask, m_block_window_min_no_mask
1029
+ )
1030
+ m_block_window_min_no_mask = tl.maximum(
1031
+ tl.minimum(m_block_window_min_no_mask, m_block_window_max),
1032
+ m_block_window_min,
1033
+ )
1034
+ m_block_window_max_no_mask = tl.maximum(
1035
+ tl.minimum(m_block_window_max_no_mask, m_block_window_max),
1036
+ m_block_window_min_no_mask,
1037
+ )
1038
+ else:
1039
+ m_block_window_min_no_mask = 0
1040
+ m_block_window_max_no_mask = 0
1041
+ m_block_sink_min = 0
1042
+ m_block_sink_max = 0
1043
+
1044
+ return AttnBwdBlockScheduler(
1045
+ m_block_min,
1046
+ m_block_max,
1047
+ m_block_min_no_mask,
1048
+ m_block_window_min,
1049
+ m_block_window_max,
1050
+ m_block_window_min_no_mask,
1051
+ m_block_window_max_no_mask,
1052
+ m_block_sink_min,
1053
+ m_block_sink_max,
1054
+ )
1055
+
1056
+
1057
+ @aggregate
1058
+ class AttnDecBlockScheduler:
1059
+ n_block_min: tl.tensor
1060
+ n_block_max: tl.tensor
1061
+ n_block_max_no_mask: tl.tensor
1062
+ n_block_window_min: tl.tensor
1063
+ n_block_window_max: tl.tensor
1064
+ n_block_window_min_no_mask: tl.tensor
1065
+ n_block_window_max_no_mask: tl.tensor
1066
+ n_block_sink_min: tl.tensor
1067
+ n_block_sink_max: tl.tensor
1068
+
1069
+ @constexpr_function
1070
+ def __init__(
1071
+ self,
1072
+ n_block_min,
1073
+ n_block_max,
1074
+ n_block_max_no_mask,
1075
+ n_block_window_min,
1076
+ n_block_window_max,
1077
+ n_block_window_min_no_mask,
1078
+ n_block_window_max_no_mask,
1079
+ n_block_sink_min,
1080
+ n_block_sink_max,
1081
+ ):
1082
+ self.n_block_min = n_block_min
1083
+ self.n_block_max = n_block_max
1084
+ self.n_block_max_no_mask = n_block_max_no_mask
1085
+ self.n_block_window_min = n_block_window_min
1086
+ self.n_block_window_max = n_block_window_max
1087
+ self.n_block_window_min_no_mask = n_block_window_min_no_mask
1088
+ self.n_block_window_max_no_mask = n_block_window_max_no_mask
1089
+ self.n_block_sink_min = n_block_sink_min
1090
+ self.n_block_sink_max = n_block_sink_max
1091
+
1092
+ @triton.jit
1093
+ def is_empty(self):
1094
+ return (
1095
+ (self.n_block_max <= self.n_block_min)
1096
+ & (self.n_block_window_max <= self.n_block_window_min)
1097
+ & (self.n_block_sink_max <= self.n_block_sink_min)
1098
+ )
1099
+
1100
+ @staticmethod
1101
+ @triton.jit
1102
+ def create(
1103
+ config: AttnDecConfig,
1104
+ split_idx,
1105
+ num_splits,
1106
+ IS_LOCAL: tl.constexpr,
1107
+ ):
1108
+ # Compute non-causal n_block range for this m_block
1109
+ (
1110
+ n_block_min,
1111
+ n_block_max,
1112
+ n_block_window_min,
1113
+ n_block_window_max,
1114
+ n_block_sink_min,
1115
+ n_block_sink_max,
1116
+ ) = block_info.get_n_block_min_max(
1117
+ seqlen_q=1,
1118
+ seqlen_k=config.actual_seqlen_k,
1119
+ m_block=0,
1120
+ split_idx=split_idx,
1121
+ num_splits=num_splits,
1122
+ window_size_sink=config.window_size_sink,
1123
+ window_size_left=config.window_size_left,
1124
+ window_size_right=config.window_size_right,
1125
+ window_size_dist=config.window_size_dist,
1126
+ TILE_N=config.TILE_N,
1127
+ TILE_M=config.TILE_M,
1128
+ IS_CAUSAL=False,
1129
+ IS_LOCAL=IS_LOCAL,
1130
+ IS_SPLIT_KV=True,
1131
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1132
+ )
1133
+ n_block_max_no_mask = block_info.get_n_block_min_causal_local_mask(
1134
+ seqlen_q=1,
1135
+ seqlen_k=config.actual_seqlen_k,
1136
+ m_block=0,
1137
+ n_block_min=n_block_min,
1138
+ window_size_right=0,
1139
+ window_size_dist=0,
1140
+ TILE_N=config.TILE_N,
1141
+ TILE_M=config.TILE_M,
1142
+ IS_LOCAL=False,
1143
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1144
+ )
1145
+
1146
+ # Clamp to split's range so the no-mask loop stays within bounds
1147
+ n_block_max_no_mask = tl.minimum(n_block_max_no_mask, n_block_max)
1148
+
1149
+ if IS_LOCAL:
1150
+ # Compute local n_block range for this m_block
1151
+ n_block_max_no_mask = tl.where(
1152
+ split_idx >= num_splits - 1,
1153
+ n_block_min,
1154
+ n_block_max_no_mask,
1155
+ )
1156
+ n_block_window_min = tl.maximum(n_block_window_min, n_block_min)
1157
+ n_block_window_max = tl.minimum(n_block_window_max, n_block_max_no_mask)
1158
+ n_block_window_max_no_mask = block_info.get_n_block_min_causal_local_mask(
1159
+ seqlen_q=1,
1160
+ seqlen_k=config.actual_seqlen_k,
1161
+ m_block=0,
1162
+ n_block_min=n_block_window_min,
1163
+ window_size_right=config.window_size_right,
1164
+ window_size_dist=config.window_size_dist,
1165
+ TILE_N=config.TILE_N,
1166
+ TILE_M=config.TILE_M,
1167
+ IS_LOCAL=True,
1168
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1169
+ )
1170
+ n_block_window_min_no_mask = block_info.get_n_block_min_before_local_mask(
1171
+ seqlen_q=1,
1172
+ seqlen_k=config.actual_seqlen_k,
1173
+ m_block=0,
1174
+ n_block_min=n_block_window_min,
1175
+ window_size_left=config.window_size_left,
1176
+ window_size_right=config.window_size_right,
1177
+ window_size_dist=config.window_size_dist,
1178
+ TILE_N=config.TILE_N,
1179
+ TILE_M=config.TILE_M,
1180
+ IS_LOCAL=True,
1181
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1182
+ )
1183
+ n_block_window_min_no_mask = tl.minimum(
1184
+ n_block_window_min_no_mask, n_block_window_max_no_mask
1185
+ )
1186
+
1187
+ # Clamp window no-mask boundaries to the window's range
1188
+ n_block_window_max_no_mask = tl.maximum(
1189
+ tl.minimum(n_block_window_max_no_mask, n_block_window_max),
1190
+ n_block_window_min,
1191
+ )
1192
+ n_block_window_min_no_mask = tl.maximum(
1193
+ tl.minimum(n_block_window_min_no_mask, n_block_window_max),
1194
+ n_block_window_min,
1195
+ )
1196
+ else:
1197
+ n_block_window_min = 0
1198
+ n_block_window_max = 0
1199
+ n_block_window_min_no_mask = 0
1200
+ n_block_window_max_no_mask = 0
1201
+
1202
+ return AttnDecBlockScheduler(
1203
+ n_block_min,
1204
+ n_block_max,
1205
+ n_block_max_no_mask,
1206
+ n_block_window_min,
1207
+ n_block_window_max,
1208
+ n_block_window_min_no_mask,
1209
+ n_block_window_max_no_mask,
1210
+ n_block_sink_min,
1211
+ n_block_sink_max,
1212
+ )
1213
+
1214
+
1215
+ @aggregate
1216
+ class AttnFwdPointerScheduler:
1217
+ q_base: tl.tensor
1218
+ k_base: tl.tensor
1219
+ v_base: tl.tensor
1220
+ a_base: tl.tensor
1221
+ d_base: tl.tensor
1222
+ out_base: tl.tensor
1223
+ lse_base: tl.tensor
1224
+ head_idx: tl.tensor
1225
+ stride_qh: tl.tensor
1226
+ stride_qm: tl.tensor
1227
+ stride_kn: tl.tensor
1228
+ stride_vn: tl.tensor
1229
+ stride_ah: tl.tensor
1230
+ stride_oh: tl.tensor
1231
+ stride_om: tl.tensor
1232
+ stride_lh: tl.tensor
1233
+
1234
+ @constexpr_function
1235
+ def __init__(
1236
+ self,
1237
+ q_base,
1238
+ k_base,
1239
+ v_base,
1240
+ a_base,
1241
+ d_base,
1242
+ out_base,
1243
+ lse_base,
1244
+ head_idx,
1245
+ stride_qh,
1246
+ stride_qm,
1247
+ stride_kn,
1248
+ stride_vn,
1249
+ stride_ah,
1250
+ stride_oh,
1251
+ stride_om,
1252
+ stride_lh,
1253
+ ):
1254
+ self.q_base = q_base
1255
+ self.k_base = k_base
1256
+ self.v_base = v_base
1257
+ self.a_base = a_base
1258
+ self.d_base = d_base
1259
+ self.out_base = out_base
1260
+ self.lse_base = lse_base
1261
+ self.head_idx = head_idx
1262
+ self.stride_qh = stride_qh
1263
+ self.stride_qm = stride_qm
1264
+ self.stride_kn = stride_kn
1265
+ self.stride_vn = stride_vn
1266
+ self.stride_ah = stride_ah
1267
+ self.stride_oh = stride_oh
1268
+ self.stride_om = stride_om
1269
+ self.stride_lh = stride_lh
1270
+
1271
+ @staticmethod
1272
+ @triton.jit
1273
+ def create(
1274
+ config: AttnFwdConfig,
1275
+ Q=None,
1276
+ K=None,
1277
+ V=None,
1278
+ A=None,
1279
+ D=None,
1280
+ Out=None,
1281
+ Lse=None,
1282
+ batch_idx=0,
1283
+ head_idx=0,
1284
+ head_kv_idx=0,
1285
+ split_idx=0,
1286
+ stride_qb=0,
1287
+ stride_qh=0,
1288
+ stride_qm=0,
1289
+ stride_kb=0,
1290
+ stride_kh=0,
1291
+ stride_kn=0,
1292
+ stride_vb=0,
1293
+ stride_vh=0,
1294
+ stride_vn=0,
1295
+ stride_ab=0,
1296
+ stride_ah=0,
1297
+ stride_am=0,
1298
+ stride_db=0,
1299
+ stride_dh=0,
1300
+ stride_dn=0,
1301
+ stride_ob=0,
1302
+ stride_oh=0,
1303
+ stride_om=0,
1304
+ stride_os=0,
1305
+ stride_lb=0,
1306
+ stride_lh=0,
1307
+ stride_ls=0,
1308
+ IS_SPLIT_KV: tl.constexpr = False,
1309
+ IS_GATED: tl.constexpr = False,
1310
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
1311
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
1312
+ ):
1313
+ # Initialize base pointers
1314
+ q_base = seqlen_info.offset_batch_Q(
1315
+ Q + head_idx * stride_qh if not config.PACK_GQA else Q,
1316
+ batch_idx,
1317
+ config.offset_q,
1318
+ config.padded_offset_q,
1319
+ stride_qb,
1320
+ stride_qm,
1321
+ HAS_CU_SEQLENS_Q,
1322
+ USE_PADDED=False,
1323
+ )
1324
+ k_base = seqlen_info.offset_batch_K(
1325
+ K + head_kv_idx * stride_kh,
1326
+ batch_idx,
1327
+ config.offset_k,
1328
+ config.padded_offset_k,
1329
+ stride_kb,
1330
+ stride_kn,
1331
+ HAS_CU_SEQLENS_K,
1332
+ USE_PADDED=False,
1333
+ )
1334
+ v_base = seqlen_info.offset_batch_K(
1335
+ V + head_kv_idx * stride_vh,
1336
+ batch_idx,
1337
+ config.offset_k,
1338
+ config.padded_offset_k,
1339
+ stride_vb,
1340
+ stride_vn,
1341
+ HAS_CU_SEQLENS_K,
1342
+ USE_PADDED=False,
1343
+ )
1344
+ out_base = seqlen_info.offset_batch_Q(
1345
+ Out + head_idx * stride_oh if not config.PACK_GQA else Out,
1346
+ batch_idx,
1347
+ config.offset_q,
1348
+ config.padded_offset_q,
1349
+ stride_ob,
1350
+ stride_om,
1351
+ HAS_CU_SEQLENS_Q,
1352
+ USE_PADDED=False,
1353
+ )
1354
+ lse_base = seqlen_info.offset_batch_Q(
1355
+ Lse + head_idx * stride_lh if not config.PACK_GQA else Lse,
1356
+ batch_idx,
1357
+ config.offset_q,
1358
+ config.padded_offset_q,
1359
+ stride_lb,
1360
+ 1,
1361
+ HAS_CU_SEQLENS_Q,
1362
+ USE_PADDED=False,
1363
+ )
1364
+
1365
+ if IS_GATED:
1366
+ a_base = seqlen_info.offset_batch_Q(
1367
+ A + head_idx * stride_ah if not config.PACK_GQA else A,
1368
+ batch_idx,
1369
+ config.offset_q,
1370
+ config.padded_offset_q,
1371
+ stride_ab,
1372
+ stride_am,
1373
+ HAS_CU_SEQLENS_Q,
1374
+ USE_PADDED=False,
1375
+ )
1376
+ d_base = seqlen_info.offset_batch_K(
1377
+ D + head_kv_idx * stride_dh,
1378
+ batch_idx,
1379
+ config.offset_k,
1380
+ config.padded_offset_k,
1381
+ stride_db,
1382
+ stride_dn,
1383
+ HAS_CU_SEQLENS_K,
1384
+ USE_PADDED=False,
1385
+ )
1386
+ else:
1387
+ a_base = tl.full((), 0, tl.int64)
1388
+ d_base = tl.full((), 0, tl.int64)
1389
+ stride_ah = tl.full((), 0, tl.int64)
1390
+
1391
+ # For split KV, offset output and LSE base pointers by split_idx
1392
+ if IS_SPLIT_KV:
1393
+ out_base += split_idx * stride_os
1394
+ lse_base += split_idx * stride_ls
1395
+
1396
+ # TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
1397
+ return AttnFwdPointerScheduler(
1398
+ q_base,
1399
+ k_base,
1400
+ v_base,
1401
+ a_base,
1402
+ d_base,
1403
+ out_base,
1404
+ lse_base,
1405
+ head_idx,
1406
+ stride_qh + tl.full((), 0, tl.int64),
1407
+ stride_qm + tl.full((), 0, tl.int64),
1408
+ stride_kn + tl.full((), 0, tl.int64),
1409
+ stride_vn + tl.full((), 0, tl.int64),
1410
+ stride_ah + tl.full((), 0, tl.int64),
1411
+ stride_oh + tl.full((), 0, tl.int64),
1412
+ stride_om + tl.full((), 0, tl.int64),
1413
+ stride_lh + tl.full((), 0, tl.int64),
1414
+ )
1415
+
1416
+ @triton.jit
1417
+ def make_q_ptrs(self, config: AttnFwdConfig):
1418
+ if config.PACK_GQA:
1419
+ return seqlen_info.make_pack_gqa_ptrs(
1420
+ self.q_base,
1421
+ config.m_block,
1422
+ self.head_idx,
1423
+ self.stride_qh,
1424
+ self.stride_qm,
1425
+ TILE_M=config.TILE_M,
1426
+ TILE_K=config.TILE_K,
1427
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1428
+ )
1429
+ else:
1430
+ return tl.make_tensor_descriptor(
1431
+ self.q_base,
1432
+ shape=[config.actual_seqlen_q, config.head_dim],
1433
+ strides=[self.stride_qm, 1],
1434
+ block_shape=[config.TILE_M, config.TILE_K],
1435
+ )
1436
+
1437
+ @triton.jit
1438
+ def make_k_ptrs(self, config: AttnFwdConfig):
1439
+ return tl.make_tensor_descriptor(
1440
+ self.k_base,
1441
+ shape=[config.actual_seqlen_k, config.head_dim],
1442
+ strides=[self.stride_kn, 1],
1443
+ block_shape=[config.TILE_N, config.TILE_K],
1444
+ )
1445
+
1446
+ @triton.jit
1447
+ def make_v_ptrs(self, config: AttnFwdConfig):
1448
+ return tl.make_tensor_descriptor(
1449
+ self.v_base,
1450
+ shape=[config.actual_seqlen_k, config.head_dim],
1451
+ strides=[self.stride_vn, 1],
1452
+ block_shape=[config.TILE_N, config.TILE_K],
1453
+ )
1454
+
1455
+ @triton.jit
1456
+ def make_a_ptrs(self, config: AttnFwdConfig):
1457
+ if config.PACK_GQA:
1458
+ return seqlen_info.make_pack_gqa_ptrs(
1459
+ self.a_base,
1460
+ config.m_block,
1461
+ self.head_idx,
1462
+ self.stride_ah,
1463
+ 1,
1464
+ TILE_M=config.TILE_M,
1465
+ TILE_K=1,
1466
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1467
+ )
1468
+ else:
1469
+ return tl.make_tensor_descriptor(
1470
+ self.a_base,
1471
+ shape=[config.actual_seqlen_q],
1472
+ strides=[1],
1473
+ block_shape=[config.TILE_M],
1474
+ )
1475
+
1476
+ @triton.jit
1477
+ def make_d_ptrs(self, config: AttnFwdConfig):
1478
+ return tl.make_tensor_descriptor(
1479
+ self.d_base,
1480
+ shape=[config.actual_seqlen_k],
1481
+ strides=[1],
1482
+ block_shape=[config.TILE_N],
1483
+ )
1484
+
1485
+ @triton.jit
1486
+ def make_out_ptrs(self, config: AttnFwdConfig):
1487
+ if config.PACK_GQA:
1488
+ return seqlen_info.make_pack_gqa_ptrs(
1489
+ self.out_base,
1490
+ config.m_block,
1491
+ self.head_idx,
1492
+ self.stride_oh,
1493
+ self.stride_om,
1494
+ TILE_M=config.TILE_M,
1495
+ TILE_K=config.TILE_K,
1496
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1497
+ )
1498
+ else:
1499
+ return tl.make_tensor_descriptor(
1500
+ self.out_base,
1501
+ shape=[config.actual_seqlen_q, config.head_dim],
1502
+ strides=[self.stride_om, 1],
1503
+ block_shape=[config.TILE_M, config.TILE_K],
1504
+ )
1505
+
1506
+ @triton.jit
1507
+ def make_lse_ptrs(self, config: AttnFwdConfig):
1508
+ if config.PACK_GQA:
1509
+ return seqlen_info.make_pack_gqa_ptrs(
1510
+ self.lse_base,
1511
+ config.m_block,
1512
+ self.head_idx,
1513
+ self.stride_lh,
1514
+ 1,
1515
+ TILE_M=config.TILE_M,
1516
+ TILE_K=1,
1517
+ QHEAD_PER_KVHEAD_PACKGQA=config.QHEAD_PER_KVHEAD_PACKGQA,
1518
+ )
1519
+ else:
1520
+ return tl.make_tensor_descriptor(
1521
+ self.lse_base,
1522
+ shape=[config.actual_seqlen_q],
1523
+ strides=[1],
1524
+ block_shape=[config.TILE_M],
1525
+ )
1526
+
1527
+ @triton.jit
1528
+ def load_q(self, config: AttnFwdConfig, q_ptrs):
1529
+ if config.PACK_GQA:
1530
+ offs_m = config.get_offs_m()
1531
+ offs_k = config.get_offs_k()
1532
+ return tl.load(
1533
+ q_ptrs,
1534
+ mask=(
1535
+ (offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
1536
+ )[:, None]
1537
+ & (offs_k < config.head_dim)[None, :],
1538
+ other=0.0,
1539
+ cache_modifier=".ca",
1540
+ )
1541
+ else:
1542
+ return q_ptrs.load([config.m_block * config.TILE_M, 0])
1543
+
1544
+ @triton.jit
1545
+ def load_k(self, config: AttnFwdConfig, k_ptrs, n_block):
1546
+ return k_ptrs.load([n_block * config.TILE_N, 0])
1547
+
1548
+ @triton.jit
1549
+ def load_v(self, config: AttnFwdConfig, v_ptrs, n_block):
1550
+ return v_ptrs.load([n_block * config.TILE_N, 0])
1551
+
1552
+ @triton.jit
1553
+ def load_a(self, config: AttnFwdConfig, a_ptrs):
1554
+ if config.PACK_GQA:
1555
+ offs_m = config.get_offs_m()
1556
+ return tl.load(
1557
+ a_ptrs,
1558
+ mask=(offs_m // config.QHEAD_PER_KVHEAD_PACKGQA)
1559
+ < config.actual_seqlen_q,
1560
+ other=0.0,
1561
+ ).to(tl.float32)
1562
+ else:
1563
+ return a_ptrs.load([config.m_block * config.TILE_M]).to(tl.float32)
1564
+
1565
+ @triton.jit
1566
+ def load_d(self, config: AttnFwdConfig, d_ptrs, n_block):
1567
+ return d_ptrs.load([n_block * config.TILE_N]).to(tl.float32)
1568
+
1569
+ @triton.jit
1570
+ def store_out(
1571
+ self, config: AttnFwdConfig, out_ptrs, o_tile, IS_SPLIT_KV: tl.constexpr = False
1572
+ ):
1573
+ if not IS_SPLIT_KV:
1574
+ if config.PACK_GQA:
1575
+ o_tile = o_tile.to(out_ptrs.dtype.element_ty)
1576
+ else:
1577
+ o_tile = o_tile.to(out_ptrs.dtype)
1578
+ if config.PACK_GQA:
1579
+ offs_m = config.get_offs_m()
1580
+ offs_k = config.get_offs_k()
1581
+ tl.store(
1582
+ out_ptrs,
1583
+ o_tile,
1584
+ mask=(
1585
+ (offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
1586
+ )[:, None]
1587
+ & (offs_k < config.head_dim)[None, :],
1588
+ cache_modifier=".wb",
1589
+ )
1590
+ else:
1591
+ out_ptrs.store([config.m_block * config.TILE_M, 0], o_tile)
1592
+
1593
+ @triton.jit
1594
+ def store_lse(self, config: AttnFwdConfig, lse_ptrs, lse_tile):
1595
+ if config.PACK_GQA:
1596
+ offs_m = config.get_offs_m()
1597
+ tl.store(
1598
+ lse_ptrs,
1599
+ lse_tile,
1600
+ mask=(
1601
+ (offs_m // config.QHEAD_PER_KVHEAD_PACKGQA) < config.actual_seqlen_q
1602
+ ),
1603
+ cache_modifier=".wb",
1604
+ )
1605
+ else:
1606
+ lse_ptrs.store([config.m_block * config.TILE_M], lse_tile)
1607
+
1608
+ @triton.jit
1609
+ def store_empty(
1610
+ self,
1611
+ config: AttnFwdConfig,
1612
+ out_ptrs,
1613
+ lse_ptrs,
1614
+ IS_SPLIT_KV: tl.constexpr = False,
1615
+ ):
1616
+ lse_tile = tl.full((config.TILE_M,), float("-inf"), dtype=tl.float32)
1617
+ self.store_lse(config, lse_ptrs, lse_tile)
1618
+ o_tile = tl.zeros((config.TILE_M, config.TILE_K), dtype=tl.float32)
1619
+ self.store_out(config, out_ptrs, o_tile, IS_SPLIT_KV=IS_SPLIT_KV)
1620
+
1621
+
1622
+ @aggregate
1623
+ class AttnBwdPointerScheduler:
1624
+ q_base: tl.tensor
1625
+ k_base: tl.tensor
1626
+ v_base: tl.tensor
1627
+ a_base: tl.tensor
1628
+ d_base: tl.tensor
1629
+ do_base: tl.tensor
1630
+ lse_base: tl.tensor
1631
+ dpsum_base: tl.tensor
1632
+ dq_accum_base: tl.tensor
1633
+ dk_base: tl.tensor
1634
+ dv_base: tl.tensor
1635
+ da_base: tl.tensor
1636
+ dd_base: tl.tensor
1637
+ stride_qm: tl.tensor
1638
+ stride_kn: tl.tensor
1639
+ stride_vn: tl.tensor
1640
+ stride_dom: tl.tensor
1641
+ stride_dqam: tl.tensor
1642
+ stride_dkn: tl.tensor
1643
+ stride_dvn: tl.tensor
1644
+
1645
+ @constexpr_function
1646
+ def __init__(
1647
+ self,
1648
+ q_base,
1649
+ k_base,
1650
+ v_base,
1651
+ a_base,
1652
+ d_base,
1653
+ do_base,
1654
+ lse_base,
1655
+ dpsum_base,
1656
+ dq_accum_base,
1657
+ dk_base,
1658
+ dv_base,
1659
+ da_base,
1660
+ dd_base,
1661
+ stride_qm,
1662
+ stride_kn,
1663
+ stride_vn,
1664
+ stride_dom,
1665
+ stride_dqam,
1666
+ stride_dkn,
1667
+ stride_dvn,
1668
+ ):
1669
+ self.q_base = q_base
1670
+ self.k_base = k_base
1671
+ self.v_base = v_base
1672
+ self.a_base = a_base
1673
+ self.d_base = d_base
1674
+ self.do_base = do_base
1675
+ self.lse_base = lse_base
1676
+ self.dpsum_base = dpsum_base
1677
+ self.dq_accum_base = dq_accum_base
1678
+ self.dk_base = dk_base
1679
+ self.dv_base = dv_base
1680
+ self.da_base = da_base
1681
+ self.dd_base = dd_base
1682
+ self.stride_qm = stride_qm
1683
+ self.stride_kn = stride_kn
1684
+ self.stride_vn = stride_vn
1685
+ self.stride_dom = stride_dom
1686
+ self.stride_dqam = stride_dqam
1687
+ self.stride_dkn = stride_dkn
1688
+ self.stride_dvn = stride_dvn
1689
+
1690
+ @staticmethod
1691
+ @triton.jit
1692
+ def create(
1693
+ config: AttnBwdConfig,
1694
+ Q=None,
1695
+ K=None,
1696
+ V=None,
1697
+ A=None,
1698
+ D=None,
1699
+ dO=None,
1700
+ LSELog2=None,
1701
+ dPsum=None,
1702
+ dQaccum=None,
1703
+ dK=None,
1704
+ dV=None,
1705
+ dA=None,
1706
+ dD=None,
1707
+ batch_idx=0,
1708
+ head_idx=0,
1709
+ head_kv_idx=0,
1710
+ split_idx=0,
1711
+ stride_qb=0,
1712
+ stride_qh=0,
1713
+ stride_qm=0,
1714
+ stride_kb=0,
1715
+ stride_kh=0,
1716
+ stride_kn=0,
1717
+ stride_vb=0,
1718
+ stride_vh=0,
1719
+ stride_vn=0,
1720
+ stride_ab=0,
1721
+ stride_ah=0,
1722
+ stride_db=0,
1723
+ stride_dh=0,
1724
+ stride_dob=0,
1725
+ stride_doh=0,
1726
+ stride_dom=0,
1727
+ stride_lb=0,
1728
+ stride_lh=0,
1729
+ stride_pb=0,
1730
+ stride_ph=0,
1731
+ stride_dqab=0,
1732
+ stride_dqah=0,
1733
+ stride_dqam=0,
1734
+ stride_dkb=0,
1735
+ stride_dkh=0,
1736
+ stride_dkn=0,
1737
+ stride_dks=0,
1738
+ stride_dvb=0,
1739
+ stride_dvh=0,
1740
+ stride_dvn=0,
1741
+ stride_dvs=0,
1742
+ stride_dab=0,
1743
+ stride_dah=0,
1744
+ stride_ddb=0,
1745
+ stride_ddh=0,
1746
+ stride_dds=0,
1747
+ IS_SPLIT_QO: tl.constexpr = False,
1748
+ IS_GATED: tl.constexpr = False,
1749
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
1750
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
1751
+ ):
1752
+ # Initialize base pointers
1753
+ q_base = seqlen_info.offset_batch_Q(
1754
+ Q + head_idx * stride_qh,
1755
+ batch_idx,
1756
+ config.offset_q,
1757
+ config.padded_offset_q,
1758
+ stride_qb,
1759
+ stride_qm,
1760
+ HAS_CU_SEQLENS_Q,
1761
+ USE_PADDED=False,
1762
+ )
1763
+ k_base = seqlen_info.offset_batch_K(
1764
+ K + head_kv_idx * stride_kh,
1765
+ batch_idx,
1766
+ config.offset_k,
1767
+ config.padded_offset_k,
1768
+ stride_kb,
1769
+ stride_kn,
1770
+ HAS_CU_SEQLENS_K,
1771
+ USE_PADDED=False,
1772
+ )
1773
+ v_base = seqlen_info.offset_batch_K(
1774
+ V + head_kv_idx * stride_vh,
1775
+ batch_idx,
1776
+ config.offset_k,
1777
+ config.padded_offset_k,
1778
+ stride_vb,
1779
+ stride_vn,
1780
+ HAS_CU_SEQLENS_K,
1781
+ USE_PADDED=False,
1782
+ )
1783
+ do_base = seqlen_info.offset_batch_Q(
1784
+ dO + head_idx * stride_doh,
1785
+ batch_idx,
1786
+ config.offset_q,
1787
+ config.padded_offset_q,
1788
+ stride_dob,
1789
+ stride_dom,
1790
+ HAS_CU_SEQLENS_Q,
1791
+ USE_PADDED=False,
1792
+ )
1793
+ lse_base = seqlen_info.offset_batch_Q(
1794
+ LSELog2 + head_idx * stride_lh,
1795
+ batch_idx,
1796
+ config.offset_q,
1797
+ config.padded_offset_q,
1798
+ stride_lb,
1799
+ 1,
1800
+ HAS_CU_SEQLENS_Q,
1801
+ USE_PADDED=True,
1802
+ )
1803
+ dpsum_base = seqlen_info.offset_batch_Q(
1804
+ dPsum + head_idx * stride_ph,
1805
+ batch_idx,
1806
+ config.offset_q,
1807
+ config.padded_offset_q,
1808
+ stride_pb,
1809
+ 1,
1810
+ HAS_CU_SEQLENS_Q,
1811
+ USE_PADDED=True,
1812
+ )
1813
+ dq_accum_base = seqlen_info.offset_batch_Q(
1814
+ dQaccum + head_idx * stride_dqah,
1815
+ batch_idx,
1816
+ config.offset_q,
1817
+ config.padded_offset_q,
1818
+ stride_dqab,
1819
+ stride_dqam,
1820
+ HAS_CU_SEQLENS_Q,
1821
+ USE_PADDED=True,
1822
+ )
1823
+ dk_base = seqlen_info.offset_batch_K(
1824
+ dK + head_kv_idx * stride_dkh,
1825
+ batch_idx,
1826
+ config.offset_k,
1827
+ config.padded_offset_k,
1828
+ stride_dkb,
1829
+ stride_dkn,
1830
+ HAS_CU_SEQLENS_K,
1831
+ USE_PADDED=False,
1832
+ )
1833
+ dv_base = seqlen_info.offset_batch_K(
1834
+ dV + head_kv_idx * stride_dvh,
1835
+ batch_idx,
1836
+ config.offset_k,
1837
+ config.padded_offset_k,
1838
+ stride_dvb,
1839
+ stride_dvn,
1840
+ HAS_CU_SEQLENS_K,
1841
+ USE_PADDED=False,
1842
+ )
1843
+
1844
+ if IS_GATED:
1845
+ a_base = seqlen_info.offset_batch_Q(
1846
+ A + head_idx * stride_ah,
1847
+ batch_idx,
1848
+ config.offset_q,
1849
+ config.padded_offset_q,
1850
+ stride_ab,
1851
+ 1,
1852
+ HAS_CU_SEQLENS_Q,
1853
+ USE_PADDED=False,
1854
+ )
1855
+ d_base = seqlen_info.offset_batch_K(
1856
+ D + head_kv_idx * stride_dh,
1857
+ batch_idx,
1858
+ config.offset_k,
1859
+ config.padded_offset_k,
1860
+ stride_db,
1861
+ 1,
1862
+ HAS_CU_SEQLENS_K,
1863
+ USE_PADDED=False,
1864
+ )
1865
+ da_base = seqlen_info.offset_batch_Q(
1866
+ dA + head_idx * stride_dah,
1867
+ batch_idx,
1868
+ config.offset_q,
1869
+ config.padded_offset_q,
1870
+ stride_dab,
1871
+ 1,
1872
+ HAS_CU_SEQLENS_Q,
1873
+ USE_PADDED=True,
1874
+ )
1875
+ dd_base = seqlen_info.offset_batch_K(
1876
+ dD + head_kv_idx * stride_ddh,
1877
+ batch_idx,
1878
+ config.offset_k,
1879
+ config.padded_offset_k,
1880
+ stride_ddb,
1881
+ 1,
1882
+ HAS_CU_SEQLENS_K,
1883
+ USE_PADDED=False,
1884
+ )
1885
+ else:
1886
+ a_base = tl.full((), 0, tl.int64)
1887
+ d_base = tl.full((), 0, tl.int64)
1888
+ da_base = tl.full((), 0, tl.int64)
1889
+ dd_base = tl.full((), 0, tl.int64)
1890
+
1891
+ # For split QO, offset key and value gradients base pointers by split_idx
1892
+ if IS_SPLIT_QO:
1893
+ dk_base += split_idx * stride_dks
1894
+ dv_base += split_idx * stride_dvs
1895
+ if IS_GATED:
1896
+ dd_base += split_idx * stride_dds
1897
+
1898
+ # TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
1899
+ return AttnBwdPointerScheduler(
1900
+ q_base,
1901
+ k_base,
1902
+ v_base,
1903
+ a_base,
1904
+ d_base,
1905
+ do_base,
1906
+ lse_base,
1907
+ dpsum_base,
1908
+ dq_accum_base,
1909
+ dk_base,
1910
+ dv_base,
1911
+ da_base,
1912
+ dd_base,
1913
+ stride_qm + tl.full((), 0, tl.int64),
1914
+ stride_kn + tl.full((), 0, tl.int64),
1915
+ stride_vn + tl.full((), 0, tl.int64),
1916
+ stride_dom + tl.full((), 0, tl.int64),
1917
+ stride_dqam + tl.full((), 0, tl.int64),
1918
+ stride_dkn + tl.full((), 0, tl.int64),
1919
+ stride_dvn + tl.full((), 0, tl.int64),
1920
+ )
1921
+
1922
+ @triton.jit
1923
+ def make_q_ptrs(self, config: AttnBwdConfig):
1924
+ return tl.make_tensor_descriptor(
1925
+ self.q_base,
1926
+ shape=[config.actual_seqlen_q, config.head_dim],
1927
+ strides=[self.stride_qm, 1],
1928
+ block_shape=[config.TILE_M, config.TILE_K],
1929
+ )
1930
+
1931
+ @triton.jit
1932
+ def make_k_ptrs(self, config: AttnBwdConfig):
1933
+ return tl.make_tensor_descriptor(
1934
+ self.k_base,
1935
+ shape=[config.actual_seqlen_k, config.head_dim],
1936
+ strides=[self.stride_kn, 1],
1937
+ block_shape=[config.TILE_N, config.TILE_K],
1938
+ )
1939
+
1940
+ @triton.jit
1941
+ def make_v_ptrs(self, config: AttnBwdConfig):
1942
+ return tl.make_tensor_descriptor(
1943
+ self.v_base,
1944
+ shape=[config.actual_seqlen_k, config.head_dim],
1945
+ strides=[self.stride_vn, 1],
1946
+ block_shape=[config.TILE_N, config.TILE_K],
1947
+ )
1948
+
1949
+ @triton.jit
1950
+ def make_a_ptrs(self, config: AttnBwdConfig):
1951
+ return tl.make_tensor_descriptor(
1952
+ self.a_base,
1953
+ shape=[config.actual_seqlen_q],
1954
+ strides=[1],
1955
+ block_shape=[config.TILE_M],
1956
+ )
1957
+
1958
+ @triton.jit
1959
+ def make_d_ptrs(self, config: AttnBwdConfig):
1960
+ return tl.make_tensor_descriptor(
1961
+ self.d_base,
1962
+ shape=[config.actual_seqlen_k],
1963
+ strides=[1],
1964
+ block_shape=[config.TILE_N],
1965
+ )
1966
+
1967
+ @triton.jit
1968
+ def make_do_ptrs(self, config: AttnBwdConfig):
1969
+ return tl.make_tensor_descriptor(
1970
+ self.do_base,
1971
+ shape=[config.actual_seqlen_q, config.head_dim],
1972
+ strides=[self.stride_dom, 1],
1973
+ block_shape=[config.TILE_M, config.TILE_K],
1974
+ )
1975
+
1976
+ @triton.jit
1977
+ def make_lse_ptrs(self, config: AttnBwdConfig):
1978
+ return tl.make_tensor_descriptor(
1979
+ self.lse_base,
1980
+ shape=[config.actual_seqlen_q],
1981
+ strides=[1],
1982
+ block_shape=[config.TILE_M],
1983
+ )
1984
+
1985
+ @triton.jit
1986
+ def make_dpsum_ptrs(self, config: AttnBwdConfig):
1987
+ return tl.make_tensor_descriptor(
1988
+ self.dpsum_base,
1989
+ shape=[config.actual_seqlen_q],
1990
+ strides=[1],
1991
+ block_shape=[config.TILE_M],
1992
+ )
1993
+
1994
+ @triton.jit
1995
+ def make_dq_accum_ptrs(self, config: AttnBwdConfig):
1996
+ return tl.make_tensor_descriptor(
1997
+ self.dq_accum_base,
1998
+ shape=[config.actual_seqlen_q, config.head_dim],
1999
+ strides=[self.stride_dqam, 1],
2000
+ block_shape=[config.TILE_M, config.TILE_K],
2001
+ )
2002
+
2003
+ @triton.jit
2004
+ def make_dk_ptrs(self, config: AttnBwdConfig):
2005
+ return tl.make_tensor_descriptor(
2006
+ self.dk_base,
2007
+ shape=[config.actual_seqlen_k, config.head_dim],
2008
+ strides=[self.stride_dkn, 1],
2009
+ block_shape=[config.TILE_N, config.TILE_K],
2010
+ )
2011
+
2012
+ @triton.jit
2013
+ def make_dv_ptrs(self, config: AttnBwdConfig):
2014
+ return tl.make_tensor_descriptor(
2015
+ self.dv_base,
2016
+ shape=[config.actual_seqlen_k, config.head_dim],
2017
+ strides=[self.stride_dvn, 1],
2018
+ block_shape=[config.TILE_N, config.TILE_K],
2019
+ )
2020
+
2021
+ @triton.jit
2022
+ def make_da_ptrs(self, config: AttnBwdConfig):
2023
+ return tl.make_tensor_descriptor(
2024
+ self.da_base,
2025
+ shape=[config.actual_seqlen_q],
2026
+ strides=[1],
2027
+ block_shape=[config.TILE_M],
2028
+ )
2029
+
2030
+ @triton.jit
2031
+ def make_dd_ptrs(self, config: AttnBwdConfig):
2032
+ return tl.make_tensor_descriptor(
2033
+ self.dd_base,
2034
+ shape=[config.actual_seqlen_k],
2035
+ strides=[1],
2036
+ block_shape=[config.TILE_N],
2037
+ )
2038
+
2039
+ @triton.jit
2040
+ def load_q(self, config: AttnBwdConfig, q_ptrs, m_block):
2041
+ return (q_ptrs.load([m_block * config.TILE_M, 0]) * config.query_scale).to(
2042
+ self.do_base.dtype.element_ty
2043
+ )
2044
+
2045
+ @triton.jit
2046
+ def load_k(self, config: AttnBwdConfig, k_ptrs):
2047
+ return (k_ptrs.load([config.n_block * config.TILE_N, 0]) * config.key_scale).to(
2048
+ self.do_base.dtype.element_ty
2049
+ )
2050
+
2051
+ @triton.jit
2052
+ def load_v(self, config: AttnBwdConfig, v_ptrs):
2053
+ return (
2054
+ v_ptrs.load([config.n_block * config.TILE_N, 0]) * config.value_scale
2055
+ ).to(self.do_base.dtype.element_ty)
2056
+
2057
+ @triton.jit
2058
+ def load_a(self, config: AttnBwdConfig, a_ptrs, m_block):
2059
+ return a_ptrs.load([m_block * config.TILE_M]).to(tl.float32)
2060
+
2061
+ @triton.jit
2062
+ def load_d(self, config: AttnBwdConfig, d_ptrs):
2063
+ return d_ptrs.load([config.n_block * config.TILE_N]).to(tl.float32)
2064
+
2065
+ @triton.jit
2066
+ def load_do(self, config: AttnBwdConfig, do_ptrs, m_block):
2067
+ return do_ptrs.load([m_block * config.TILE_M, 0])
2068
+
2069
+ @triton.jit
2070
+ def load_lse(self, config: AttnBwdConfig, lse_ptrs, m_block):
2071
+ return lse_ptrs.load([m_block * config.TILE_M])
2072
+
2073
+ @triton.jit
2074
+ def load_dpsum(self, config: AttnBwdConfig, dpsum_ptrs, m_block):
2075
+ return dpsum_ptrs.load([m_block * config.TILE_M])
2076
+
2077
+ @triton.jit
2078
+ def store_dq(self, config: AttnBwdConfig, dq_ptrs, m_block, dq_tile):
2079
+ dq_ptrs.atomic_add([m_block * config.TILE_M, 0], dq_tile)
2080
+
2081
+ @triton.jit
2082
+ def store_dk(self, config: AttnBwdConfig, dk_ptrs, dk_tile):
2083
+ if config.QHEAD_PER_KVHEAD > 1:
2084
+ dk_ptrs.atomic_add([config.n_block * config.TILE_N, 0], dk_tile)
2085
+ else:
2086
+ dk_ptrs.store([config.n_block * config.TILE_N, 0], dk_tile)
2087
+
2088
+ @triton.jit
2089
+ def store_dv(self, config: AttnBwdConfig, dv_ptrs, dv_tile):
2090
+ if config.QHEAD_PER_KVHEAD > 1:
2091
+ dv_ptrs.atomic_add([config.n_block * config.TILE_N, 0], dv_tile)
2092
+ else:
2093
+ dv_ptrs.store([config.n_block * config.TILE_N, 0], dv_tile)
2094
+
2095
+ @triton.jit
2096
+ def store_da(self, config: AttnBwdConfig, da_ptrs, m_block, da_tile):
2097
+ da_ptrs.atomic_add([m_block * config.TILE_M], da_tile)
2098
+
2099
+ @triton.jit
2100
+ def store_dd(self, config: AttnBwdConfig, dd_ptrs, dd_tile):
2101
+ if config.QHEAD_PER_KVHEAD > 1:
2102
+ dd_ptrs.atomic_add([config.n_block * config.TILE_N], dd_tile)
2103
+ else:
2104
+ dd_ptrs.store([config.n_block * config.TILE_N], dd_tile)
2105
+
2106
+
2107
+ @aggregate
2108
+ class AttnDecPointerScheduler:
2109
+ q_base: tl.tensor
2110
+ k_base: tl.tensor
2111
+ v_base: tl.tensor
2112
+ a_base: tl.tensor
2113
+ d_base: tl.tensor
2114
+ out_base: tl.tensor
2115
+ lse_base: tl.tensor
2116
+ stride_qh: tl.tensor
2117
+ stride_kn: tl.tensor
2118
+ stride_vn: tl.tensor
2119
+ stride_oh: tl.tensor
2120
+
2121
+ @constexpr_function
2122
+ def __init__(
2123
+ self,
2124
+ q_base,
2125
+ k_base,
2126
+ v_base,
2127
+ a_base,
2128
+ d_base,
2129
+ out_base,
2130
+ lse_base,
2131
+ stride_qh,
2132
+ stride_kn,
2133
+ stride_vn,
2134
+ stride_oh,
2135
+ ):
2136
+ self.q_base = q_base
2137
+ self.k_base = k_base
2138
+ self.v_base = v_base
2139
+ self.a_base = a_base
2140
+ self.d_base = d_base
2141
+ self.out_base = out_base
2142
+ self.lse_base = lse_base
2143
+ self.stride_qh = stride_qh
2144
+ self.stride_kn = stride_kn
2145
+ self.stride_vn = stride_vn
2146
+ self.stride_oh = stride_oh
2147
+
2148
+ @staticmethod
2149
+ @triton.jit
2150
+ def create(
2151
+ config: AttnDecConfig,
2152
+ Q=None,
2153
+ K=None,
2154
+ V=None,
2155
+ A=None,
2156
+ D=None,
2157
+ Out=None,
2158
+ Lse=None,
2159
+ batch_idx=0,
2160
+ head_idx=0,
2161
+ head_kv_idx=0,
2162
+ split_idx=0,
2163
+ stride_qb=0,
2164
+ stride_qh=0,
2165
+ stride_qm=0,
2166
+ stride_kb=0,
2167
+ stride_kh=0,
2168
+ stride_kn=0,
2169
+ stride_vb=0,
2170
+ stride_vh=0,
2171
+ stride_vn=0,
2172
+ stride_ab=0,
2173
+ stride_ah=0,
2174
+ stride_db=0,
2175
+ stride_dh=0,
2176
+ stride_ob=0,
2177
+ stride_oh=0,
2178
+ stride_om=0,
2179
+ stride_os=0,
2180
+ stride_lb=0,
2181
+ stride_lh=0,
2182
+ stride_lm=0,
2183
+ stride_ls=0,
2184
+ IS_GATED: tl.constexpr = False,
2185
+ HAS_CU_SEQLENS_Q: tl.constexpr = False,
2186
+ HAS_CU_SEQLENS_K: tl.constexpr = False,
2187
+ ):
2188
+ # Initialize base pointers
2189
+ q_base = seqlen_info.offset_batch_Q(
2190
+ Q + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_qh,
2191
+ batch_idx,
2192
+ config.offset_q,
2193
+ config.padded_offset_q,
2194
+ stride_qb,
2195
+ stride_qm,
2196
+ HAS_CU_SEQLENS_Q,
2197
+ USE_PADDED=False,
2198
+ )
2199
+ k_base = seqlen_info.offset_batch_K(
2200
+ K + head_kv_idx * stride_kh,
2201
+ batch_idx,
2202
+ config.offset_k,
2203
+ config.padded_offset_k,
2204
+ stride_kb,
2205
+ stride_kn,
2206
+ HAS_CU_SEQLENS_K,
2207
+ USE_PADDED=False,
2208
+ )
2209
+ v_base = seqlen_info.offset_batch_K(
2210
+ V + head_kv_idx * stride_vh,
2211
+ batch_idx,
2212
+ config.offset_k,
2213
+ config.padded_offset_k,
2214
+ stride_vb,
2215
+ stride_vn,
2216
+ HAS_CU_SEQLENS_K,
2217
+ USE_PADDED=False,
2218
+ )
2219
+ out_base = seqlen_info.offset_batch_Q(
2220
+ Out + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_oh,
2221
+ batch_idx,
2222
+ config.offset_q,
2223
+ config.padded_offset_q,
2224
+ stride_ob,
2225
+ stride_om,
2226
+ HAS_CU_SEQLENS_Q,
2227
+ USE_PADDED=False,
2228
+ )
2229
+ lse_base = seqlen_info.offset_batch_Q(
2230
+ Lse + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_lh,
2231
+ batch_idx,
2232
+ config.offset_q,
2233
+ config.padded_offset_q,
2234
+ stride_lb,
2235
+ stride_lm,
2236
+ HAS_CU_SEQLENS_Q,
2237
+ USE_PADDED=False,
2238
+ )
2239
+
2240
+ if IS_GATED:
2241
+ a_base = seqlen_info.offset_batch_Q(
2242
+ A + head_idx * config.QHEAD_PER_KVHEAD_PACKGQA * stride_ah,
2243
+ batch_idx,
2244
+ config.offset_q,
2245
+ config.padded_offset_q,
2246
+ stride_ab,
2247
+ 1,
2248
+ HAS_CU_SEQLENS_Q,
2249
+ USE_PADDED=False,
2250
+ )
2251
+ d_base = seqlen_info.offset_batch_K(
2252
+ D + head_kv_idx * stride_dh,
2253
+ batch_idx,
2254
+ config.offset_k,
2255
+ config.padded_offset_k,
2256
+ stride_db,
2257
+ 1,
2258
+ HAS_CU_SEQLENS_K,
2259
+ USE_PADDED=False,
2260
+ )
2261
+ else:
2262
+ a_base = tl.full((), 0, tl.int64)
2263
+ d_base = tl.full((), 0, tl.int64)
2264
+
2265
+ # For split KV, offset output and LSE base pointers by split_idx
2266
+ out_base += split_idx * stride_os
2267
+ lse_base += split_idx * stride_ls
2268
+
2269
+ # TODO: remove once Triton aggregate accepts host scalar strides or callers materialize strides
2270
+ return AttnDecPointerScheduler(
2271
+ q_base,
2272
+ k_base,
2273
+ v_base,
2274
+ a_base,
2275
+ d_base,
2276
+ out_base,
2277
+ lse_base,
2278
+ stride_qh + tl.full((), 0, tl.int64),
2279
+ stride_kn + tl.full((), 0, tl.int64),
2280
+ stride_vn + tl.full((), 0, tl.int64),
2281
+ stride_oh + tl.full((), 0, tl.int64),
2282
+ )
2283
+
2284
+ @triton.jit
2285
+ def make_q_ptrs(self, config: AttnDecConfig):
2286
+ return tl.make_tensor_descriptor(
2287
+ self.q_base,
2288
+ shape=[config.QHEAD_PER_KVHEAD_PACKGQA, config.head_dim],
2289
+ strides=[self.stride_qh, 1],
2290
+ block_shape=[config.TILE_M, config.TILE_K],
2291
+ )
2292
+
2293
+ @triton.jit
2294
+ def make_k_ptrs(self, config: AttnDecConfig):
2295
+ return tl.make_tensor_descriptor(
2296
+ self.k_base,
2297
+ shape=[config.actual_seqlen_k, config.head_dim],
2298
+ strides=[self.stride_kn, 1],
2299
+ block_shape=[config.TILE_N, config.TILE_K],
2300
+ )
2301
+
2302
+ @triton.jit
2303
+ def make_v_ptrs(self, config: AttnDecConfig):
2304
+ return tl.make_tensor_descriptor(
2305
+ self.v_base,
2306
+ shape=[config.actual_seqlen_k, config.head_dim],
2307
+ strides=[self.stride_vn, 1],
2308
+ block_shape=[config.TILE_N, config.TILE_K],
2309
+ )
2310
+
2311
+ @triton.jit
2312
+ def make_a_ptrs(self, config: AttnDecConfig):
2313
+ return tl.make_tensor_descriptor(
2314
+ self.a_base,
2315
+ shape=[config.QHEAD_PER_KVHEAD_PACKGQA],
2316
+ strides=[1],
2317
+ block_shape=[config.TILE_M],
2318
+ )
2319
+
2320
+ @triton.jit
2321
+ def make_d_ptrs(self, config: AttnDecConfig):
2322
+ return tl.make_tensor_descriptor(
2323
+ self.d_base,
2324
+ shape=[config.actual_seqlen_k],
2325
+ strides=[1],
2326
+ block_shape=[config.TILE_N],
2327
+ )
2328
+
2329
+ @triton.jit
2330
+ def make_out_ptrs(self, config: AttnDecConfig):
2331
+ return tl.make_tensor_descriptor(
2332
+ self.out_base,
2333
+ shape=[config.QHEAD_PER_KVHEAD_PACKGQA, config.head_dim],
2334
+ strides=[self.stride_oh, 1],
2335
+ block_shape=[config.TILE_M, config.TILE_K],
2336
+ )
2337
+
2338
+ @triton.jit
2339
+ def make_lse_ptrs(self, config: AttnDecConfig):
2340
+ return tl.make_tensor_descriptor(
2341
+ self.lse_base,
2342
+ shape=[config.QHEAD_PER_KVHEAD_PACKGQA],
2343
+ strides=[1],
2344
+ block_shape=[config.TILE_M],
2345
+ )
2346
+
2347
+ @triton.jit
2348
+ def load_q(self, config: AttnDecConfig, q_ptrs):
2349
+ return q_ptrs.load([0, 0])
2350
+
2351
+ @triton.jit
2352
+ def load_k(self, config: AttnDecConfig, k_ptrs, n_block):
2353
+ return k_ptrs.load([n_block * config.TILE_N, 0])
2354
+
2355
+ @triton.jit
2356
+ def load_v(self, config: AttnDecConfig, v_ptrs, n_block):
2357
+ return v_ptrs.load([n_block * config.TILE_N, 0])
2358
+
2359
+ @triton.jit
2360
+ def load_a(self, config: AttnDecConfig, a_ptrs):
2361
+ return a_ptrs.load([0]).to(tl.float32)
2362
+
2363
+ @triton.jit
2364
+ def load_d(self, config: AttnDecConfig, d_ptrs, n_block):
2365
+ return d_ptrs.load([n_block * config.TILE_N]).to(tl.float32)
2366
+
2367
+ @triton.jit
2368
+ def store_out(self, config: AttnDecConfig, out_ptrs, o_tile):
2369
+ out_ptrs.store([0, 0], o_tile)
2370
+
2371
+ @triton.jit
2372
+ def store_lse(self, config: AttnDecConfig, lse_ptrs, lse_tile):
2373
+ lse_ptrs.store([0], lse_tile)
2374
+
2375
+ @triton.jit
2376
+ def store_empty(self, config: AttnDecConfig, out_ptrs, lse_ptrs, Out):
2377
+ lse_tile = tl.full((config.TILE_M,), float("-inf"), dtype=tl.float32)
2378
+ self.store_lse(config, lse_ptrs, lse_tile)
2379
+ o_tile = tl.zeros((config.TILE_M, config.TILE_K), dtype=Out.dtype.element_ty)
2380
+ self.store_out(config, out_ptrs, o_tile)
2381
+
2382
+
2383
+ @aggregate
2384
+ class AttnMaskScheduler:
2385
+ fixed_block: tl.tensor
2386
+ actual_seqlen_q: tl.tensor
2387
+ actual_seqlen_k: tl.tensor
2388
+ window_size_sink: tl.tensor
2389
+ window_size_left: tl.tensor
2390
+ window_size_right: tl.tensor
2391
+ window_size_dist: tl.tensor
2392
+ TILE_M: tl.constexpr
2393
+ TILE_N: tl.constexpr
2394
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr
2395
+ SWAP_AB: tl.constexpr
2396
+
2397
+ @constexpr_function
2398
+ def __init__(
2399
+ self,
2400
+ fixed_block,
2401
+ actual_seqlen_q,
2402
+ actual_seqlen_k,
2403
+ window_size_sink,
2404
+ window_size_left,
2405
+ window_size_right,
2406
+ window_size_dist,
2407
+ TILE_M,
2408
+ TILE_N,
2409
+ QHEAD_PER_KVHEAD_PACKGQA,
2410
+ SWAP_AB,
2411
+ ):
2412
+ self.fixed_block = fixed_block
2413
+ self.actual_seqlen_q = actual_seqlen_q
2414
+ self.actual_seqlen_k = actual_seqlen_k
2415
+ self.window_size_sink = window_size_sink
2416
+ self.window_size_left = window_size_left
2417
+ self.window_size_right = window_size_right
2418
+ self.window_size_dist = window_size_dist
2419
+ self.TILE_M = tl.constexpr(TILE_M)
2420
+ self.TILE_N = tl.constexpr(TILE_N)
2421
+ self.QHEAD_PER_KVHEAD_PACKGQA = tl.constexpr(QHEAD_PER_KVHEAD_PACKGQA)
2422
+ self.SWAP_AB = tl.constexpr(SWAP_AB)
2423
+
2424
+ @staticmethod
2425
+ @triton.jit
2426
+ def create(
2427
+ config,
2428
+ SWAP_AB: tl.constexpr = False,
2429
+ ):
2430
+ if not SWAP_AB:
2431
+ return AttnMaskScheduler(
2432
+ config.m_block,
2433
+ config.actual_seqlen_q,
2434
+ config.actual_seqlen_k,
2435
+ config.window_size_sink,
2436
+ config.window_size_left,
2437
+ config.window_size_right,
2438
+ config.window_size_dist,
2439
+ config.TILE_M,
2440
+ config.TILE_N,
2441
+ config.QHEAD_PER_KVHEAD_PACKGQA,
2442
+ False,
2443
+ )
2444
+ else:
2445
+ return AttnMaskScheduler(
2446
+ config.n_block,
2447
+ config.actual_seqlen_q,
2448
+ config.actual_seqlen_k,
2449
+ config.window_size_sink,
2450
+ config.window_size_left,
2451
+ config.window_size_right,
2452
+ config.window_size_dist,
2453
+ config.TILE_M,
2454
+ config.TILE_N,
2455
+ 1,
2456
+ True,
2457
+ )
2458
+
2459
+ @triton.jit
2460
+ def apply_mask(
2461
+ self,
2462
+ acc_s,
2463
+ iter_block,
2464
+ MASK_CAUSAL: tl.constexpr = False,
2465
+ MASK_LOCAL: tl.constexpr = False,
2466
+ MASK_SINK: tl.constexpr = False,
2467
+ ):
2468
+ if not self.SWAP_AB:
2469
+ m_block = self.fixed_block
2470
+ n_block = iter_block
2471
+ else:
2472
+ m_block = iter_block
2473
+ n_block = self.fixed_block
2474
+ return mask.apply_mask(
2475
+ acc_s=acc_s,
2476
+ m_block=m_block,
2477
+ n_block=n_block,
2478
+ seqlen_q=self.actual_seqlen_q,
2479
+ seqlen_k=self.actual_seqlen_k,
2480
+ window_size_sink=self.window_size_sink,
2481
+ window_size_left=self.window_size_left,
2482
+ window_size_right=self.window_size_right,
2483
+ window_size_dist=self.window_size_dist,
2484
+ MASK_SEQLEN=True,
2485
+ MASK_CAUSAL=MASK_CAUSAL,
2486
+ MASK_LOCAL=MASK_LOCAL,
2487
+ MASK_SINK=MASK_SINK,
2488
+ TILE_M=self.TILE_M,
2489
+ TILE_N=self.TILE_N,
2490
+ QHEAD_PER_KVHEAD_PACKGQA=self.QHEAD_PER_KVHEAD_PACKGQA,
2491
+ SWAP_AB=self.SWAP_AB,
2492
+ )
2493
+
2494
+
2495
+ @aggregate
2496
+ class SoftmaxScheduler:
2497
+ softmax_scale_log2: tl.tensor
2498
+ value_scale: tl.tensor
2499
+
2500
+ @constexpr_function
2501
+ def __init__(
2502
+ self,
2503
+ softmax_scale_log2,
2504
+ value_scale,
2505
+ ):
2506
+ self.softmax_scale_log2 = softmax_scale_log2
2507
+ self.value_scale = value_scale
2508
+
2509
+ @staticmethod
2510
+ @triton.jit
2511
+ def create(config):
2512
+ return SoftmaxScheduler(
2513
+ config.softmax_scale_log2,
2514
+ config.value_scale,
2515
+ )
2516
+
2517
+ @triton.jit
2518
+ def online_softmax(
2519
+ self,
2520
+ acc_s,
2521
+ row_max,
2522
+ row_sum,
2523
+ CHECK_INF: tl.constexpr = False,
2524
+ RESCALE_THRESHOLD: tl.constexpr = 0.0,
2525
+ ):
2526
+ return activations.online_softmax(
2527
+ acc_s=acc_s,
2528
+ row_max=row_max,
2529
+ row_sum=row_sum,
2530
+ scale_log2=self.softmax_scale_log2,
2531
+ CHECK_INF=CHECK_INF,
2532
+ RESCALE_THRESHOLD=RESCALE_THRESHOLD,
2533
+ )
2534
+
2535
+ @triton.jit
2536
+ def online_sparse_softmax(
2537
+ self,
2538
+ acc_s,
2539
+ row_max,
2540
+ row_sum,
2541
+ softmax_threshold_log2,
2542
+ CHECK_INF: tl.constexpr = False,
2543
+ ):
2544
+ return activations.online_sparse_softmax(
2545
+ acc_s=acc_s,
2546
+ row_max=row_max,
2547
+ row_sum=row_sum,
2548
+ scale_log2=self.softmax_scale_log2,
2549
+ softmax_threshold_log2=softmax_threshold_log2,
2550
+ CHECK_INF=CHECK_INF,
2551
+ )
2552
+
2553
+ @triton.jit
2554
+ def rescale_o(
2555
+ self,
2556
+ acc_o,
2557
+ row_scale,
2558
+ LAZY_RESCALE: tl.constexpr = False,
2559
+ ):
2560
+ return activations.rescale_o(
2561
+ acc_o=acc_o,
2562
+ row_scale=row_scale,
2563
+ LAZY_RESCALE=LAZY_RESCALE,
2564
+ )
2565
+
2566
+ @triton.jit
2567
+ def finalize(
2568
+ self,
2569
+ row_max,
2570
+ row_sum,
2571
+ IS_LOG2: tl.constexpr = False,
2572
+ CHECK_NAN: tl.constexpr = True,
2573
+ ):
2574
+ return activations.finalize(
2575
+ row_max=row_max,
2576
+ row_sum=row_sum,
2577
+ scale_log2=self.softmax_scale_log2,
2578
+ final_scale=self.value_scale,
2579
+ IS_LOG2=IS_LOG2,
2580
+ CHECK_NAN=CHECK_NAN,
2581
+ )
2582
+
2583
+ @triton.jit
2584
+ def log_sigmoid(
2585
+ self,
2586
+ acc_s,
2587
+ FASTMATH: tl.constexpr = False,
2588
+ ):
2589
+ return activations.log_sigmoid(x=acc_s, FASTMATH=FASTMATH)
2590
+
2591
+ @triton.jit
2592
+ def online_gate(
2593
+ self,
2594
+ a_max,
2595
+ a_min,
2596
+ d_max,
2597
+ d_min,
2598
+ gate_max,
2599
+ gate_threshold_log2,
2600
+ ):
2601
+ return activations.online_gate(
2602
+ a_max=a_max,
2603
+ a_min=a_min,
2604
+ d_max=d_max,
2605
+ d_min=d_min,
2606
+ gate_max=gate_max,
2607
+ scale_log2=self.softmax_scale_log2,
2608
+ gate_threshold_log2=gate_threshold_log2,
2609
+ )
build/torch-cuda/seqlen_info.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import triton
2
+ import triton.language as tl
3
+
4
+
5
+ @triton.jit
6
+ def get_seqlen_info(
7
+ batch_idx,
8
+ seqlen_static,
9
+ cu_seqlens,
10
+ seqused,
11
+ HAS_CU_SEQLENS: tl.constexpr,
12
+ HAS_SEQUSED: tl.constexpr,
13
+ ):
14
+ """
15
+ Get offset and seqlen for a given batch index.
16
+
17
+ :param batch_idx: Index of the batch.
18
+ :param seqlen_static: Static sequence length if cu_seqlens is not provided.
19
+ :param cu_seqlens: Cumulative sequence lengths tensor.
20
+ :param seqused: Actual sequence lengths tensor.
21
+ :param HAS_CU_SEQLENS: Boolean flag indicating if cu_seqlens is provided.
22
+ :param HAS_SEQUSED: Boolean flag indicating if seqused is provided.
23
+
24
+ :return offset: Offset for the given batch index.
25
+ :return seqlen: Sequence length for the given batch index.
26
+ """
27
+ if HAS_CU_SEQLENS:
28
+ offset = tl.load(cu_seqlens + batch_idx)
29
+ if HAS_SEQUSED:
30
+ seqlen = tl.load(seqused + batch_idx)
31
+ else:
32
+ seqlen = tl.load(cu_seqlens + batch_idx + 1) - offset
33
+ else:
34
+ offset = 0
35
+ if HAS_SEQUSED:
36
+ seqlen = tl.load(seqused + batch_idx)
37
+ else:
38
+ seqlen = seqlen_static
39
+ return offset, seqlen
40
+
41
+
42
+ @triton.jit
43
+ def get_seqlen_info_qk(
44
+ batch_idx,
45
+ seqlen_q_static,
46
+ seqlen_k_static,
47
+ cu_seqlens_q,
48
+ cu_seqlens_k,
49
+ seqused_q,
50
+ seqused_k,
51
+ TILE_M: tl.constexpr,
52
+ TILE_N: tl.constexpr,
53
+ HAS_CU_SEQLENS_Q: tl.constexpr,
54
+ HAS_CU_SEQLENS_K: tl.constexpr,
55
+ HAS_SEQUSED_Q: tl.constexpr,
56
+ HAS_SEQUSED_K: tl.constexpr,
57
+ ):
58
+ """
59
+ Get offset, padded_offset, and seqlen for both Q and K.
60
+
61
+ :param batch_idx: Index of the batch.
62
+ :param seqlen_q_static: Static sequence length for Q if cu_seqlens_q is not provided.
63
+ :param seqlen_k_static: Static sequence length for K if cu_seqlens_k is not provided.
64
+ :param cu_seqlens_q: Cumulative sequence lengths tensor for Q.
65
+ :param cu_seqlens_k: Cumulative sequence lengths tensor for K.
66
+ :param seqused_q: Actual sequence lengths tensor for Q.
67
+ :param seqused_k: Actual sequence lengths tensor for K.
68
+ :param TILE_M: Tile size for Q.
69
+ :param TILE_N: Tile size for K.
70
+ :param HAS_CU_SEQLENS_Q: Boolean flag indicating if cu_seqlens_q is provided.
71
+ :param HAS_CU_SEQLENS_K: Boolean flag indicating if cu_seqlens_k is provided.
72
+ :param HAS_SEQUSED_Q: Boolean flag indicating if seqused_q is provided.
73
+ :param HAS_SEQUSED_K: Boolean flag indicating if seqused_k is provided.
74
+
75
+ :return offset_q: Offset for Q for the given batch index.
76
+ :return offset_k: Offset for K for the given batch index.
77
+ :return padded_offset_q: Padded offset for Q aligned to TILE_M.
78
+ :return padded_offset_k: Padded offset for K aligned to TILE_N.
79
+ :return seqlen_q: Sequence length for Q for the given batch index.
80
+ :return seqlen_k: Sequence length for K for the given batch index.
81
+ """
82
+
83
+ # Q offset and seqlen
84
+ if HAS_CU_SEQLENS_Q:
85
+ offset_q = tl.load(cu_seqlens_q + batch_idx)
86
+ padded_offset_q = (offset_q + batch_idx * TILE_M) // TILE_M * TILE_M
87
+ if HAS_SEQUSED_Q:
88
+ seqlen_q = tl.load(seqused_q + batch_idx)
89
+ else:
90
+ seqlen_q = tl.load(cu_seqlens_q + batch_idx + 1) - offset_q
91
+ else:
92
+ offset_q = 0
93
+ padded_offset_q = 0
94
+ if HAS_SEQUSED_Q:
95
+ seqlen_q = tl.load(seqused_q + batch_idx)
96
+ else:
97
+ seqlen_q = seqlen_q_static
98
+
99
+ # K offset and seqlen
100
+ if HAS_CU_SEQLENS_K:
101
+ offset_k = tl.load(cu_seqlens_k + batch_idx)
102
+ padded_offset_k = (offset_k + batch_idx * TILE_N) // TILE_N * TILE_N
103
+ if HAS_SEQUSED_K:
104
+ seqlen_k = tl.load(seqused_k + batch_idx)
105
+ else:
106
+ seqlen_k = tl.load(cu_seqlens_k + batch_idx + 1) - offset_k
107
+ else:
108
+ offset_k = 0
109
+ padded_offset_k = 0
110
+ if HAS_SEQUSED_K:
111
+ seqlen_k = tl.load(seqused_k + batch_idx)
112
+ else:
113
+ seqlen_k = seqlen_k_static
114
+
115
+ return offset_q, offset_k, padded_offset_q, padded_offset_k, seqlen_q, seqlen_k
116
+
117
+
118
+ @triton.jit
119
+ def get_softmax_threshold(
120
+ softmax_threshold,
121
+ m_block,
122
+ seqlen_q,
123
+ seqlen_k,
124
+ IS_CAUSAL: tl.constexpr,
125
+ TILE_M: tl.constexpr,
126
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
127
+ ):
128
+ """
129
+ Compute the softmax threshold for a given block.
130
+
131
+ :param softmax_threshold: Threshold value normalized by full key length.
132
+ :param m_block: Current block index along the M dimension.
133
+ :param seqlen_q: Sequence length of the query.
134
+ :param seqlen_k: Sequence length of the key.
135
+ :param IS_CAUSAL: Boolean flag indicating if the attention is causal.
136
+ :param TILE_M: Tile size along the M dimension.
137
+ :param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
138
+
139
+ :return softmax_threshold_log2: softmax threshold of shape [TILE_M] in log2-domain for the given block.
140
+ """
141
+ if IS_CAUSAL:
142
+ offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
143
+ q_idx = offs_m
144
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
145
+ q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
146
+ causal_offset = seqlen_k - seqlen_q
147
+ s_thr = softmax_threshold * (q_idx + causal_offset + 1.0) / seqlen_k
148
+ else:
149
+ s_thr = tl.full((TILE_M,), softmax_threshold, dtype=tl.float32)
150
+ softmax_threshold_log2 = tl.log2(s_thr)
151
+ return softmax_threshold_log2
152
+
153
+
154
+ @triton.jit
155
+ def get_gate_threshold(
156
+ gate_threshold,
157
+ m_block,
158
+ seqlen_q,
159
+ seqlen_k,
160
+ IS_CAUSAL: tl.constexpr,
161
+ TILE_M: tl.constexpr,
162
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
163
+ IS_ADAPT_GATE: tl.constexpr,
164
+ ):
165
+ """
166
+ Compute the gate threshold for a given block.
167
+
168
+ :param gate_threshold: Threshold value for the gate.
169
+ :param m_block: Current block index along the M dimension.
170
+ :param seqlen_q: Sequence length of the query.
171
+ :param seqlen_k: Sequence length of the key.
172
+ :param IS_CAUSAL: Boolean flag indicating if the attention is causal.
173
+ :param TILE_M: Tile size along the M dimension.
174
+ :param QHEAD_PER_KVHEAD_PACKGQA: Ratio of query heads to key/value heads for packed GQA.
175
+ :param IS_ADAPT_GATE: Boolean flag indicating if self-adaptive gate threshold is enabled.
176
+ :return gate_threshold_log2: Lower-bound scalar gate threshold in log2-domain for the given block.
177
+ """
178
+ if IS_CAUSAL and IS_ADAPT_GATE:
179
+ offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
180
+ q_idx = offs_m
181
+ if QHEAD_PER_KVHEAD_PACKGQA > 1:
182
+ q_idx = q_idx // QHEAD_PER_KVHEAD_PACKGQA
183
+ causal_offset = seqlen_k - seqlen_q
184
+ g_thr = tl.min(gate_threshold * (q_idx + causal_offset + 1.0) / seqlen_k)
185
+ else:
186
+ g_thr = tl.full((), gate_threshold, dtype=tl.float32)
187
+ gate_threshold_log2 = tl.log2(g_thr)
188
+ return gate_threshold_log2
189
+
190
+
191
+ @triton.jit
192
+ def offset_batch_Q(
193
+ base_ptr,
194
+ batch_idx,
195
+ offset,
196
+ padded_offset,
197
+ stride_batch,
198
+ stride_seq,
199
+ HAS_CU_SEQLENS: tl.constexpr,
200
+ USE_PADDED: tl.constexpr,
201
+ ):
202
+ if HAS_CU_SEQLENS:
203
+ actual_offset = padded_offset if USE_PADDED else offset
204
+ return base_ptr + actual_offset * stride_seq
205
+ else:
206
+ return base_ptr + batch_idx * stride_batch
207
+
208
+
209
+ @triton.jit
210
+ def offset_batch_K(
211
+ base_ptr,
212
+ batch_idx,
213
+ offset,
214
+ padded_offset,
215
+ stride_batch,
216
+ stride_seq,
217
+ HAS_CU_SEQLENS: tl.constexpr,
218
+ USE_PADDED: tl.constexpr,
219
+ ):
220
+ if HAS_CU_SEQLENS:
221
+ actual_offset = padded_offset if USE_PADDED else offset
222
+ return base_ptr + actual_offset * stride_seq
223
+ else:
224
+ return base_ptr + batch_idx * stride_batch
225
+
226
+
227
+ @triton.jit
228
+ def make_ptrs(
229
+ base_ptrs,
230
+ mn_block,
231
+ stride_seq,
232
+ TILE_MN: tl.constexpr,
233
+ TILE_K: tl.constexpr,
234
+ SWAP_AB: tl.constexpr,
235
+ ):
236
+ offs_mn = mn_block * TILE_MN + tl.arange(0, TILE_MN)
237
+ if TILE_K > 1:
238
+ offs_k = tl.arange(0, TILE_K)
239
+ if SWAP_AB:
240
+ ptrs = base_ptrs + offs_mn[None, :] * stride_seq + offs_k[:, None]
241
+ else:
242
+ ptrs = base_ptrs + offs_mn[:, None] * stride_seq + offs_k[None, :]
243
+ else:
244
+ ptrs = base_ptrs + offs_mn * stride_seq
245
+ return ptrs
246
+
247
+
248
+ @triton.jit
249
+ def make_pack_gqa_ptrs(
250
+ base_ptrs,
251
+ m_block,
252
+ head_idx,
253
+ stride_head,
254
+ stride_seq,
255
+ TILE_M: tl.constexpr,
256
+ TILE_K: tl.constexpr,
257
+ QHEAD_PER_KVHEAD_PACKGQA: tl.constexpr,
258
+ ):
259
+ offs_m = m_block * TILE_M + tl.arange(0, TILE_M)
260
+ m_idx = offs_m // QHEAD_PER_KVHEAD_PACKGQA
261
+ q_head_offset = offs_m - m_idx * QHEAD_PER_KVHEAD_PACKGQA
262
+ q_head = head_idx * QHEAD_PER_KVHEAD_PACKGQA + q_head_offset
263
+ if TILE_K > 1:
264
+ offs_k = tl.arange(0, TILE_K)
265
+ ptrs = (
266
+ base_ptrs
267
+ + m_idx[:, None] * stride_seq
268
+ + q_head[:, None] * stride_head
269
+ + offs_k[None, :]
270
+ )
271
+ else:
272
+ ptrs = base_ptrs + m_idx * stride_seq + q_head * stride_head
273
+ return ptrs
build/torch-cuda/utils.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import functools
2
+ import torch
3
+ import triton
4
+
5
+
6
+ def get_device():
7
+ """
8
+ Get the appropriate device for computation.
9
+
10
+ :return device: torch.device object
11
+ """
12
+ # TODO: add NPU
13
+ # Works for both NVIDIA and AMD
14
+ if torch.cuda.is_available():
15
+ return torch.device("cuda")
16
+ # Intel XPU if available
17
+ elif torch.xpu.is_available():
18
+ return torch.device("xpu")
19
+ elif torch.mps.is_available():
20
+ return torch.device("mps")
21
+ else:
22
+ return torch.device("cpu")
23
+
24
+
25
+ def ensure_contiguous(fn):
26
+ """
27
+ Decorator to ensure that all tensor inputs to the decorated function are contiguous.
28
+
29
+ :param fn: Function to be decorated
30
+
31
+ :return wrapper: Wrapped function
32
+ """
33
+
34
+ @functools.wraps(fn)
35
+ def wrapper(ctx, *args, **kwargs):
36
+ def maybe_to_contiguous(x):
37
+ return x.contiguous() if isinstance(x, torch.Tensor) else x
38
+
39
+ args = [maybe_to_contiguous(arg) for arg in args]
40
+ kwargs = {k: maybe_to_contiguous(v) for k, v in kwargs.items()}
41
+ return fn(ctx, *args, **kwargs)
42
+
43
+ return wrapper
44
+
45
+
46
+ @functools.lru_cache(maxsize=4096)
47
+ def window_sizes_heuristic(
48
+ seqlen_k: int,
49
+ num_heads_kv: int,
50
+ device: torch.device,
51
+ equal_bandwidth: bool = True,
52
+ window_dist: int = 1024,
53
+ window_sink: int = 64,
54
+ ) -> torch.Tensor:
55
+ """
56
+ Compute token count window sizes that partition non-diagonal causal distances into bands.
57
+
58
+ :param seqlen_k: Sequence length of keys.
59
+ :param num_heads_kv: Number of KV heads.
60
+ :param device: Target device.
61
+ :param equal_bandwidth: If True, use equal-bandwidth partitioning for balanced decode load. If False, use equal-area partitioning for balanced forward and backward load.
62
+ :param window_dist: Near-diagonal token count shared by all KV heads.
63
+ :param window_sink: Sink token count shared by all KV heads.
64
+
65
+ :return: int32 tensor with shape [num_heads_kv, 4], columns are [window_sink, window_left, window_right, window_dist]. window_sink is the prefix sink token count, window_left is the distant local band token count, window_right is the token count gap after the near-diagonal window before the distant band, and window_dist is the near-diagonal token count.
66
+ """
67
+ window_dist = min(max(window_dist, 0), seqlen_k)
68
+ window_sink = min(max(window_sink, 0), max(seqlen_k - window_dist, 0))
69
+ head_kv_idx = torch.arange(num_heads_kv + 1, dtype=torch.float32)
70
+ distance_span = max(seqlen_k - window_sink - window_dist, 0)
71
+ if equal_bandwidth:
72
+ breakpoints = (distance_span * head_kv_idx / num_heads_kv).to(torch.int32)
73
+ else:
74
+ breakpoints = (
75
+ distance_span * (1.0 - torch.sqrt(1.0 - head_kv_idx / num_heads_kv))
76
+ ).to(torch.int32)
77
+ window_size_left = breakpoints[1:] - breakpoints[:-1]
78
+ window_size_right = breakpoints[:-1]
79
+ window_size_dist = torch.full_like(window_size_left, window_dist)
80
+ window_size_sink = torch.full_like(window_size_left, window_sink)
81
+ return torch.stack(
82
+ [window_size_sink, window_size_left, window_size_right, window_size_dist], dim=1
83
+ ).to(device)
84
+
85
+
86
+ def max_split_blocks_from_window_sizes(
87
+ window_sizes: torch.Tensor, TILE_N: int
88
+ ) -> int | None:
89
+ """
90
+ Estimate useful split-axis blocks for local attention.
91
+
92
+ The exact useful split count depends on the max values stored in window_sizes.
93
+
94
+ :param window_sizes: Int32 tensor with shape [num_heads_kv, 4], columns are [sink, left, right, dist]
95
+ :param TILE_N: Tile size for N dimension.
96
+
97
+ :return: Useful split-axis blocks, or None when CUDA Graph capture needs a static launch-grid bound.
98
+ """
99
+ if window_sizes is None or window_sizes.numel() == 0:
100
+ return 1
101
+ if (
102
+ window_sizes.is_cuda
103
+ and torch.cuda.is_available()
104
+ and torch.cuda.is_current_stream_capturing()
105
+ ):
106
+ return None
107
+ window_sizes = torch.clamp(window_sizes, min=0)
108
+ window_sink = torch.max(window_sizes[:, 0])
109
+ window_left = torch.max(window_sizes[:, 1])
110
+ window_dist = torch.max(window_sizes[:, 3])
111
+ distant_blocks = torch.div(window_left + TILE_N - 1, TILE_N, rounding_mode="floor")
112
+ near_blocks = (window_dist > 0).to(window_left.dtype)
113
+ sink_blocks = torch.div(window_sink + TILE_N - 1, TILE_N, rounding_mode="floor")
114
+ split_blocks = torch.maximum(
115
+ distant_blocks + near_blocks + sink_blocks,
116
+ torch.ones((), dtype=window_left.dtype, device=window_sizes.device),
117
+ )
118
+ return int(split_blocks)
119
+
120
+
121
+ @functools.lru_cache(maxsize=4096)
122
+ def num_splits_heuristic(
123
+ seqlen_q: int,
124
+ seqlen_k: int,
125
+ num_SMs: int,
126
+ TILE_M: int,
127
+ TILE_N: int,
128
+ max_split_blocks: int | None = None,
129
+ ) -> int:
130
+ """
131
+ Determine the number of KV splits for FlashDecoding.
132
+
133
+ Splits only when there are enough KV blocks to benefit from parallelism,
134
+ and targets full SM occupancy by over-subscribing the M-block count.
135
+
136
+ :param seqlen_q: Sequence length of queries.
137
+ :param seqlen_k: Sequence length of keys.
138
+ :param num_SMs: Number of streaming multiprocessors on the device.
139
+ :param TILE_M: Tile size for M dimension.
140
+ :param TILE_N: Tile size for N dimension.
141
+ :param max_split_blocks: Optional cap on useful split-axis blocks. Local two-band attention should pass this to avoid assigning splits to empty gap regions.
142
+
143
+ :return: Number of splits.
144
+ """
145
+ total_mblocks = triton.cdiv(seqlen_q, TILE_M)
146
+ num_n_blocks = triton.cdiv(seqlen_k, TILE_N)
147
+ effective_n_blocks = num_n_blocks
148
+ if max_split_blocks is not None:
149
+ effective_n_blocks = min(num_n_blocks, max(max_split_blocks, 1))
150
+ max_splits = 1 << (max(num_SMs, 1).bit_length() - 1)
151
+ if effective_n_blocks <= 4:
152
+ # 1 means no splitting
153
+ return 1
154
+ return max(1, min(num_SMs // max(total_mblocks, 1), max_splits, effective_n_blocks))
155
+
156
+
157
+ def alloc_fn(size: int, alignment: int, stream):
158
+ """
159
+ TMA descriptors require a global memory allocation
160
+
161
+ :param size: Size of the allocation in bytes.
162
+ :param alignment: Alignment requirement in bytes.
163
+ :param stream: CUDA stream for the allocation.
164
+
165
+ :return: A torch.Tensor representing the allocated memory.
166
+ """
167
+ return torch.empty(size, device="cuda", dtype=torch.int8)