ajh-code commited on
Commit
958a9cd
·
verified ·
1 Parent(s): ee3ab6f

Add runtime/spas_sage_attn/utils.py

Browse files
Files changed (1) hide show
  1. runtime/spas_sage_attn/utils.py +447 -0
runtime/spas_sage_attn/utils.py ADDED
@@ -0,0 +1,447 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Copyright (c) 2025 by SpargeAttn team.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ """
16
+
17
+ import torch
18
+ import torch.nn.functional as F
19
+ from einops import rearrange, repeat
20
+ import triton
21
+ import triton.language as tl
22
+ from torch import Tensor
23
+
24
+
25
+ def precision_metric(quant_o, fa2_o, verbose=True, round_num=4):
26
+ if quant_o.shape[-2] > 200000:
27
+ quant_o, fa2_o = quant_o.cpu(), fa2_o.cpu()
28
+ x, xx = quant_o.float(), fa2_o.float()
29
+ sim = F.cosine_similarity(x.reshape(1, -1), xx.reshape(1, -1)).item()
30
+ l1 = ( (x - xx).abs().sum() / xx.abs().sum() ).item()
31
+ rmse = torch.sqrt(torch.mean((x -xx) ** 2)).item()
32
+ sim = round(sim, round_num)
33
+ l1 = round(l1, round_num)
34
+ rmse = round(rmse, round_num)
35
+ if verbose: print(f'Cossim: {sim:.6f}, L1: {l1:.6f}, RMSE:{rmse:.6f}')
36
+ return {"Cossim": sim, "L1": l1, "RMSE": rmse}
37
+
38
+ def hyperparameter_check(hyper, H, device):
39
+ if type(hyper) == float or type(hyper) == int:
40
+ hyper = torch.full((H,), float(hyper), device=device)
41
+ elif isinstance(hyper, Tensor):
42
+ assert len(hyper.shape) <= 1, "Hyperparameter tensor must be 1D"
43
+ if len(hyper.shape) == 0:
44
+ hyper = torch.full((H,), hyper.item(), device=device)
45
+ assert hyper.numel() == H, f"Hyperparameter tensor must have {H} elements, but has {hyper.numel()}"
46
+ hyper = hyper.to(device)
47
+ else:
48
+ print(hyper)
49
+ raise ValueError("Hyperparameter must be a float or a tensor")
50
+ return hyper
51
+
52
+
53
+
54
+ @triton.jit
55
+ def triton_block_map_to_lut_kernel(map_ptr, lut_ptr, valid_block_num_ptr, num_block_k):
56
+ b, h, q = tl.program_id(0), tl.program_id(1), tl.program_id(2)
57
+ B, H, Q = tl.num_programs(0), tl.num_programs(1), tl.num_programs(2)
58
+ valid_block_num = 0
59
+
60
+ map_ptr = map_ptr + b * H * Q * num_block_k + h * Q * num_block_k + q * num_block_k
61
+ lut_ptr = lut_ptr + b * H * Q * num_block_k + h * Q * num_block_k + q * num_block_k
62
+ valid_block_num_ptr = valid_block_num_ptr + b * H * Q + h * Q + q
63
+
64
+ valid_block_num = 0
65
+ prev_block = 0
66
+
67
+ for i in range(num_block_k):
68
+ cur_block = tl.load(map_ptr + i)
69
+ if cur_block:
70
+ tl.store(lut_ptr + valid_block_num, i - prev_block)
71
+ valid_block_num += 1
72
+ prev_block = i
73
+
74
+ tl.store(valid_block_num_ptr, valid_block_num)
75
+
76
+ def block_map_lut_triton(block_map):
77
+ assert block_map.dim() == 4
78
+ assert block_map.is_contiguous()
79
+
80
+ B, H, Q, K = block_map.shape
81
+ lut = torch.zeros((B, H, Q, K), dtype=torch.int32, device=block_map.device)
82
+ valid_block_num = torch.zeros((B, H, Q), dtype=torch.int32, device=block_map.device)
83
+
84
+ grid = (B, H, Q)
85
+ triton_block_map_to_lut_kernel[grid](block_map, lut, valid_block_num, K)
86
+
87
+ return lut, valid_block_num
88
+
89
+ @triton.jit
90
+ def qk_quantize(
91
+ # Pointers
92
+ x_ptr,
93
+ xm_ptr,
94
+ x_quant_ptr,
95
+ scale_ptr,
96
+ # Constexpr dimensions
97
+ N: tl.constexpr,
98
+ D: tl.constexpr,
99
+ BS: tl.constexpr,
100
+ fuse_mean: tl.constexpr
101
+ ):
102
+ """
103
+ Triton kernel to perform per-block quantization of a tensor X to int8.
104
+ It loads a block of X, optionally subtracts a mean vector, then calculates
105
+ a scaling factor for the block and quantizes the data to int8.
106
+
107
+ Grid: (B, H, NB)
108
+ B: Batch size
109
+ H: Number of heads
110
+ NB: Number of blocks in the N dimension (N // BS)
111
+ """
112
+ # 1. Get program IDs to identify the current block
113
+ b, h, nb = tl.program_id(0), tl.program_id(1), tl.program_id(2)
114
+ B, H, NB = tl.num_programs(0), tl.num_programs(1), tl.num_programs(2)
115
+
116
+ # 2. Calculate pointers for the input block X
117
+ block_offset = b * H * N * D + h * N * D + nb * BS * D
118
+ x_ptrs = x_ptr + block_offset + tl.arange(0, BS)[:, None] * D + tl.arange(0, D)[None, :]
119
+
120
+ # Create a mask to handle the last block if N is not a multiple of BS
121
+ xmask = (nb * BS + tl.arange(0, BS)[:, None]) < N
122
+
123
+ # Load the input block
124
+ x = tl.load(x_ptrs, mask=xmask, other=0.0)
125
+
126
+ # 3. (Optional) Subtract the mean if fuse_mean is enabled
127
+ if fuse_mean:
128
+ xm_ptrs = xm_ptr + b * H * D + h * D + tl.arange(0, D)
129
+ x_mean = tl.load(xm_ptrs)
130
+ x -= x_mean
131
+ # Re-apply mask to zero out padded values after subtraction
132
+ x = tl.where(xmask, x, 0.0)
133
+
134
+ # 4. Perform quantization
135
+ # Convert to float32 for stable calculations
136
+ x_fp32 = x.to(tl.float32)
137
+
138
+ # Calculate the scale: max(abs(x)) / 127.0
139
+ # The scale is per-block
140
+ scale = tl.max(tl.abs(x_fp32)) / 127.0
141
+ # Add a small epsilon to avoid division by zero
142
+ scale += 1e-7
143
+
144
+ # Quantize to int8: (x / scale) and round to nearest integer
145
+ x_int8 = x_fp32 / scale
146
+ # Round to nearest: add 0.5 for positive, -0.5 for negative
147
+ x_int8 += 0.5 * tl.where(x_int8 >= 0, 1, -1)
148
+ x_int8 = x_int8.to(tl.int8)
149
+
150
+ # 5. Calculate output pointers and store the results
151
+ # Pointers for the quantized output tensor
152
+ x_quant_ptrs = x_quant_ptr + block_offset + tl.arange(0, BS)[:, None] * D + tl.arange(0, D)[None, :]
153
+ # Pointer for the scale value of this block
154
+ scale_ptrs = scale_ptr + b * H * NB + h * NB + nb
155
+
156
+ # Store the quantized int8 values
157
+ tl.store(x_quant_ptrs, x_int8, mask=xmask)
158
+ # Store the scale value
159
+ tl.store(scale_ptrs, scale)
160
+
161
+ @triton.jit
162
+ def triton_bmm_pool_sim_simmean_fuse_quant(
163
+ x_ptr,
164
+ xm_ptr,
165
+ pool_ptr,
166
+ sim_ptr,
167
+ x_quant_ptr,
168
+ scale_ptr,
169
+ simthreshd1,
170
+ N: tl.constexpr,
171
+ D: tl.constexpr,
172
+ BS: tl.constexpr,
173
+ fuse_mean: tl.constexpr
174
+ ):
175
+ b, h, nb = tl.program_id(0), tl.program_id(1), tl.program_id(2)
176
+ B, H, NB = tl.num_programs(0), tl.num_programs(1), tl.num_programs(2)
177
+
178
+ block_offset = b * H * N * D + h * N * D + nb * BS * D
179
+ xmask = (nb*BS + tl.arange(0, BS)[:, None]) < N
180
+ x_ptrs = x_ptr + block_offset + tl.arange(0, BS)[:, None] * D + tl.arange(0, D)[None, :]
181
+ x = tl.load(x_ptrs, mask = xmask)
182
+ BS_ = BS if (N - nb*BS) >= BS else (N - nb*BS)
183
+
184
+ if fuse_mean:
185
+ xm_ptrs = xm_ptr + b * H * D + h * D + tl.arange(0, D)
186
+ x_mean = tl.load(xm_ptrs)
187
+ x -= x_mean
188
+ x = tl.where(xmask, x, 0)
189
+
190
+ cur_h1 = tl.load(simthreshd1 + h)
191
+ x_fp32 = x.to(tl.float32)
192
+
193
+ pool = (tl.sum(x_fp32, axis=0) / BS_)
194
+ x_norm = tl.sqrt(tl.sum(x_fp32 * x_fp32, axis=1, keep_dims=True))
195
+ x = (x / x_norm).to(tl.float16) # norm at D dim
196
+
197
+ grams = tl.dot(x, tl.trans(x))
198
+ sum_value = tl.sum(grams).to(tl.float32)
199
+ cur_sim = (sum_value / (BS_ * BS_)) > cur_h1
200
+
201
+ pool_block_offset = b * H * NB * D + h * NB * D + nb * D
202
+ tl.store(pool_ptr + pool_block_offset + tl.arange(0, D), pool)
203
+ sim_offset = b * H * NB + h * NB + nb
204
+ tl.store(sim_ptr + sim_offset, cur_sim)
205
+
206
+ scale = tl.max(tl.abs(x_fp32)) / 127.
207
+ scale += 0.0000001
208
+ x_int8 = x_fp32 / scale
209
+ x_int8 += 0.5 * tl.where(x_int8 >= 0, 1, -1)
210
+ x_int8 = x_int8.to(tl.int8)
211
+ x_quant_ptrs = x_quant_ptr + block_offset + tl.arange(0, BS)[:, None] * D + tl.arange(0, D)[None, :]
212
+ scale_ptrs = scale_ptr + b * H * NB + h * NB + nb
213
+ tl.store(x_quant_ptrs, x_int8, mask = xmask)
214
+ tl.store(scale_ptrs, scale)
215
+
216
+ @triton.jit
217
+ def triton_bmm_pool_sim_simmean(x_ptr, pool_ptr, sim_ptr, simthreshd1, N: tl.constexpr, D: tl.constexpr, BS: tl.constexpr):
218
+ b, h, nb = tl.program_id(0), tl.program_id(1), tl.program_id(2)
219
+ B, H, NB = tl.num_programs(0), tl.num_programs(1), tl.num_programs(2)
220
+
221
+ block_offset = b * H * N * D + h * N * D + nb * BS * D
222
+ xmask = (nb*BS + tl.arange(0, BS)[:, None]) < N
223
+ x_ptrs = x_ptr + block_offset + tl.arange(0, BS)[:, None] * D + tl.arange(0, D)[None, :]
224
+ x = tl.load(x_ptrs, mask = xmask)
225
+ BS_ = BS if (N - nb*BS) >= BS else (N - nb*BS)
226
+
227
+ cur_h1 = tl.load(simthreshd1 + h)
228
+ x_fp32 = x.to(tl.float32)
229
+ pool = (tl.sum(x_fp32, axis=0) / BS_)
230
+ x_norm = tl.sqrt(tl.sum(x_fp32 * x_fp32, axis=1, keep_dims=True))
231
+ x = (x / x_norm).to(tl.float16) # norm at D dim
232
+
233
+ grams = tl.dot(x, tl.trans(x))
234
+ sum_value = tl.sum(grams).to(tl.float32)
235
+ cur_sim = (sum_value / (BS_ * BS_)) > cur_h1
236
+
237
+ pool_block_offset = b * H * NB * D + h * NB * D + nb * D
238
+ tl.store(pool_ptr + pool_block_offset + tl.arange(0, D), pool)
239
+ sim_offset = b * H * NB + h * NB + nb
240
+ tl.store(sim_ptr + sim_offset, cur_sim)
241
+
242
+
243
+ def get_pool_sim_triton_simmean(x, block_size, simthreshd1):
244
+ x = x.contiguous()
245
+ B, H, N, D = x.shape
246
+ nblock = (N + block_size - 1) // block_size # Number of blocks per feature map
247
+ pool = torch.empty((B, H, nblock, D), device=x.device, dtype=x.dtype)
248
+ sim_blocks = torch.empty((B, H, nblock), device=x.device, dtype=torch.bool)
249
+ grid = (B, H, nblock)
250
+ # Launch kernel
251
+ triton_bmm_pool_sim_simmean[grid](x, pool, sim_blocks, simthreshd1, N=N, D=D, BS=block_size)
252
+ return pool, sim_blocks
253
+
254
+ #todo(xingyang): wrapper for tensor quantization
255
+ def get_quant(x, x_mean, block_size):
256
+ x = x.contiguous()
257
+ B, H, N, D = x.shape
258
+ nblock = (N + block_size - 1) // block_size
259
+ x_quant = torch.empty(x.shape, device=x.device, dtype=torch.int8)
260
+ x_scale = torch.empty((B, H, nblock), device=x.device, dtype=torch.float32)
261
+ grid = (B, H, nblock)
262
+ qk_quantize[grid](x, x_mean, x_quant, x_scale, N=N, D=D, BS=block_size, fuse_mean=(True if x_mean is not None else False))
263
+ return x_quant, x_scale
264
+
265
+ def get_vanilla_qk_quant(q, k, km=None, BLKQ=128, BLKK=64):
266
+ q_int8, q_scale = get_quant(q, None, BLKQ)
267
+ k_int8, k_scale = get_quant(k, km, BLKK)
268
+ return q_int8, q_scale, k_int8, k_scale
269
+
270
+ def get_pool_sim_triton_simmean_fuse_quant(x, x_mean, block_size, simthreshd1):
271
+ x = x.contiguous()
272
+ B, H, N, D = x.shape
273
+ nblock = (N + block_size - 1) // block_size # Number of blocks per feature map
274
+ pool = torch.empty((B, H, nblock, D), device=x.device, dtype=x.dtype)
275
+ sim_blocks = torch.empty((B, H, nblock), device=x.device, dtype=torch.bool)
276
+ x_quant = torch.empty(x.shape, device=x.device, dtype=torch.int8)
277
+ x_scale = torch.empty((B, H, nblock), device=x.device, dtype=torch.float32)
278
+ grid = (B, H, nblock)
279
+ triton_bmm_pool_sim_simmean_fuse_quant[grid](x, x_mean, pool, sim_blocks, x_quant, x_scale, simthreshd1, N=N, D=D, BS=block_size, fuse_mean=(True if x_mean is not None else False))
280
+ return pool, sim_blocks, x_quant, x_scale
281
+
282
+ @triton.jit
283
+ def triton_fill_block_map_kernel(final_map, num_to_select, sorted_indices, NK: tl.constexpr):
284
+ b, h, q = tl.program_id(0), tl.program_id(1), tl.program_id(2)
285
+ B, H, Q = tl.num_programs(0), tl.num_programs(1), tl.num_programs(2)
286
+ cur_num_to_select = tl.load(num_to_select + b * H * Q + h * Q + q)
287
+ cur_sorted_idx_ptr = sorted_indices + b * H * Q * NK + h * Q * NK + q * NK
288
+ cur_final_map_ptr = final_map + b * H * Q * NK + h * Q * NK + q * NK
289
+ cur_num_to_select = (cur_num_to_select + 1) if cur_num_to_select == 0 else cur_num_to_select
290
+ for i in range(cur_num_to_select):
291
+ cur_idx = tl.load(cur_sorted_idx_ptr + i)
292
+ tl.store(cur_final_map_ptr + cur_idx, 1)
293
+
294
+
295
+ def fill_block_map_triton(final_map, num_to_select, sorted_indices):
296
+ final_map = final_map.contiguous()
297
+ num_to_select = num_to_select.contiguous()
298
+ sorted_indices = sorted_indices.contiguous()
299
+ B, H, Q, K = final_map.shape
300
+ grid = (B, H, Q)
301
+ triton_fill_block_map_kernel[grid](final_map, num_to_select, sorted_indices, K)
302
+ return final_map
303
+
304
+ @triton.jit
305
+ def triton_fill_causal_mask(mask, BqdivBk):
306
+ q, k = tl.program_id(0), tl.program_id(1)
307
+ Q, K = tl.num_programs(0), tl.num_programs(1)
308
+ if k >= (q + 1) * BqdivBk:
309
+ tl.store(mask + q * K + k, 0)
310
+ else:
311
+ tl.store(mask + q * K + k, 1)
312
+
313
+ def fill_causal_mask_triton(mask, BqdivBk:float):
314
+ assert mask.dim() == 2
315
+ triton_fill_causal_mask[mask.shape](mask, BqdivBk)
316
+ return mask
317
+
318
+
319
+ def get_block_map_meansim(q, k, is_causal=False, BLKQ=128, BLKK=64, simthreshd1=0.1, cdfthreshd=0.9, topk=None, is_sparse=True, return_lut=False, attention_sink=False):
320
+ assert (cdfthreshd is None and topk is not None) \
321
+ or (cdfthreshd is not None and topk is None), "Only one of cdfthreshd and topk can be set."
322
+
323
+ Headnum = q.size(1)
324
+ simthreshd1 = hyperparameter_check(simthreshd1, Headnum, q.device)
325
+ if cdfthreshd is not None:
326
+ cdfthreshd = hyperparameter_check(cdfthreshd, Headnum, q.device)
327
+ if topk is not None:
328
+ topk = hyperparameter_check(topk, Headnum, q.device)
329
+ nq = (q.shape[-2] + BLKQ - 1) // BLKQ
330
+ nk = (k.shape[-2] + BLKK - 1) // BLKK
331
+ pooled_qblocks, sim_qblocks = get_pool_sim_triton_simmean(q, BLKQ, simthreshd1)
332
+ pooled_kblocks, sim_kblocks = get_pool_sim_triton_simmean(k, BLKK, simthreshd1)
333
+
334
+ sim_kblocks = sim_kblocks.unsqueeze(-2).expand(-1, -1, nq, -1) # faster than repeat
335
+ sim_qblocks = sim_qblocks.unsqueeze(-1).expand(-1, -1, -1, nk)
336
+ pooled_score = pooled_qblocks @ pooled_kblocks.transpose(-1, -2) * q.shape[-1] ** -0.5
337
+ pooled_score[~sim_kblocks] = -torch.inf
338
+ if is_causal:
339
+ nq = pooled_qblocks.shape[-2]
340
+ nk = pooled_kblocks.shape[-2]
341
+ empty_mask = torch.empty(nq, nk, device=q.device, dtype=torch.bool)
342
+ causal_mask = fill_causal_mask_triton(empty_mask, BLKQ / BLKK)
343
+ pooled_score = pooled_score.masked_fill(~causal_mask[None, None, ...], -torch.inf)
344
+ pooled_score = pooled_score.softmax(-1)
345
+ sorted_score = torch.sort(pooled_score, dim=-1, descending=True)
346
+ cdf = torch.cumsum(sorted_score.values, dim=-1)
347
+ B, H, Q, K = cdf.shape
348
+ if cdfthreshd is not None:
349
+ cdfthreshd_ts = cdfthreshd.view(1, H, 1, 1)
350
+ cdfthreshd_ts = cdfthreshd_ts.expand(B, -1, Q, 1).contiguous()
351
+ num_to_select = torch.searchsorted(cdf, cdfthreshd_ts, right=True).squeeze(-1)
352
+ else:
353
+ num_to_select = (topk * K).to(torch.int64).view(1, H, 1).expand(B, -1, Q).contiguous()
354
+
355
+ final_map = torch.zeros_like(pooled_score, dtype=torch.bool)
356
+ final_map[~sim_kblocks] = 1
357
+ final_map[~sim_qblocks] = 1
358
+ final_map = fill_block_map_triton(final_map, num_to_select, sorted_score.indices)
359
+ if is_causal:
360
+ final_map = final_map * causal_mask[None, None, ...]
361
+
362
+ if attention_sink:
363
+ final_map[:, :, :, 0] = 1
364
+
365
+ if not return_lut:
366
+ return final_map
367
+ else:
368
+ lut, valid_block_num = block_map_lut_triton(final_map)
369
+ return lut, valid_block_num
370
+
371
+ def get_block_map_meansim_fuse_quant(q, k, km=None, is_causal=False, BLKQ=128, BLKK=64, simthreshd1=0.1, cdfthreshd=0.9, topk=None, is_sparse=True, return_lut=False, attention_sink=False):
372
+ assert (cdfthreshd is None and topk is not None) \
373
+ or (cdfthreshd is not None and topk is None), "Only one of cdfthreshd and topk can be set."
374
+
375
+ Headnum = q.size(1)
376
+ simthreshd1 = hyperparameter_check(simthreshd1, Headnum, q.device)
377
+ if cdfthreshd is not None:
378
+ cdfthreshd = hyperparameter_check(cdfthreshd, Headnum, q.device)
379
+ if topk is not None:
380
+ topk = hyperparameter_check(topk, Headnum, q.device)
381
+ nq = (q.shape[-2] + BLKQ - 1) // BLKQ
382
+ nk = (k.shape[-2] + BLKK - 1) // BLKK
383
+ pooled_qblocks, sim_qblocks, q_int8, q_scale = get_pool_sim_triton_simmean_fuse_quant(q, None, BLKQ, simthreshd1)
384
+ pooled_kblocks, sim_kblocks, k_int8, k_scale = get_pool_sim_triton_simmean_fuse_quant(k, km, BLKK, simthreshd1)
385
+
386
+ sim_kblocks = sim_kblocks.unsqueeze(-2).expand(-1, -1, nq, -1) # faster than repeat
387
+ sim_qblocks = sim_qblocks.unsqueeze(-1).expand(-1, -1, -1, nk)
388
+ pooled_score = pooled_qblocks @ pooled_kblocks.transpose(-1, -2) * q.shape[-1] ** -0.5
389
+ pooled_score[~sim_kblocks] = -torch.inf
390
+ if is_causal:
391
+ nq = pooled_qblocks.shape[-2]
392
+ nk = pooled_kblocks.shape[-2]
393
+ empty_mask = torch.empty(nq, nk, device=q.device, dtype=torch.bool)
394
+ causal_mask = fill_causal_mask_triton(empty_mask, BLKQ / BLKK)
395
+ pooled_score = pooled_score.masked_fill(~causal_mask[None, None, ...], -torch.inf)
396
+ pooled_score = pooled_score.softmax(-1)
397
+ sorted_score = torch.sort(pooled_score, dim=-1, descending=True)
398
+ cdf = torch.cumsum(sorted_score.values, dim=-1)
399
+ B, H, Q, K = cdf.shape
400
+ if cdfthreshd is not None:
401
+ cdfthreshd_ts = cdfthreshd.view(1, H, 1, 1)
402
+ cdfthreshd_ts = cdfthreshd_ts.expand(B, -1, Q, 1).contiguous()
403
+ num_to_select = torch.searchsorted(cdf, cdfthreshd_ts, right=True).squeeze(-1)
404
+ else:
405
+ num_to_select = (topk * K).to(torch.int64).view(1, H, 1).expand(B, -1, Q).contiguous()
406
+
407
+ final_map = torch.zeros_like(pooled_score, dtype=torch.bool)
408
+ final_map[~sim_kblocks] = 1
409
+ final_map[~sim_qblocks] = 1
410
+ final_map = fill_block_map_triton(final_map, num_to_select, sorted_score.indices)
411
+ if is_causal:
412
+ final_map = final_map * causal_mask[None, None, ...]
413
+
414
+ if attention_sink:
415
+ final_map[:, :, :, 0] = 1
416
+
417
+ if not return_lut:
418
+ return final_map, q_int8, q_scale, k_int8, k_scale
419
+ else:
420
+ lut, valid_block_num = block_map_lut_triton(final_map)
421
+ return lut, valid_block_num, q_int8, q_scale, k_int8, k_scale
422
+
423
+
424
+ def block_map_to_mask(block_map, BLKQ=128, BLKK=64):
425
+ B, H, x, y = block_map.shape
426
+
427
+ expanded_mask = torch.zeros((B, H, x * BLKQ, y * BLKK), dtype=torch.bool, device=block_map.device)
428
+ for i in range(x):
429
+ for j in range(y):
430
+ expanded_mask[..., i * BLKQ: (i + 1) * BLKQ, j * BLKK: (j + 1) * BLKK] = block_map[..., i:i+1, j:j+1]
431
+
432
+ return expanded_mask
433
+
434
+ def block_map_lut(block_map):
435
+ valid_entry_num = block_map.to(torch.int32).sum(dim=-1)
436
+
437
+ B, H, x, y = block_map.shape
438
+
439
+ one_matrix = torch.ones((B, H, x, y), dtype=torch.int32, device=block_map.device)
440
+ cum_matrix = torch.cumsum(one_matrix, dim=-1)
441
+ masked_cum_matrix = cum_matrix * block_map.to(torch.int32)
442
+ filled_matrix = masked_cum_matrix.clone()
443
+ filled_matrix[~block_map] = 10000000
444
+ lut = torch.sort(filled_matrix, dim=-1)[0] - 1 # make index start from 0
445
+ lut[:, :, :, 1:] = lut[:, :, :, 1:] - lut[:, :, :, :-1]
446
+
447
+ return lut.to(torch.int32), valid_entry_num.to(torch.int32)