PyTorch
llama
federico-alvetreti commited on
Commit
8fc2ee3
·
verified ·
1 Parent(s): 5184a16

Add stage1.py (ModelScope adansa source, verbatim)

Browse files
Files changed (1) hide show
  1. stage1.py +343 -0
stage1.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from typing import Any, Tuple, Union
3
+ from collections import Counter
4
+ import torch
5
+ import triton
6
+ import triton.language as tl
7
+ import warnings
8
+ from native_sparse_attention.ops.triton.utils import get_num_warps_stages, is_hopper_gpu
9
+
10
+
11
+ IS_HOPPER_GPU = is_hopper_gpu()
12
+
13
+
14
+ @triton.jit
15
+ def forward_kernel(
16
+ q_ptr, # Q: n x h x d
17
+ k_ptr, # K: n x h x d
18
+ attn_score_ptr, # S: n x h x d
19
+ # size and stride at compresstion
20
+ kernel_size,
21
+ kernel_stride,
22
+ # seqlens
23
+ cu_seqlens_q,
24
+ cu_seqlens_k,
25
+ # shape
26
+ NUM_KV_HEADS,
27
+ NUM_SHARE_Q_HEADS,
28
+ HEAD_DIM,
29
+ # sm_scale
30
+ sm_scale,
31
+ # stride
32
+ stride_qn,
33
+ stride_qh,
34
+ stride_qd,
35
+ stride_kn,
36
+ stride_kh,
37
+ stride_kd,
38
+ stride_sh,
39
+ stride_sq,
40
+ stride_sk,
41
+ # META parameters
42
+ BLOCK_SIZE_Q: tl.constexpr, # q block size
43
+ BLOCK_SIZE_K: tl.constexpr, # k block size
44
+ BLOCK_SIZE_D: tl.constexpr,
45
+ ):
46
+ qk_scale = sm_scale * 1.44269504
47
+ # get batch id and head id
48
+ pid_b = tl.program_id(0)
49
+ pid_h = tl.program_id(1)
50
+ pid_q = tl.program_id(2)
51
+ pid_kh = pid_h // NUM_SHARE_Q_HEADS
52
+ # get q k start and len after rmpad
53
+ q_start = tl.load(cu_seqlens_q + pid_b)
54
+ q_len = tl.load(cu_seqlens_q + pid_b + 1) - q_start
55
+ k_start = tl.load(cu_seqlens_k + pid_b)
56
+ k_len = tl.load(cu_seqlens_k + pid_b + 1) - k_start
57
+ # skip first kernel_size query block, because they do no attend to any keys
58
+ q_start_in_seq = pid_q * BLOCK_SIZE_Q + kernel_size - 1
59
+ if q_start_in_seq >= q_len:
60
+ return
61
+ # init qkv pointer
62
+ q_ptrs = tl.make_block_ptr(
63
+ base=q_ptr + q_start * stride_qn + pid_h * stride_qh,
64
+ shape=(q_len, HEAD_DIM),
65
+ strides=(stride_qn, stride_qd),
66
+ offsets=(q_start_in_seq, 0),
67
+ block_shape=(BLOCK_SIZE_Q, BLOCK_SIZE_D),
68
+ order=(1, 0),
69
+ )
70
+ k_ptrs = tl.make_block_ptr(
71
+ base=k_ptr + k_start * stride_kn + pid_kh * stride_kh,
72
+ shape=(HEAD_DIM, k_len),
73
+ strides=(stride_kd, stride_kn),
74
+ offsets=(0, 0),
75
+ block_shape=(BLOCK_SIZE_D, BLOCK_SIZE_K),
76
+ order=(0, 1),
77
+ )
78
+ s_ptrs = tl.make_block_ptr(
79
+ base=attn_score_ptr + pid_h * stride_sh + q_start * stride_sq + 0 * stride_sk,
80
+ shape=(q_len, k_len),
81
+ strides=(stride_sq, stride_sk),
82
+ offsets=(q_start_in_seq, 0),
83
+ block_shape=(BLOCK_SIZE_Q, BLOCK_SIZE_K),
84
+ order=(1, 0),
85
+ )
86
+ # load q
87
+ q = tl.load(q_ptrs, boundary_check=(0, 1), padding_option="zero")
88
+ # init statistics
89
+ off_q = tl.arange(0, BLOCK_SIZE_Q) + q_start_in_seq
90
+ off_k = tl.arange(0, BLOCK_SIZE_K) * kernel_stride + kernel_size - 1
91
+ # attention
92
+ lo = 0
93
+ hi = min(k_len, (q_start_in_seq + BLOCK_SIZE_Q - kernel_size) // kernel_stride + 1)
94
+ for i in range(lo, hi, BLOCK_SIZE_K):
95
+ i = tl.multiple_of(i, BLOCK_SIZE_K)
96
+ # load k
97
+ k = tl.load(k_ptrs, boundary_check=(1, 0), padding_option="zero")
98
+ # compute qk
99
+ qk = tl.zeros((BLOCK_SIZE_Q, BLOCK_SIZE_K), dtype=tl.float32)
100
+ qk += tl.where(
101
+ off_q[:, None] >= (i * kernel_stride + off_k)[None, :], 0, float("-inf")
102
+ )
103
+ qk += tl.dot(q, k) * qk_scale
104
+ # store s
105
+ tl.store(s_ptrs, qk.to(tl.bfloat16), boundary_check=(0, 1))
106
+ # update ptrs
107
+ k_ptrs = tl.advance(k_ptrs, (0, BLOCK_SIZE_K))
108
+ s_ptrs = tl.advance(s_ptrs, (0, BLOCK_SIZE_K))
109
+
110
+
111
+ def compressed_attention_fwd(
112
+ q: torch.Tensor,
113
+ k: torch.Tensor,
114
+ kernel_size: int,
115
+ kernel_stride: int,
116
+ cu_seqlens_q: torch.Tensor,
117
+ cu_seqlens_k: torch.Tensor,
118
+ max_seqlen_q: int,
119
+ max_seqlen_k: int,
120
+ sm_scale: float,
121
+ ):
122
+ # dtype check
123
+ assert k.dtype == q.dtype
124
+ assert cu_seqlens_q.dtype == torch.int32 and cu_seqlens_k.dtype == torch.int32
125
+ # shape
126
+ q_len, num_q_heads, head_dim = q.shape
127
+ k_len, num_k_heads, head_dim = k.shape
128
+ batch_size = cu_seqlens_q.shape[0] - 1
129
+ assert q_len > k_len
130
+ # gqa
131
+ assert num_q_heads % num_k_heads == 0
132
+ num_share_q_heads = num_q_heads // num_k_heads
133
+ # output tensor
134
+ # attn_score = torch.full((num_q_heads, q_len, max_seqlen_k), float('-inf'), dtype=q.dtype, device=q.device)
135
+ attn_score = torch.full((q_len, num_q_heads, max_seqlen_k), float('-inf'), dtype=q.dtype, device=q.device)
136
+ # launch kernel
137
+ grid = lambda META: (
138
+ batch_size,
139
+ num_q_heads,
140
+ triton.cdiv(max_seqlen_q, META["BLOCK_SIZE_Q"]),
141
+ )
142
+ BLOCK_SIZE_Q = 128
143
+ BLOCK_SIZE_K = 128
144
+ BLOCK_SIZE_D = triton.next_power_of_2(head_dim)
145
+ num_warps, num_stages = get_num_warps_stages(head_dim, BLOCK_SIZE_Q, IS_HOPPER_GPU)
146
+ forward_kernel[grid](
147
+ q,
148
+ k,
149
+ attn_score,
150
+ kernel_size,
151
+ kernel_stride,
152
+ cu_seqlens_q,
153
+ cu_seqlens_k,
154
+ num_k_heads,
155
+ num_share_q_heads,
156
+ head_dim,
157
+ sm_scale,
158
+ q.stride(0),
159
+ q.stride(1),
160
+ q.stride(2),
161
+ k.stride(0),
162
+ k.stride(1),
163
+ k.stride(2),
164
+ attn_score.stride(1), # qlen
165
+ attn_score.stride(0), # h
166
+ attn_score.stride(2),
167
+ BLOCK_SIZE_Q=BLOCK_SIZE_Q,
168
+ BLOCK_SIZE_K=BLOCK_SIZE_K,
169
+ BLOCK_SIZE_D=BLOCK_SIZE_D,
170
+ num_warps=num_warps,
171
+ num_stages=num_stages,
172
+ )
173
+ return attn_score.transpose(0, 1).contiguous()
174
+
175
+ def reference_attn_score(
176
+ q, k,
177
+ kernel_size, kernel_stride,
178
+ cu_seqlens_q, cu_seqlens_k,
179
+ sm_scale,
180
+ ):
181
+ # q: [total_q, Hq, D], k: [total_k, Hk, D]
182
+ total_q, Hq, D = q.shape
183
+ total_k, Hk, _ = k.shape
184
+ B = cu_seqlens_q.numel() - 1
185
+ share = Hq // Hk
186
+ qk_scale = sm_scale * 1.44269504
187
+
188
+ out = torch.full((Hq, total_q, total_k), float("-inf"), device=q.device, dtype=torch.float32)
189
+
190
+ for b in range(B):
191
+ qs = int(cu_seqlens_q[b].item()); qe = int(cu_seqlens_q[b+1].item())
192
+ ks = int(cu_seqlens_k[b].item()); ke = int(cu_seqlens_k[b+1].item())
193
+ q_len = qe - qs
194
+ k_len = ke - ks
195
+
196
+ q_b = q[qs:qe].float() # [q_len, Hq, D]
197
+ k_b = k[ks:ke].float() # [k_len, Hk, D]
198
+
199
+ # key position in original sequence for compressed k index j
200
+ key_pos = torch.arange(k_len, device=q.device) * kernel_stride + (kernel_size - 1) # [k_len]
201
+
202
+ for hq in range(Hq):
203
+ hk = hq // share
204
+ # [q_len, D] @ [D, k_len] -> [q_len, k_len]
205
+ scores = (q_b[:, hq, :] @ k_b[:, hk, :].T) * qk_scale
206
+
207
+ q_pos = torch.arange(q_len, device=q.device) + (kernel_size - 1) # 注意:你 kernel 的 q_start_in_seq 起点偏移
208
+ # 这里要严格模拟 kernel:kernel 从 q_pos = kernel_size-1 开始写,其它保持 -inf
209
+ # 所以我们把 full q_len 的 scores 先置 -inf,再对可写区间写入
210
+ full_scores = torch.full((q_len, k_len), float("-inf"), device=q.device, dtype=torch.float32)
211
+ valid_q = torch.arange(q_len, device=q.device) >= (kernel_size - 1)
212
+ # causal mask: q_pos >= key_pos
213
+ causal = (q_pos[:, None] >= key_pos[None, :])
214
+ full_scores[valid_q] = torch.where(causal[valid_q], scores[valid_q], float("-inf"))
215
+
216
+ out[hq, qs:qe, ks:ke] = full_scores
217
+
218
+ return out
219
+
220
+
221
+ def reference_attn_score(
222
+ q, k,
223
+ kernel_size, kernel_stride,
224
+ cu_seqlens_q, cu_seqlens_k,
225
+ sm_scale,
226
+ ):
227
+ total_q, Hq, D = q.shape
228
+ total_k, Hk, _ = k.shape
229
+ B = cu_seqlens_q.numel() - 1
230
+ share = Hq // Hk
231
+ qk_scale = sm_scale * 1.44269504
232
+
233
+ out = torch.full((Hq, total_q, total_k), float("-inf"), device=q.device, dtype=torch.bfloat16)
234
+
235
+ for b in range(B):
236
+ qs = int(cu_seqlens_q[b]); qe = int(cu_seqlens_q[b+1])
237
+ ks = int(cu_seqlens_k[b]); ke = int(cu_seqlens_k[b+1])
238
+ q_len = qe - qs
239
+ k_len = ke - ks
240
+
241
+ q_b = q[qs:qe].float()
242
+ k_b = k[ks:ke].float()
243
+
244
+ key_pos = torch.arange(k_len, device=q.device) * kernel_stride + (kernel_size - 1) # [k_len]
245
+ q_pos = torch.arange(q_len, device=q.device) # ✅ 不要 + (kernel_size-1)
246
+ valid_q = q_pos >= (kernel_size - 1)
247
+
248
+ causal = (q_pos[:, None] >= key_pos[None, :]) # [q_len, k_len]
249
+
250
+ for hq in range(Hq):
251
+ hk = hq // share
252
+ scores = (q_b[:, hq, :] @ k_b[:, hk, :].T) * qk_scale # [q_len, k_len]
253
+
254
+ full_scores = torch.full((q_len, k_len), float("-inf"), device=q.device, dtype=torch.float32)
255
+ full_scores[valid_q] = torch.where(causal[valid_q], scores[valid_q], float("-inf"))
256
+ out[hq, qs:qe, ks:ke] = full_scores.to(torch.bfloat16)
257
+
258
+ return out
259
+
260
+
261
+ def test_compressed_attention_fwd(
262
+ device="cuda",
263
+ dtype=torch.bfloat16,
264
+ B=1,
265
+ q_lens=(1024,),
266
+ k_lens=(32,),
267
+ Hq=32,
268
+ Hk=2,
269
+ D=128,
270
+ kernel_size=32,
271
+ kernel_stride=32,
272
+ sm_scale=None,
273
+ atol=2e-2,
274
+ ):
275
+ assert Hq % Hk == 0
276
+ if sm_scale is None:
277
+ sm_scale = 1.0 / math.sqrt(D)
278
+
279
+ # build cu_seqlens and packed q/k
280
+ cu_q = [0]
281
+ cu_k = [0]
282
+ for i in range(B):
283
+ cu_q.append(cu_q[-1] + q_lens[i])
284
+ cu_k.append(cu_k[-1] + k_lens[i])
285
+ cu_seqlens_q = torch.tensor(cu_q, device=device, dtype=torch.int32)
286
+ cu_seqlens_k = torch.tensor(cu_k, device=device, dtype=torch.int32)
287
+
288
+ total_q = cu_q[-1]
289
+ total_k = cu_k[-1]
290
+
291
+ q = torch.randn((total_q, Hq, D), device=device, dtype=dtype)
292
+ k = torch.randn((total_k, Hk, D), device=device, dtype=dtype)
293
+
294
+ max_seqlen_q = max(q_lens)
295
+ max_seqlen_k = max(k_lens)
296
+
297
+ # run triton
298
+ attn_triton = compressed_attention_fwd(
299
+ q, k,
300
+ kernel_size, kernel_stride,
301
+ cu_seqlens_q, cu_seqlens_k,
302
+ max_seqlen_q, max_seqlen_k,
303
+ sm_scale,
304
+ ) # 你需要把 compressed_attention_fwd 修成 return attn_score
305
+
306
+ # reference
307
+ ref = reference_attn_score(
308
+ q, k,
309
+ kernel_size, kernel_stride,
310
+ cu_seqlens_q, cu_seqlens_k,
311
+ sm_scale,
312
+ ) # fp32
313
+
314
+ from infllm_v2 import infllmv2_attn_stage1
315
+
316
+ attn_cuda = infllmv2_attn_stage1(
317
+ q.repeat_interleave(2, dim=1).contiguous(),
318
+ k.contiguous(),
319
+ k.contiguous(),
320
+ cu_seqlens_q=cu_seqlens_q,
321
+ cu_seqlens_k=cu_seqlens_k,
322
+ max_seqlen_q=max_seqlen_q,
323
+ max_seqlen_k=max_seqlen_k,
324
+ causal=True
325
+ ) / 2
326
+ _attn_triton = attn_triton.exp() / (attn_triton.exp().sum(dim=-1, keepdim=True) + 1e-8)
327
+ _attn_triton = _attn_triton.reshape(Hk, -1, _attn_triton.shape[-2], _attn_triton.shape[-1])
328
+ _attn_triton = _attn_triton.sum(dim=1)
329
+
330
+ # compare (ignore -inf)
331
+ attn_t = attn_triton.float()
332
+ mask = torch.isfinite(ref)
333
+ if mask.any():
334
+ max_err = (attn_t[mask] - ref[mask]).abs().max().item()
335
+ else:
336
+ max_err = 0.0
337
+
338
+ print(f"max_abs_err={max_err}")
339
+ assert max_err <= atol, f"too large error: {max_err} > {atol}"
340
+ print("finish")
341
+
342
+ if __name__ == "__main__":
343
+ test_compressed_attention_fwd()