liangsu9988 commited on
Commit
ce790a1
·
verified ·
1 Parent(s): 3428622

Uploaded using `kernel-builder`.

Browse files
build/torch213-cxx11-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,293 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Static-buffer FlashAttention-2 runtime operators from FlashRT."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ import math
7
+ from typing import Optional
8
+
9
+ import torch
10
+
11
+ from ._ops import add_op_namespace_prefix, ops
12
+
13
+
14
+ SUPPORTED_HEAD_DIMS = tuple(range(8, 257, 8))
15
+ COMPILED_HEAD_DIM_BUCKETS = (64, 96, 128, 256)
16
+ # Vendored FA2 split-KV partial-head tiles are valid only in these logical
17
+ # ranges. Other supported dimensions use the correct no-split kernel.
18
+ SPLIT_HEAD_DIMS = tuple(range(40, 129, 8)) + tuple(range(232, 257, 8))
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class FA2Workspace:
23
+ """Preallocated split-KV workspace for one static attention shape."""
24
+
25
+ softmax_lse_accum: torch.Tensor
26
+ out_accum: torch.Tensor
27
+ num_sms: int
28
+ num_splits: int
29
+
30
+
31
+ def _ceildiv(a: int, b: int) -> int:
32
+ return (a + b - 1) // b
33
+
34
+
35
+ def recommended_num_splits(
36
+ batch: int,
37
+ seqlen_q: int,
38
+ seqlen_k: int,
39
+ heads_q: int,
40
+ head_dim: int,
41
+ num_sms: int,
42
+ ) -> int:
43
+ """Return the exact split count selected by the FlashRT FA2 heuristic."""
44
+
45
+ values = (batch, seqlen_q, seqlen_k, heads_q, head_dim, num_sms)
46
+ if any(int(v) <= 0 for v in values):
47
+ raise ValueError("all shape values and num_sms must be positive")
48
+ if int(head_dim) not in SUPPORTED_HEAD_DIMS:
49
+ raise ValueError("head_dim must be a positive multiple of 8 at most 256")
50
+ if int(head_dim) not in SPLIT_HEAD_DIMS:
51
+ return 1
52
+ block_n = 256 if head_dim <= 64 else (128 if head_dim <= 128 else 64)
53
+ n_blocks = _ceildiv(seqlen_k, block_n)
54
+ m_blocks = _ceildiv(seqlen_q, 64)
55
+ blocks = batch * heads_q * m_blocks
56
+ effective_sms = num_sms * 2
57
+ if blocks >= 0.8 * effective_sms:
58
+ return 1
59
+ max_splits = min(128, effective_sms, n_blocks)
60
+ efficiencies = [0.0] * (max_splits + 1)
61
+ best = 0.0
62
+ for split in range(1, max_splits + 1):
63
+ eligible = split == 1 or _ceildiv(n_blocks, split) != _ceildiv(n_blocks, split - 1)
64
+ if not eligible:
65
+ continue
66
+ waves = blocks * split / effective_sms
67
+ efficiencies[split] = waves / math.ceil(waves)
68
+ best = max(best, efficiencies[split])
69
+ for split in range(1, max_splits + 1):
70
+ eligible = split == 1 or _ceildiv(n_blocks, split) != _ceildiv(n_blocks, split - 1)
71
+ if eligible and efficiencies[split] >= 0.85 * best:
72
+ return split
73
+ return 1
74
+
75
+
76
+ def allocate_workspace(
77
+ q: torch.Tensor,
78
+ k: torch.Tensor,
79
+ *,
80
+ num_sms: Optional[int] = None,
81
+ ) -> Optional[FA2Workspace]:
82
+ """Allocate the exact split-KV workspace selected for ``q`` and ``k``.
83
+
84
+ Returns ``None`` when the heuristic selects the no-split path. Allocate
85
+ once during runtime setup; never call this helper inside a captured loop.
86
+ """
87
+
88
+ if q.ndim != 4 or k.ndim != 4:
89
+ raise ValueError("q and k must have shape (B, S, H, D)")
90
+ if q.shape[-1] not in SUPPORTED_HEAD_DIMS:
91
+ raise ValueError("head_dim must be a positive multiple of 8 at most 256")
92
+ if q.shape[-1] not in SPLIT_HEAD_DIMS:
93
+ return None
94
+ if num_sms is None:
95
+ num_sms = torch.cuda.get_device_properties(q.device).multi_processor_count
96
+ splits = recommended_num_splits(
97
+ q.shape[0], q.shape[1], k.shape[1], q.shape[2], q.shape[3], num_sms
98
+ )
99
+ if splits == 1:
100
+ return None
101
+ lse = torch.empty(
102
+ (splits, q.shape[0], q.shape[2], q.shape[1]),
103
+ device=q.device,
104
+ dtype=torch.float32,
105
+ )
106
+ d_rounded = (q.shape[3] + 31) & ~31
107
+ out = torch.empty(
108
+ (splits, q.shape[0], q.shape[2], q.shape[1], d_rounded),
109
+ device=q.device,
110
+ dtype=torch.float32,
111
+ )
112
+ return FA2Workspace(lse, out, int(num_sms), int(splits))
113
+
114
+
115
+ def allocate_outputs(q: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
116
+ """Allocate output and LSE tensors for a static ``(B,S,H,D)`` query."""
117
+
118
+ if q.ndim != 4:
119
+ raise ValueError("q must have shape (B, S, H, D)")
120
+ out = torch.empty_strided(q.shape, q.stride(), device=q.device, dtype=q.dtype)
121
+ lse = torch.empty(
122
+ (q.shape[0], q.shape[2], q.shape[1]),
123
+ device=q.device,
124
+ dtype=torch.float32,
125
+ )
126
+ return out, lse
127
+
128
+
129
+ def _workspace_args(
130
+ workspace: Optional[FA2Workspace],
131
+ ) -> tuple[Optional[torch.Tensor], Optional[torch.Tensor], int]:
132
+ if workspace is None:
133
+ return None, None, 0
134
+ return workspace.softmax_lse_accum, workspace.out_accum, int(workspace.num_sms)
135
+
136
+
137
+ @torch.library.register_fake(add_op_namespace_prefix("forward_static"))
138
+ def _forward_static_fake(
139
+ q: torch.Tensor,
140
+ k: torch.Tensor,
141
+ v: torch.Tensor,
142
+ out: torch.Tensor,
143
+ softmax_lse: torch.Tensor,
144
+ softmax_lse_accum: Optional[torch.Tensor],
145
+ out_accum: Optional[torch.Tensor],
146
+ softmax_scale: float,
147
+ causal: bool = False,
148
+ num_sms: int = 0,
149
+ ) -> None:
150
+ del k, v, softmax_scale, causal, num_sms
151
+ if q.ndim != 4 or out.shape != q.shape:
152
+ raise RuntimeError("q/out must have matching (B, S, H, D) shapes")
153
+ if softmax_lse.shape != (q.shape[0], q.shape[2], q.shape[1]):
154
+ raise RuntimeError("softmax_lse must have shape (B, H, S)")
155
+ if (softmax_lse_accum is None) != (out_accum is None):
156
+ raise RuntimeError("split-KV workspace tensors must be both set or both None")
157
+ return None
158
+
159
+
160
+ @torch.library.register_fake(add_op_namespace_prefix("forward_seqused_static"))
161
+ def _forward_seqused_static_fake(
162
+ q: torch.Tensor,
163
+ k: torch.Tensor,
164
+ v: torch.Tensor,
165
+ seqused_k: torch.Tensor,
166
+ out: torch.Tensor,
167
+ softmax_lse: torch.Tensor,
168
+ softmax_lse_accum: Optional[torch.Tensor],
169
+ out_accum: Optional[torch.Tensor],
170
+ softmax_scale: float,
171
+ num_sms: int = 0,
172
+ ) -> None:
173
+ del seqused_k
174
+ return _forward_static_fake(
175
+ q,
176
+ k,
177
+ v,
178
+ out,
179
+ softmax_lse,
180
+ softmax_lse_accum,
181
+ out_accum,
182
+ softmax_scale,
183
+ False,
184
+ num_sms,
185
+ )
186
+
187
+
188
+ def forward_static(
189
+ q: torch.Tensor,
190
+ k: torch.Tensor,
191
+ v: torch.Tensor,
192
+ *,
193
+ out: torch.Tensor,
194
+ softmax_lse: torch.Tensor,
195
+ workspace: Optional[FA2Workspace] = None,
196
+ softmax_scale: Optional[float] = None,
197
+ causal: bool = False,
198
+ ) -> torch.Tensor:
199
+ """Run allocation-free FA2 forward into caller-owned static buffers."""
200
+
201
+ if softmax_scale is None:
202
+ softmax_scale = q.shape[-1] ** -0.5
203
+ lse_accum, out_accum, num_sms = _workspace_args(workspace)
204
+ ops.forward_static(
205
+ q,
206
+ k,
207
+ v,
208
+ out,
209
+ softmax_lse,
210
+ lse_accum,
211
+ out_accum,
212
+ float(softmax_scale),
213
+ bool(causal),
214
+ int(num_sms),
215
+ )
216
+ return out
217
+
218
+
219
+ def forward_seqused_static(
220
+ q: torch.Tensor,
221
+ k: torch.Tensor,
222
+ v: torch.Tensor,
223
+ seqused_k: torch.Tensor,
224
+ *,
225
+ out: torch.Tensor,
226
+ softmax_lse: torch.Tensor,
227
+ workspace: Optional[FA2Workspace] = None,
228
+ softmax_scale: Optional[float] = None,
229
+ ) -> torch.Tensor:
230
+ """Run BF16 FA2 with device-resident per-batch K/V lengths.
231
+
232
+ Values in ``seqused_k`` must be in ``[1, k.shape[1]]``. When split-KV is
233
+ enabled, the LSE workspace is reset to ``-inf`` on the current stream; that
234
+ reset is captured together with the kernel by CUDA Graphs.
235
+ """
236
+
237
+ if softmax_scale is None:
238
+ softmax_scale = q.shape[-1] ** -0.5
239
+ lse_accum, out_accum, num_sms = _workspace_args(workspace)
240
+ if lse_accum is not None:
241
+ lse_accum.fill_(-torch.inf)
242
+ ops.forward_seqused_static(
243
+ q,
244
+ k,
245
+ v,
246
+ seqused_k,
247
+ out,
248
+ softmax_lse,
249
+ lse_accum,
250
+ out_accum,
251
+ float(softmax_scale),
252
+ int(num_sms),
253
+ )
254
+ return out
255
+
256
+
257
+ def forward(
258
+ q: torch.Tensor,
259
+ k: torch.Tensor,
260
+ v: torch.Tensor,
261
+ *,
262
+ softmax_scale: Optional[float] = None,
263
+ causal: bool = False,
264
+ use_split_kv: bool = True,
265
+ ) -> torch.Tensor:
266
+ """Convenience API that allocates outputs and optional split-KV workspace."""
267
+
268
+ out, lse = allocate_outputs(q)
269
+ workspace = allocate_workspace(q, k) if use_split_kv else None
270
+ return forward_static(
271
+ q,
272
+ k,
273
+ v,
274
+ out=out,
275
+ softmax_lse=lse,
276
+ workspace=workspace,
277
+ softmax_scale=softmax_scale,
278
+ causal=causal,
279
+ )
280
+
281
+
282
+ __all__ = [
283
+ "FA2Workspace",
284
+ "COMPILED_HEAD_DIM_BUCKETS",
285
+ "SPLIT_HEAD_DIMS",
286
+ "SUPPORTED_HEAD_DIMS",
287
+ "allocate_outputs",
288
+ "allocate_workspace",
289
+ "forward",
290
+ "forward_seqused_static",
291
+ "forward_static",
292
+ "recommended_num_splits",
293
+ ]
build/torch213-cxx11-cu130-x86_64-linux/_fa2_seqused_runtime_cuda_9ea2146.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e78b78088c67872a5149377c538bf8c68537469a984f8c34cfd75ae739274d39
3
+ size 374479696
build/torch213-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _fa2_seqused_runtime_cuda_9ea2146
3
+ ops = torch.ops._fa2_seqused_runtime_cuda_9ea2146
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_fa2_seqused_runtime_cuda_9ea2146::{op_name}"
build/torch213-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "fa2-seqused-runtime",
3
+ "id": "_fa2_seqused_runtime_cuda_9ea2146",
4
+ "version": 1,
5
+ "license": "BSD-3-Clause",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "10.0",
11
+ "12.0",
12
+ "8.0",
13
+ "9.0"
14
+ ]
15
+ },
16
+ "digest": {
17
+ "algorithm": "sha256",
18
+ "files": {
19
+ "__init__.py": "CN+Ar/JeWCSd1O49S4rR3XPtrtlRXwoY54NuRMsiFDM=",
20
+ "_fa2_seqused_runtime_cuda_9ea2146.abi3.so": "54t4CIxnhypRSTd8U4v4xoU3RpqYT4w0z9da5zknTTk=",
21
+ "_ops.py": "csDcHTNxl0h1zDNATeVxZ1/hrMeV5W8mdnPqqupklBk="
22
+ }
23
+ },
24
+ "provenance": {
25
+ "kernel-builder": {
26
+ "version": "0.17.0-dev0",
27
+ "sha": "81f55ea30fd8f819dcf93a3c934dd584c895bd2f",
28
+ "dirty": false
29
+ },
30
+ "kernel": {
31
+ "sha": "9ea2146c52cd2b967296f47342fb32a14bb25033",
32
+ "dirty": false
33
+ }
34
+ }
35
+ }