| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | |
| 4 | # Authors: |
| 5 | # - Burkhard Ringlein <ngl@zurich.ibm.com> |
| 6 | # - Jan van Lunteren <jvl@zurich.ibm.com> |
| 7 | # - Chih-Chieh Yang <chih.chieh.yang@ibm.com> |
| 8 | # - Thomas Parnell <tpa@zurich.ibm.com> |
| 9 | |
| 10 | import torch |
| 11 | |
| 12 | from vllm import _custom_ops as ops |
| 13 | from vllm.logger import init_logger |
| 14 | from vllm.platforms import current_platform |
| 15 | from vllm.triton_utils import tl, triton |
| 16 | |
| 17 | from .prefix_prefill import context_attention_fwd |
| 18 | |
| 19 | logger = init_logger(__name__) |
| 20 | |
| 21 | float8_info = torch.finfo(current_platform.fp8_dtype()) |
| 22 | |
| 23 | |
| 24 | def has_native_kv_cache_layout( |
| 25 | key_cache: torch.Tensor, |
| 26 | value_cache: torch.Tensor, |
| 27 | ) -> bool: |
| 28 | """Return whether KV cache blocks can use the native ROCm pairing. |
| 29 | |
| 30 | The native reshape_and_cache writer assumes packed blocks. If cache update |
| 31 | needs reshape_and_cache_flash for a stride-padded hybrid layout, decode |
| 32 | should use the matching Triton path too. |
| 33 | """ |
| 34 | return ( |
| 35 | key_cache.stride(0) == key_cache.shape[1:].numel() |
| 36 | and value_cache.stride(0) == value_cache.shape[1:].numel() |
| 37 | ) |
| 38 | |
| 39 | |
| 40 | @triton.jit |
| 41 | def cdiv_fn(x, y): |
| 42 | return (x + y - 1) // y |
| 43 | |
| 44 | |
| 45 | @triton.jit |
| 46 | def kernel_paged_attention_2d( |
| 47 | output_ptr, # [num_tokens, num_query_heads, head_size] |
| 48 | query_ptr, # [num_tokens, num_query_heads, head_size] |
| 49 | key_cache_ptr, # [num_blks, num_kv_heads, head_size // x, blk_size, x] |
| 50 | value_cache_ptr, # [num_blks, num_kv_heads, head_size, blk_size] |
| 51 | sink_ptr, # [num_query_heads] |
| 52 | block_tables_ptr, # [num_seqs, max_num_blocks_per_seq] |
| 53 | seq_lens_ptr, # [num_seqs] |
| 54 | alibi_slopes_ptr, # [num_query_heads] |
| 55 | scale, # float32 |
| 56 | k_scale, # float32 |
| 57 | v_scale, # float32 |
| 58 | out_scale_inv, |
| 59 | num_query_heads: tl.constexpr, # int |
| 60 | num_queries_per_kv: tl.constexpr, # int |
| 61 | num_queries_per_kv_padded: tl.constexpr, # int |
| 62 | block_table_stride: tl.int64, # int |
| 63 | query_stride_0: tl.int64, # int |
| 64 | query_stride_1: tl.int64, # int, should be equal to head_size |
| 65 | output_stride_0: tl.int64, # int |
| 66 | output_stride_1: tl.int64, # int, should be equal to head_size |
| 67 | BLOCK_SIZE: tl.constexpr, # int |
| 68 | PHYSICAL_BLOCK_SIZE: tl.constexpr, # int |
| 69 | HEAD_SIZE: tl.constexpr, # int |
| 70 | HEAD_SIZE_PADDED: tl.constexpr, # int, must be power of 2 |
| 71 | USE_ALIBI_SLOPES: tl.constexpr, # bool |
| 72 | SLIDING_WINDOW: tl.constexpr, # int |
| 73 | x: tl.constexpr, # int |
| 74 | stride_k_cache_0: tl.int64, # int |
| 75 | stride_k_cache_1: tl.int64, # int |
| 76 | stride_k_cache_2: tl.int64, # int |
| 77 | stride_k_cache_3: tl.int64, # int |
| 78 | stride_k_cache_4: tl.int64, # int |
| 79 | stride_v_cache_0: tl.int64, # int |
| 80 | stride_v_cache_1: tl.int64, # int |
| 81 | stride_v_cache_2: tl.int64, # int |
| 82 | stride_v_cache_3: tl.int64, # int |
| 83 | filter_by_query_len: tl.constexpr, # bool |
| 84 | query_start_len_ptr, # [num_seqs+1] |
| 85 | USE_SINKS: tl.constexpr, # bool |
| 86 | USE_FP8: tl.constexpr, |
| 87 | FP8_MIN: tl.constexpr = float8_info.min, |
| 88 | FP8_MAX: tl.constexpr = float8_info.max, |
| 89 | ): |
| 90 | seq_idx = tl.program_id(0) |
| 91 | kv_head_idx = tl.program_id(1) |
| 92 | |
| 93 | if filter_by_query_len: |
| 94 | cur_batch_in_all_start_index = tl.load(query_start_len_ptr + seq_idx) |
| 95 | cur_batch_in_all_stop_index = tl.load(query_start_len_ptr + seq_idx + 1) |
| 96 | cur_batch_query_len = cur_batch_in_all_stop_index - cur_batch_in_all_start_index |
| 97 | if cur_batch_query_len > 1: |
| 98 | return |
| 99 | else: |
| 100 | cur_batch_in_all_start_index = seq_idx |
| 101 | |
| 102 | query_head_idx = kv_head_idx * num_queries_per_kv + tl.arange( |
| 103 | 0, num_queries_per_kv_padded |
| 104 | ) |
| 105 | |
| 106 | query_offset = ( |
| 107 | cur_batch_in_all_start_index * query_stride_0 |
| 108 | + query_head_idx[:, None] * query_stride_1 |
| 109 | ) |
| 110 | |
| 111 | head_mask = query_head_idx < (kv_head_idx + 1) * num_queries_per_kv |
| 112 | head_mask = head_mask & (query_head_idx < num_query_heads) |
| 113 | |
| 114 | dim_mask = tl.where(tl.arange(0, HEAD_SIZE_PADDED) < HEAD_SIZE, 1, 0).to(tl.int1) |
| 115 | |
| 116 | # Q : (num_queries_per_kv, HEAD_SIZE,) |
| 117 | Q = tl.load( |
| 118 | query_ptr + query_offset + tl.arange(0, HEAD_SIZE_PADDED)[None, :], |
| 119 | mask=dim_mask[None, :] & head_mask[:, None], |
| 120 | other=0.0, |
| 121 | ) |
| 122 | |
| 123 | block_table_offset = seq_idx * block_table_stride |
| 124 | |
| 125 | if not USE_SINKS: |
| 126 | M = tl.full([num_queries_per_kv_padded], float("-inf"), dtype=tl.float32) |
| 127 | L = tl.zeros([num_queries_per_kv_padded], dtype=tl.float32) |
| 128 | else: |
| 129 | M = tl.load( |
| 130 | sink_ptr + query_head_idx, |
| 131 | mask=head_mask, |
| 132 | other=float("-inf"), |
| 133 | ).to(dtype=tl.float32) |
| 134 | L = tl.where(float("-inf") < M, 1.0, 0.0) |
| 135 | |
| 136 | acc = tl.zeros([num_queries_per_kv_padded, HEAD_SIZE_PADDED], dtype=tl.float32) |
| 137 | |
| 138 | # sequence len for this particular sequence |
| 139 | seq_len = tl.load(seq_lens_ptr + seq_idx) |
| 140 | |
| 141 | # alibi slope for this head |
| 142 | if USE_ALIBI_SLOPES: |
| 143 | alibi_slope = tl.load( |
| 144 | alibi_slopes_ptr + query_head_idx, mask=head_mask, other=0.0 |
| 145 | ) |
| 146 | |
| 147 | num_blocks = cdiv_fn(seq_len, BLOCK_SIZE) |
| 148 | |
| 149 | offs_n = tl.arange(0, BLOCK_SIZE) |
| 150 | offs_d = tl.arange(0, HEAD_SIZE_PADDED) |
| 151 | # iterate through tiles |
| 152 | for j in range(0, num_blocks): |
| 153 | start_n = j * BLOCK_SIZE |
| 154 | # Calculate the logical location within a non-standard physical block, |
| 155 | # such as 544 in Qwen/Qwen3-Next-80B-A3B-Thinking. |
| 156 | # Supports non-contiguous mapping |
| 157 | # from logical blocks to physical blocks |
| 158 | abs_token_idx = start_n + offs_n |
| 159 | l_block_idx = abs_token_idx // PHYSICAL_BLOCK_SIZE |
| 160 | # Vectorized loading of physical block IDs |
| 161 | p_block_idx = tl.load(block_tables_ptr + block_table_offset + l_block_idx) |
| 162 | internal_offsets = abs_token_idx % PHYSICAL_BLOCK_SIZE |
| 163 | |
| 164 | # 5D addressing logic of K |
| 165 | k_offset = ( |
| 166 | p_block_idx[None, :] * stride_k_cache_0 |
| 167 | + kv_head_idx * stride_k_cache_1 |
| 168 | + (offs_d[:, None] // x) * stride_k_cache_2 |
| 169 | + internal_offsets[None, :] * stride_k_cache_3 |
| 170 | + (offs_d[:, None] % x) * stride_k_cache_4 |
| 171 | ) |
| 172 | |
| 173 | # 4D addressing logic of V (Slot is innermost) |
| 174 | v_offset = ( |
| 175 | p_block_idx[:, None] * stride_v_cache_0 |
| 176 | + kv_head_idx * stride_v_cache_1 |
| 177 | + offs_d[None, :] * stride_v_cache_2 |
| 178 | + internal_offsets[:, None] * stride_v_cache_3 |
| 179 | ) |
| 180 | |
| 181 | # Only the final tile can straddle seq_len. Slots >= seq_len are |
| 182 | # unwritten KV cache that may hold NaN/garbage; they are score-masked |
| 183 | # below, but 0 * NaN = NaN would still poison the output, so mask them |
| 184 | # out of the K/V loads too. Earlier tiles are fully written, so they |
| 185 | # use the cheaper token-uniform dim_mask (matching the pre-0.25.0 fast |
| 186 | # path) and skip the per-token predicate entirely. |
| 187 | # K : (HEAD_SIZE, BLOCK_SIZE), V : (BLOCK_SIZE, HEAD_SIZE) |
| 188 | if j == num_blocks - 1: |
| 189 | kv_load_mask = abs_token_idx < seq_len |
| 190 | K_load = tl.load( |
| 191 | key_cache_ptr + k_offset, |
| 192 | mask=dim_mask[:, None] & kv_load_mask[None, :], |
| 193 | other=0.0, |
| 194 | eviction_policy="evict_last", |
| 195 | ) |
| 196 | V_load = tl.load( |
| 197 | value_cache_ptr + v_offset, |
| 198 | mask=dim_mask[None, :] & kv_load_mask[:, None], |
| 199 | other=0.0, |
| 200 | eviction_policy="evict_last", |
| 201 | ) |
| 202 | else: |
| 203 | K_load = tl.load( |
| 204 | key_cache_ptr + k_offset, |
| 205 | mask=dim_mask[:, None], |
| 206 | other=0.0, |
| 207 | eviction_policy="evict_last", |
| 208 | ) |
| 209 | V_load = tl.load( |
| 210 | value_cache_ptr + v_offset, |
| 211 | mask=dim_mask[None, :], |
| 212 | other=0.0, |
| 213 | eviction_policy="evict_last", |
| 214 | ) |
| 215 | |
| 216 | if K_load.dtype.is_fp8(): |
| 217 | K = (K_load.to(tl.float32) * tl.load(k_scale)).to(Q.dtype) |
| 218 | else: |
| 219 | K = K_load |
| 220 | |
| 221 | if V_load.dtype.is_fp8(): |
| 222 | V = (V_load.to(tl.float32) * tl.load(v_scale)).to(Q.dtype) |
| 223 | else: |
| 224 | V = V_load |
| 225 | |
| 226 | seq_offset = j * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) |
| 227 | boundary = tl.full([BLOCK_SIZE], seq_len, dtype=tl.int32) |
| 228 | seq_mask = seq_offset[None, :] < boundary |
| 229 | |
| 230 | # First calculate the dot, then apply the mask. |
| 231 | qk = scale * tl.dot(Q, K) |
| 232 | S = tl.where(head_mask[:, None] & seq_mask, qk, float("-inf")) |
| 233 | |
| 234 | context_len = seq_len - 1 |
| 235 | |
| 236 | if SLIDING_WINDOW > 0: |
| 237 | S = tl.where((context_len - seq_offset) < SLIDING_WINDOW, S, -10000) |
| 238 | |
| 239 | if USE_ALIBI_SLOPES: |
| 240 | S += alibi_slope[:, None] * (seq_offset - context_len) |
| 241 | |
| 242 | # compute running maximum |
| 243 | # m_j : (num_queries_per_kv,) |
| 244 | m_j = tl.maximum(M, tl.max(S, axis=1)) |
| 245 | |
| 246 | # P : (num_queries_per_kv, BLOCK_SIZE,) |
| 247 | p = tl.exp(S - m_j[:, None]) |
| 248 | p = tl.where(m_j[:, None] == float("-inf"), 0.0, p) |
| 249 | |
| 250 | # l_j : (num_queries_per_kv,) |
| 251 | l_j = tl.sum(p, axis=1) |
| 252 | |
| 253 | # alpha : (num_queries_per_kv, ) |
| 254 | alpha = tl.exp(M - m_j) |
| 255 | alpha = tl.where(float("-inf") == M, 0.0, alpha) |
| 256 | |
| 257 | # acc : (num_queries_per_kv, BLOCK_SIZE,) |
| 258 | acc = acc * alpha[:, None] |
| 259 | |
| 260 | # update constants |
| 261 | L = L * alpha + l_j |
| 262 | M = m_j |
| 263 | |
| 264 | # acc : (num_queries_per_kv, BLOCK_SIZE,) |
| 265 | acc += tl.dot(p.to(V.dtype), V) |
| 266 | |
| 267 | # epilogue |
| 268 | acc = acc / (L[:, None] + 1e-10) |
| 269 | if USE_FP8: |
| 270 | acc = acc * tl.load(out_scale_inv) |
| 271 | acc = tl.clamp(acc, FP8_MIN, FP8_MAX) |
| 272 | |
| 273 | output_offset = ( |
| 274 | cur_batch_in_all_start_index * output_stride_0 |
| 275 | + query_head_idx * output_stride_1 |
| 276 | ) |
| 277 | |
| 278 | tl.store( |
| 279 | output_ptr + output_offset[:, None] + tl.arange(0, HEAD_SIZE_PADDED)[None, :], |
| 280 | acc, |
| 281 | mask=dim_mask[None, :] & head_mask[:, None], |
| 282 | ) |
| 283 | |
| 284 | |
| 285 | def chunked_prefill_paged_decode( |
| 286 | query, |
| 287 | key, |
| 288 | value, |
| 289 | output, |
| 290 | kv_cache_dtype, |
| 291 | key_cache, |
| 292 | value_cache, |
| 293 | block_table, |
| 294 | query_start_loc, |
| 295 | seq_lens, |
| 296 | max_seq_len, |
| 297 | max_query_len, |
| 298 | k_scale, |
| 299 | v_scale, |
| 300 | alibi_slopes=None, |
| 301 | sliding_window=None, |
| 302 | sm_scale=None, |
| 303 | output_scale=None, |
| 304 | # Optional tensor for sinks |
| 305 | sinks=None, |
| 306 | is_block_table_ptr: bool = False, |
| 307 | causal: bool = True, |
| 308 | ): |
| 309 | if sm_scale is None: |
| 310 | sm_scale = 1.0 / (query.shape[2] ** 0.5) |
| 311 | |
| 312 | use_alibi_slopes = alibi_slopes is not None |
| 313 | |
| 314 | if sliding_window is None or sliding_window <= 0: |
| 315 | sliding_window = 0 |
| 316 | |
| 317 | if max_query_len > 1: |
| 318 | context_attention_fwd( |
| 319 | q=query, |
| 320 | k=key, |
| 321 | v=value, |
| 322 | o=output, |
| 323 | kv_cache_dtype=kv_cache_dtype, |
| 324 | k_cache=key_cache, |
| 325 | v_cache=value_cache, |
| 326 | b_loc=block_table, |
| 327 | b_start_loc=query_start_loc, |
| 328 | b_seq_len=seq_lens, |
| 329 | max_seq_len=max_seq_len, |
| 330 | max_input_len=max_query_len, |
| 331 | k_scale=k_scale, |
| 332 | v_scale=v_scale, |
| 333 | alibi_slopes=alibi_slopes, |
| 334 | sliding_window=sliding_window, |
| 335 | sm_scale=sm_scale, |
| 336 | skip_decode=True, |
| 337 | fp8_out_scale=output_scale, |
| 338 | sinks=sinks, |
| 339 | causal=causal, |
| 340 | ) |
| 341 | |
| 342 | block_size = value_cache.shape[3] |
| 343 | num_seqs = len(seq_lens) |
| 344 | num_query_heads = query.shape[1] |
| 345 | # key may be None in cross-attention decode (already cached from encoder) |
| 346 | num_kv_heads = key.shape[1] if key is not None else key_cache.shape[1] |
| 347 | num_queries_per_kv = num_query_heads // num_kv_heads |
| 348 | head_size = query.shape[2] |
| 349 | |
| 350 | # Conversion of FP8 Tensor from uint8 storage to |
| 351 | # appropriate torch.dtype for interpretation by Triton |
| 352 | if "fp8" in kv_cache_dtype: |
| 353 | assert key_cache.dtype in [torch.uint8, current_platform.fp8_dtype()] |
| 354 | assert value_cache.dtype in [torch.uint8, current_platform.fp8_dtype()] |
| 355 | |
| 356 | if kv_cache_dtype in ("fp8", "fp8_e4m3"): |
| 357 | target_dtype = current_platform.fp8_dtype() |
| 358 | elif kv_cache_dtype == "fp8_e5m2": |
| 359 | target_dtype = torch.float8_e5m2 |
| 360 | else: |
| 361 | raise ValueError( |
| 362 | f"Unsupported FP8 kv_cache_dtype {kv_cache_dtype}: " |
| 363 | f"should be one of 'fp8', 'fp8_e4m3', 'fp8_e5m2'." |
| 364 | ) |
| 365 | |
| 366 | key_cache = key_cache.view(target_dtype) |
| 367 | value_cache = value_cache.view(target_dtype) |
| 368 | |
| 369 | num_queries_per_kv_padded = max(triton.next_power_of_2(num_queries_per_kv), 16) |
| 370 | |
| 371 | from vllm.platforms.rocm import use_rocm_custom_paged_attention |
| 372 | |
| 373 | use_custom = use_rocm_custom_paged_attention( |
| 374 | query.dtype, |
| 375 | head_size, |
| 376 | block_size, |
| 377 | num_queries_per_kv, |
| 378 | max_seq_len, |
| 379 | sliding_window, |
| 380 | kv_cache_dtype, |
| 381 | alibi_slopes, |
| 382 | sinks, |
| 383 | ) |
| 384 | has_native_layout = has_native_kv_cache_layout(key_cache, value_cache) |
| 385 | # Force Triton for non-standard blocks like Qwen3's 544 and for |
| 386 | # stride-padded hybrid layouts. The latter use reshape_and_cache_flash |
| 387 | # during cache update, so keep decode on the matching stride-aware path. |
| 388 | is_pow2 = block_size > 0 and (block_size & (block_size - 1) == 0) |
| 389 | if not is_pow2 or not has_native_layout: |
| 390 | use_custom = False |
| 391 | |
| 392 | if use_custom: |
| 393 | _PARTITION_SIZE_ROCM = 256 |
| 394 | max_num_partitions = ( |
| 395 | max_seq_len + _PARTITION_SIZE_ROCM - 1 |
| 396 | ) // _PARTITION_SIZE_ROCM |
| 397 | assert _PARTITION_SIZE_ROCM % block_size == 0 |
| 398 | total_num_seq = block_table.shape[0] |
| 399 | tmp_output = torch.empty( |
| 400 | size=(total_num_seq, num_query_heads, max_num_partitions, head_size), |
| 401 | dtype=query.dtype, |
| 402 | device=output.device, |
| 403 | ) |
| 404 | exp_sums = torch.empty( |
| 405 | size=(total_num_seq, num_query_heads, max_num_partitions), |
| 406 | dtype=torch.float32, |
| 407 | device=output.device, |
| 408 | ) |
| 409 | max_logits = torch.empty_like(exp_sums) |
| 410 | |
| 411 | ops.paged_attention_rocm( |
| 412 | output, |
| 413 | exp_sums, |
| 414 | max_logits, |
| 415 | tmp_output, |
| 416 | query, |
| 417 | key_cache, |
| 418 | value_cache, |
| 419 | num_kv_heads, |
| 420 | scale=sm_scale, |
| 421 | block_tables=block_table, |
| 422 | seq_lens=seq_lens, |
| 423 | query_start_loc=query_start_loc, |
| 424 | block_size=block_size, |
| 425 | max_seq_len=max_seq_len, |
| 426 | alibi_slopes=alibi_slopes, |
| 427 | kv_cache_dtype=kv_cache_dtype, |
| 428 | k_scale=k_scale, |
| 429 | v_scale=v_scale, |
| 430 | fp8_out_scale=output_scale, |
| 431 | ) |
| 432 | else: |
| 433 | logger.warning_once( |
| 434 | "Cannot use ROCm custom paged attention kernel," |
| 435 | " falling back to Triton implementation." |
| 436 | ) |
| 437 | real_block_size = value_cache.shape[3] |
| 438 | # The standard model directly uses the original block_size. |
| 439 | # Non-standard 544 uses 32 to accommodate integer division logic. |
| 440 | # Cap at 128 to avoid exceeding GPU shared memory limits |
| 441 | # (e.g. hybrid Mamba models inflate block_size to 2048). |
| 442 | # The kernel handles TRITON_BLOCK_SIZE != PHYSICAL_BLOCK_SIZE |
| 443 | # via the l_block_idx/internal_offsets addressing logic. |
| 444 | MAX_TRITON_BLOCK_SIZE = 128 |
| 445 | TRITON_BLOCK_SIZE = min(block_size, MAX_TRITON_BLOCK_SIZE) if is_pow2 else 32 |
| 446 | if is_block_table_ptr: |
| 447 | # Using the physical base address of tensors |
| 448 | kv_element_size = key_cache.element_size() |
| 449 | block_byte_stride = key_cache.stride(0) * kv_element_size |
| 450 | # Get the starting physical address of the KV Cache |
| 451 | base_addr = key_cache.data_ptr() |
| 452 | |
| 453 | # Normalization: Directly calculate the block offset |
| 454 | # of the pointer relative to the base address |
| 455 | processed_block_table = ((block_table - base_addr) // block_byte_stride).to( |
| 456 | torch.int32 |
| 457 | ) |
| 458 | else: |
| 459 | processed_block_table = block_table.to(torch.int32) |
| 460 | |
| 461 | kernel_paged_attention_2d[ |
| 462 | ( |
| 463 | num_seqs, |
| 464 | num_kv_heads, |
| 465 | ) |
| 466 | ]( |
| 467 | output_ptr=output, |
| 468 | query_ptr=query, |
| 469 | key_cache_ptr=key_cache, |
| 470 | value_cache_ptr=value_cache, |
| 471 | sink_ptr=sinks, |
| 472 | block_tables_ptr=processed_block_table, |
| 473 | seq_lens_ptr=seq_lens, |
| 474 | alibi_slopes_ptr=alibi_slopes, |
| 475 | scale=sm_scale, |
| 476 | k_scale=k_scale, |
| 477 | v_scale=v_scale, |
| 478 | out_scale_inv=1.0 / output_scale if output_scale is not None else 1.0, |
| 479 | num_query_heads=num_query_heads, |
| 480 | num_queries_per_kv=num_queries_per_kv, |
| 481 | num_queries_per_kv_padded=num_queries_per_kv_padded, |
| 482 | block_table_stride=processed_block_table.stride(0), |
| 483 | query_stride_0=query.stride(0), |
| 484 | query_stride_1=query.stride(1), |
| 485 | output_stride_0=output.stride(0), |
| 486 | output_stride_1=output.stride(1), |
| 487 | BLOCK_SIZE=TRITON_BLOCK_SIZE, |
| 488 | PHYSICAL_BLOCK_SIZE=real_block_size, |
| 489 | HEAD_SIZE=head_size, |
| 490 | HEAD_SIZE_PADDED=triton.next_power_of_2(head_size), |
| 491 | USE_ALIBI_SLOPES=use_alibi_slopes, |
| 492 | SLIDING_WINDOW=sliding_window, |
| 493 | x=key_cache.shape[4], |
| 494 | stride_k_cache_0=key_cache.stride(0), |
| 495 | stride_k_cache_1=key_cache.stride(1), |
| 496 | stride_k_cache_2=key_cache.stride(2), |
| 497 | stride_k_cache_3=key_cache.stride(3), |
| 498 | stride_k_cache_4=key_cache.stride(4), |
| 499 | stride_v_cache_0=value_cache.stride(0), |
| 500 | stride_v_cache_1=value_cache.stride(1), |
| 501 | stride_v_cache_2=value_cache.stride(2), |
| 502 | stride_v_cache_3=value_cache.stride(3), |
| 503 | filter_by_query_len=True, |
| 504 | query_start_len_ptr=query_start_loc, |
| 505 | USE_SINKS=sinks is not None, |
| 506 | USE_FP8=output_scale is not None, |
| 507 | ) |