main
py 613 lines 20.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import torch
5
6 from vllm.model_executor.layers.quantization.utils.quant_utils import (
7 FP8_DTYPE,
8 get_fp8_min_max,
9 )
10 from vllm.platforms import current_platform
11 from vllm.triton_utils import tl, triton
12 from vllm.utils.torch_utils import is_quantized_kv_cache
13 from vllm.v1.kv_cache_interface import KVQuantMode
14
15 FP8_MIN, FP8_MAX = get_fp8_min_max()
16
17 _NATIVE_KV_CACHE_DTYPES = {"auto", "float16", "bfloat16", "float32", "half", "float"}
18
19
20 def _is_supported_kv_cache_dtype(kv_cache_dtype: str) -> bool:
21 if not (
22 kv_cache_dtype in _NATIVE_KV_CACHE_DTYPES
23 or is_quantized_kv_cache(kv_cache_dtype)
24 ):
25 return False
26 if kv_cache_dtype.startswith("fp8"):
27 return current_platform.has_device_capability(89) or current_platform.is_xpu()
28 if kv_cache_dtype == "bfloat16":
29 return current_platform.has_device_capability(80) or current_platform.is_xpu()
30 return True
31
32
33 @triton.jit
34 def reshape_and_cache_kernel_flash(
35 key_ptr, # [num_tokens, num_heads, head_size]
36 value_ptr, # [num_tokens, num_heads, head_size]
37 key_cache_ptr, # [num_blocks, block_size, num_heads, head_size]
38 value_cache_ptr, # [num_blocks, block_size, num_heads, head_size]
39 slot_mapping_ptr, # [num_tokens]
40 k_scale, # float32
41 v_scale, # float32
42 # strides
43 key_stride: tl.int64,
44 value_stride: tl.int64,
45 block_stride: tl.int64,
46 head_stride: tl.int64,
47 dim_stride_k: tl.int64,
48 dim_stride_v: tl.int64,
49 page_stride: tl.int64,
50 num_heads: tl.constexpr,
51 head_size: tl.constexpr,
52 block_size: tl.constexpr,
53 x: tl.constexpr,
54 USE_HEAD_MAJOR_LAYOUT: tl.constexpr,
55 # FP8 flags
56 FP8_KV_CACHE: tl.constexpr,
57 # tune parameters
58 TILE_SIZE: tl.constexpr,
59 ):
60 token_idx = tl.program_id(axis=0)
61 slot_idx = tl.load(slot_mapping_ptr + token_idx).to(tl.int64)
62 if slot_idx < 0:
63 # Padding token that should be ignored.
64 return
65
66 block_idx = slot_idx // block_size
67 block_offset = slot_idx % block_size
68
69 tile_i = tl.program_id(axis=1)
70 tile_offs = tl.arange(0, TILE_SIZE)
71 tile_pos = tile_i * TILE_SIZE + tile_offs
72 src_key_idx = token_idx * key_stride
73 src_value_idx = token_idx * value_stride
74
75 if USE_HEAD_MAJOR_LAYOUT:
76 # Decompose the tile index back into head and dim coordinates.
77 cur_head = tile_pos // head_size
78 cur_dim = tile_pos % head_size
79 # Value addressing (4D): [Block, Head, Dim, Slot]
80 tgt_idx_v = (
81 block_idx * block_stride
82 + cur_head * head_stride
83 + cur_dim * dim_stride_v
84 + block_offset * 1
85 )
86 # Key addressing (5D): [Block, Head, Dim//8, Slot, 8]
87 tgt_idx_k = (
88 block_idx * block_stride
89 + cur_head * head_stride
90 + (cur_dim // x) * dim_stride_k
91 + block_offset * x
92 + (cur_dim % x)
93 )
94 else:
95 cur_head = tile_pos // head_size
96 cur_dim = tile_pos % head_size
97 tgt_idx_k = (
98 block_idx * block_stride
99 + block_offset * page_stride
100 + cur_head * head_stride
101 + cur_dim
102 )
103 tgt_idx_v = tgt_idx_k
104
105 # [TILE_SIZE]
106 key_load = tl.load(
107 key_ptr + src_key_idx + tile_pos, mask=tile_pos < (num_heads * head_size)
108 )
109 if FP8_KV_CACHE:
110 # tl.store will do the correct implicit cast to fp8,
111 # based on the key_cache_ptr.dtype.element_ty
112 key_tile = key_load if key_load.dtype.is_fp8() else key_load / tl.load(k_scale)
113 else:
114 key_tile = key_load
115
116 # [TILE_SIZE]
117 value_load = tl.load(
118 value_ptr + src_value_idx + tile_pos, mask=tile_pos < (num_heads * head_size)
119 )
120 if FP8_KV_CACHE:
121 if value_load.dtype.is_fp8():
122 value_tile = value_load
123 else:
124 # tl.store will do the correct implicit cast to fp8,
125 # based on the value_cache_ptr.dtype.element_ty
126 value_tile = value_load / tl.load(v_scale)
127 else:
128 value_tile = value_load
129
130 tl.store(
131 key_cache_ptr + tgt_idx_k,
132 key_tile,
133 mask=tile_pos < (num_heads * head_size),
134 )
135 tl.store(
136 value_cache_ptr + tgt_idx_v,
137 value_tile,
138 mask=tile_pos < (num_heads * head_size),
139 )
140 return
141
142
143 # ---------------------------------------------------------------------------
144 # Per-token-head dynamic quantization kernel
145 # Grid: (num_tokens, NUM_KV_HEADS)
146 # Each program handles one (token, head) pair:
147 # 1. Loads K (or V) for that single head
148 # 2. Computes absmax across head_size → scale = absmax / QUANT_MAX
149 # 3. Quantizes and stores the data + per-head scale
150 #
151 # Parametrised by QUANT_MAX / QUANT_MIN so the same code path works
152 # for int8 (±127/128), fp8_e4m3 (±448), and other formats.
153 # ---------------------------------------------------------------------------
154 @triton.jit
155 def _reshape_cache_per_token_head(
156 key_ptr, # [num_tokens, num_kv_heads, head_size]
157 value_ptr, # [num_tokens, num_kv_heads, head_size_v]
158 key_cache_ptr, # [num_blocks, block_size, num_kv_heads, head_size]
159 value_cache_ptr, # [num_blocks, block_size, num_kv_heads, head_size_v]
160 k_scale_cache_ptr, # [num_blocks, block_size, num_kv_heads] float32
161 v_scale_cache_ptr, # [num_blocks, block_size, num_kv_heads] float32
162 slot_mapping_ptr, # [num_tokens]
163 stride_key_tok: tl.int64,
164 stride_key_head: tl.int64,
165 stride_val_tok: tl.int64,
166 stride_val_head: tl.int64,
167 stride_kc_blk: tl.int64, # key_cache stride over blocks
168 stride_kc_slot: tl.int64, # key_cache stride over slots
169 stride_kc_head: tl.int64, # key_cache stride over heads
170 stride_vc_blk: tl.int64,
171 stride_vc_slot: tl.int64,
172 stride_vc_head: tl.int64,
173 stride_ks_blk: tl.int64, # k_scale_cache stride[0] (blocks)
174 stride_ks_slot: tl.int64, # k_scale_cache stride[1] (slots)
175 stride_ks_head: tl.int64, # k_scale_cache stride[2] (heads)
176 stride_vs_blk: tl.int64, # v_scale_cache stride[0] (blocks)
177 stride_vs_slot: tl.int64, # v_scale_cache stride[1] (slots)
178 stride_vs_head: tl.int64, # v_scale_cache stride[2] (heads)
179 block_size: tl.constexpr,
180 head_size: tl.constexpr,
181 head_size_v: tl.constexpr,
182 HEAD_SIZE_PADDED: tl.constexpr, # next_power_of_2(max(head_size, head_size_v))
183 QUANT_MAX: tl.constexpr = 127.0,
184 QUANT_MIN: tl.constexpr = -128.0,
185 IS_INT_QUANT: tl.constexpr = False,
186 ):
187 tok = tl.program_id(0)
188 head = tl.program_id(1)
189
190 slot = tl.load(slot_mapping_ptr + tok).to(tl.int64)
191 if slot < 0:
192 return
193
194 blk = slot // block_size
195 slot_in_blk = slot % block_size
196
197 dim_offs = tl.arange(0, HEAD_SIZE_PADDED)
198
199 # ---- Key: load one head → absmax → quantize → store -------------------
200 k_mask = dim_offs < head_size
201 k_h = tl.load(
202 key_ptr + tok * stride_key_tok + head * stride_key_head + dim_offs,
203 mask=k_mask,
204 other=0.0,
205 ).to(tl.float32)
206
207 k_scale = tl.maximum(tl.max(tl.abs(k_h)) / QUANT_MAX, 1e-6)
208 tl.store(
209 k_scale_cache_ptr
210 + blk * stride_ks_blk
211 + slot_in_blk * stride_ks_slot
212 + head * stride_ks_head,
213 k_scale,
214 )
215
216 k_q = k_h * (1.0 / k_scale)
217 if IS_INT_QUANT:
218 # Round half away from zero before the int8 store truncates.
219 k_q = tl.where(k_q >= 0, k_q + 0.5, k_q - 0.5)
220 k_q = tl.clamp(k_q, QUANT_MIN, QUANT_MAX)
221 tl.store(
222 key_cache_ptr
223 + blk * stride_kc_blk
224 + slot_in_blk * stride_kc_slot
225 + head * stride_kc_head
226 + dim_offs,
227 k_q,
228 mask=k_mask,
229 )
230
231 # ---- Value: same per-head approach ------------------------------------
232 v_mask = dim_offs < head_size_v
233 v_h = tl.load(
234 value_ptr + tok * stride_val_tok + head * stride_val_head + dim_offs,
235 mask=v_mask,
236 other=0.0,
237 ).to(tl.float32)
238
239 v_scale = tl.maximum(tl.max(tl.abs(v_h)) / QUANT_MAX, 1e-6)
240 tl.store(
241 v_scale_cache_ptr
242 + blk * stride_vs_blk
243 + slot_in_blk * stride_vs_slot
244 + head * stride_vs_head,
245 v_scale,
246 )
247
248 v_q = v_h * (1.0 / v_scale)
249 if IS_INT_QUANT:
250 # Round half away from zero before the int8 store truncates.
251 v_q = tl.where(v_q >= 0, v_q + 0.5, v_q - 0.5)
252 v_q = tl.clamp(v_q, QUANT_MIN, QUANT_MAX)
253 tl.store(
254 value_cache_ptr
255 + blk * stride_vc_blk
256 + slot_in_blk * stride_vc_slot
257 + head * stride_vc_head
258 + dim_offs,
259 v_q,
260 mask=v_mask,
261 )
262
263
264 # Mapping from cache torch dtype to (QUANT_MAX, QUANT_MIN) for the
265 # per-token-head quantization kernel.
266 _PER_TOKEN_HEAD_QUANT_PARAMS: dict[torch.dtype, tuple[float, float]] = {
267 torch.int8: (127.0, -128.0),
268 FP8_DTYPE: (FP8_MAX, FP8_MIN),
269 }
270
271
272 def triton_reshape_and_cache_flash_per_token_head_quant(
273 key: torch.Tensor, # [num_tokens, num_kv_heads, head_size]
274 value: torch.Tensor, # [num_tokens, num_kv_heads, head_size_v]
275 key_cache: torch.Tensor, # [num_blocks, block_size, num_kv_heads, head_size]
276 value_cache: torch.Tensor, # [num_blocks, block_size, num_kv_heads, head_size_v]
277 k_scale_cache: torch.Tensor, # [num_blocks, block_size, num_kv_heads] float32
278 v_scale_cache: torch.Tensor, # [num_blocks, block_size, num_kv_heads] float32
279 slot_mapping: torch.Tensor, # [num_tokens]
280 kv_quant_mode: KVQuantMode,
281 ):
282 """Quantize key/value per (token, head) and write to paged cache.
283
284 Computes one scale = absmax / QUANT_MAX per (token, head), stores
285 quantized data in key_cache/value_cache, and stores the float32
286 scale in k_scale_cache/v_scale_cache.
287
288 INT4 needs sub-byte packing + a Hadamard rotation, so it is handled by
289 its own kernel; INT8 / FP8 share this kernel, with the quantization
290 range (QUANT_MAX, QUANT_MIN) derived from the cache tensor dtype.
291 """
292 if kv_quant_mode == KVQuantMode.INT4_PER_TOKEN_HEAD:
293 from vllm.v1.attention.ops.int4_per_token_head import (
294 reshape_and_cache_int4,
295 )
296
297 reshape_and_cache_int4(
298 key,
299 value,
300 key_cache,
301 value_cache,
302 slot_mapping,
303 k_scale_cache=k_scale_cache,
304 v_scale_cache=v_scale_cache,
305 )
306 return
307
308 cache_dtype = key_cache.dtype
309 quant_params = _PER_TOKEN_HEAD_QUANT_PARAMS.get(cache_dtype)
310 if quant_params is None:
311 raise ValueError(
312 f"Per-token-head quantization not supported for cache dtype "
313 f"{cache_dtype}. Supported: {list(_PER_TOKEN_HEAD_QUANT_PARAMS)}"
314 )
315 quant_max, quant_min = quant_params
316
317 num_tokens, num_kv_heads, head_size = key.shape
318 head_size_v = value.shape[2]
319 head_size_padded = triton.next_power_of_2(max(head_size, head_size_v))
320
321 block_size = key_cache.shape[1]
322
323 if current_platform.is_rocm() or current_platform.is_xpu():
324 num_warps = 4
325 else:
326 num_warps = min(16, max(1, head_size_padded // 32))
327
328 _reshape_cache_per_token_head[(num_tokens, num_kv_heads)](
329 key_ptr=key,
330 value_ptr=value,
331 key_cache_ptr=key_cache,
332 value_cache_ptr=value_cache,
333 k_scale_cache_ptr=k_scale_cache,
334 v_scale_cache_ptr=v_scale_cache,
335 slot_mapping_ptr=slot_mapping,
336 stride_key_tok=key.stride(0),
337 stride_key_head=key.stride(1),
338 stride_val_tok=value.stride(0),
339 stride_val_head=value.stride(1),
340 stride_kc_blk=key_cache.stride(0),
341 stride_kc_slot=key_cache.stride(1),
342 stride_kc_head=key_cache.stride(2),
343 stride_vc_blk=value_cache.stride(0),
344 stride_vc_slot=value_cache.stride(1),
345 stride_vc_head=value_cache.stride(2),
346 stride_ks_blk=k_scale_cache.stride(0),
347 stride_ks_slot=k_scale_cache.stride(1),
348 stride_ks_head=k_scale_cache.stride(2),
349 stride_vs_blk=v_scale_cache.stride(0),
350 stride_vs_slot=v_scale_cache.stride(1),
351 stride_vs_head=v_scale_cache.stride(2),
352 block_size=block_size,
353 head_size=head_size,
354 head_size_v=head_size_v,
355 HEAD_SIZE_PADDED=head_size_padded,
356 QUANT_MAX=quant_max,
357 QUANT_MIN=quant_min,
358 IS_INT_QUANT=cache_dtype == torch.int8,
359 num_warps=num_warps,
360 )
361
362
363 def triton_reshape_and_cache_flash(
364 key: torch.Tensor, # [num_tokens, num_heads, head_size]
365 value: torch.Tensor, # [num_tokens, num_heads, head_size]
366 # [num_blocks, block_size, num_heads, head_size]
367 key_cache: torch.Tensor,
368 # [num_blocks, block_size, num_heads, head_size]
369 value_cache: torch.Tensor,
370 slot_mapping: torch.Tensor, # [num_tokens]
371 kv_cache_dtype: str, # "auto", "fp8"
372 k_scale: torch.Tensor, # float32
373 v_scale: torch.Tensor, # float32
374 ):
375 num_heads = key.shape[1]
376 head_size = key.shape[2]
377
378 use_head_major_layout = key_cache.ndim == 5
379 if use_head_major_layout:
380 block_size = key_cache.shape[3]
381 x = key_cache.shape[4]
382 head_stride = key_cache.stride(1)
383 dim_stride_k = key_cache.stride(2)
384 dim_stride_v = value_cache.stride(2)
385 else:
386 block_size = key_cache.shape[1]
387 x = 1
388 dim_stride_k = 0
389 dim_stride_v = 0
390 head_stride = key_cache.stride()[2]
391 n = num_heads * head_size
392 key_stride = key.stride()[0]
393 value_stride = value.stride()[0]
394 block_stride = key_cache.stride()[0]
395 page_stride = key_cache.stride()[1]
396
397 assert _is_supported_kv_cache_dtype(kv_cache_dtype), (
398 f"Triton reshape-and-cache cannot store kv_cache_dtype={kv_cache_dtype} "
399 f"on this device: an FP8 KV cache needs native fp8e4nv (SM89+). Use "
400 f"--kv-cache-dtype bfloat16 (or float16 on SM75)."
401 )
402 kv_cache_torch_dtype = (
403 current_platform.fp8_dtype()
404 if is_quantized_kv_cache(kv_cache_dtype)
405 else key_cache.dtype
406 )
407
408 if key_cache.dtype != kv_cache_torch_dtype and is_quantized_kv_cache(
409 kv_cache_dtype
410 ):
411 # to avoid erounous implicit cast in triton kernel (tl.store to uint8)
412 # (e.g. explicit cast to fp8e4m3fnuz is not supported in triton 3.4)
413 key_cache = key_cache.view(kv_cache_torch_dtype)
414 value_cache = value_cache.view(kv_cache_torch_dtype)
415 FP8_KV_CACHE = is_quantized_kv_cache(kv_cache_dtype)
416 # heuristics instead of autotuning
417 TILE_SIZE = min(2048, triton.next_power_of_2(n))
418 if current_platform.is_rocm() or current_platform.is_xpu():
419 num_stages = 4
420 num_warps = 8
421 else: # cuda
422 num_stages = 10
423 num_warps = 16
424 if torch.cuda.get_device_capability(key.device)[0] < 9:
425 TILE_SIZE = min(512, TILE_SIZE)
426
427 # TODO(ngl): maybe replace with static launch grid to avoid overhead if
428 # using cudagraphs
429 grid = lambda meta: (
430 slot_mapping.shape[0],
431 triton.cdiv(n, meta["TILE_SIZE"]),
432 )
433
434 reshape_and_cache_kernel_flash[grid](
435 key_ptr=key,
436 value_ptr=value,
437 key_cache_ptr=key_cache,
438 value_cache_ptr=value_cache,
439 slot_mapping_ptr=slot_mapping,
440 k_scale=k_scale,
441 v_scale=v_scale,
442 # strides
443 key_stride=key_stride,
444 value_stride=value_stride,
445 block_stride=block_stride,
446 head_stride=head_stride,
447 dim_stride_k=dim_stride_k,
448 dim_stride_v=dim_stride_v,
449 page_stride=page_stride,
450 num_heads=num_heads,
451 head_size=head_size,
452 block_size=block_size,
453 x=x,
454 USE_HEAD_MAJOR_LAYOUT=use_head_major_layout,
455 FP8_KV_CACHE=FP8_KV_CACHE,
456 # autotune parameters
457 TILE_SIZE=TILE_SIZE,
458 num_warps=num_warps,
459 num_stages=num_stages,
460 )
461
462
463 @triton.jit
464 def reshape_and_cache_kernel_flash_diffkv(
465 key_ptr, # [num_tokens, num_heads, head_size]
466 value_ptr, # [num_tokens, num_heads, head_size_v]
467 kv_cache_ptr, # [num_blocks, block_size, num_heads, head_size + head_size_v]
468 slot_mapping_ptr, # [num_tokens]
469 k_scale, # float32
470 v_scale, # float32
471 # strides
472 key_stride: tl.int64,
473 value_stride: tl.int64,
474 block_stride: tl.int64,
475 page_stride: tl.int64,
476 head_stride: tl.int64,
477 num_heads: tl.constexpr,
478 head_size_k: tl.constexpr,
479 head_size_v: tl.constexpr,
480 block_size: tl.constexpr,
481 # FP8 flags
482 FP8_KV_CACHE: tl.constexpr,
483 # tune parameters
484 TILE_SIZE: tl.constexpr,
485 ):
486 token_idx = tl.program_id(axis=0)
487 slot_idx = tl.load(slot_mapping_ptr + token_idx).to(tl.int64)
488 if slot_idx < 0:
489 # Padding token that should be ignored.
490 return
491
492 tile_i = tl.program_id(axis=1)
493 tile_offs = tl.arange(0, TILE_SIZE)
494
495 block_idx = slot_idx // block_size
496 block_offset = slot_idx % block_size
497
498 src_key_idx = token_idx * key_stride + tile_i * head_size_k
499 src_value_idx = token_idx * value_stride + tile_i * head_size_v
500
501 tgt_idx = (
502 block_idx * block_stride + block_offset * page_stride + tile_i * head_stride
503 )
504
505 # [TILE_SIZE]
506 key_load = tl.load(key_ptr + src_key_idx + tile_offs, mask=tile_offs < head_size_k)
507 if FP8_KV_CACHE:
508 # tl.store will do the correct implicit cast to fp8,
509 # based on the key_cache_ptr.dtype.element_ty
510 key_tile = key_load if key_load.dtype.is_fp8() else key_load / tl.load(k_scale)
511 else:
512 key_tile = key_load
513
514 # [TILE_SIZE]
515 value_load = tl.load(
516 value_ptr + src_value_idx + tile_offs, mask=tile_offs < head_size_v
517 )
518 if FP8_KV_CACHE:
519 if value_load.dtype.is_fp8():
520 value_tile = value_load
521 else:
522 # tl.store will do the correct implicit cast to fp8,
523 # based on the value_cache_ptr.dtype.element_ty
524 value_tile = value_load / tl.load(v_scale)
525 else:
526 value_tile = value_load
527
528 tl.store(
529 kv_cache_ptr + tgt_idx + tile_offs,
530 key_tile,
531 mask=tile_offs < head_size_k,
532 )
533 tl.store(
534 kv_cache_ptr + tgt_idx + head_size_k + tile_offs,
535 value_tile,
536 mask=tile_offs < head_size_v,
537 )
538 return
539
540
541 def triton_reshape_and_cache_flash_diffkv(
542 key: torch.Tensor, # [num_tokens, num_heads, head_size]
543 value: torch.Tensor, # [num_tokens, num_heads, head_size_v]
544 # Strided [num_blocks, block_size, num_heads, head_size + head_size_v].
545 kv_cache: torch.Tensor,
546 slot_mapping: torch.Tensor, # [num_tokens]
547 kv_cache_dtype: str, # "auto", "fp8"
548 k_scale: torch.Tensor, # float32
549 v_scale: torch.Tensor, # float32
550 ):
551 num_heads = key.shape[1]
552 head_size_k = key.shape[2]
553 head_size_v = value.shape[2]
554 block_size = kv_cache.shape[1]
555
556 k_stride = key.stride()[0]
557 v_stride = value.stride()[0]
558 block_stride = kv_cache.stride()[0]
559 page_stride = kv_cache.stride()[1]
560 head_stride = kv_cache.stride()[2]
561
562 kv_cache_torch_dtype = (
563 current_platform.fp8_dtype()
564 if is_quantized_kv_cache(kv_cache_dtype)
565 else kv_cache.dtype
566 )
567
568 if kv_cache.dtype != kv_cache_torch_dtype and is_quantized_kv_cache(kv_cache_dtype):
569 # to avoid erounous implicit cast in triton kernel (tl.store to uint8)
570 # (e.g. explicit cast to fp8e4m3fnuz is not supported in triton 3.4)
571 kv_cache = kv_cache.view(kv_cache_torch_dtype)
572 FP8_KV_CACHE = is_quantized_kv_cache(kv_cache_dtype)
573 # heuristics instead of autotuning
574 TILE_SIZE = max(head_size_k, head_size_v)
575 TILE_SIZE = triton.next_power_of_2(TILE_SIZE)
576 if current_platform.is_rocm() or current_platform.is_xpu():
577 num_stages = 4
578 num_warps = 8
579 else: # cuda
580 num_stages = 10
581 num_warps = 16
582
583 # TODO(ngl): maybe replace with static launch grid to avoid overhead if
584 # using cudagraphs
585 grid = lambda meta: (
586 slot_mapping.shape[0],
587 num_heads,
588 )
589
590 reshape_and_cache_kernel_flash_diffkv[grid](
591 key_ptr=key,
592 value_ptr=value,
593 kv_cache_ptr=kv_cache,
594 slot_mapping_ptr=slot_mapping,
595 k_scale=k_scale,
596 v_scale=v_scale,
597 # strides
598 key_stride=k_stride,
599 value_stride=v_stride,
600 block_stride=block_stride,
601 page_stride=page_stride,
602 head_stride=head_stride,
603 num_heads=num_heads,
604 head_size_k=head_size_k,
605 head_size_v=head_size_v,
606 block_size=block_size,
607 # FP8 flags
608 FP8_KV_CACHE=FP8_KV_CACHE,
609 # autotune parameters
610 TILE_SIZE=TILE_SIZE,
611 num_warps=num_warps,
612 num_stages=num_stages,
613 )