| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | |
| 4 | import torch |
| 5 | |
| 6 | from vllm.triton_utils import tl, triton |
| 7 | from vllm.utils.torch_utils import aux_stream |
| 8 | |
| 9 | LOW_BLOCK_M = 32 |
| 10 | LOW_BLOCK_N = 64 |
| 11 | LOW_NUM_WARPS = 4 |
| 12 | THROUGHPUT_BLOCK_M = 32 |
| 13 | THROUGHPUT_BLOCK_N = 128 |
| 14 | THROUGHPUT_GROUP_M = 2 |
| 15 | THROUGHPUT_NUM_WARPS = 4 |
| 16 | SMALL_TOKEN_THRESHOLD = 128 |
| 17 | SMALL_NUM_WARPS = 2 |
| 18 | Q_BLOCK_ROWS = 8 |
| 19 | Q_NUM_WARPS = 2 |
| 20 | KV_BLOCK_ROWS = 4 |
| 21 | KV_NUM_WARPS = 2 |
| 22 | |
| 23 | |
| 24 | @triton.jit(do_not_specialize=["rows"]) |
| 25 | def _rel_proj_low_latency_kernel( |
| 26 | qkvr_ptr, |
| 27 | rel_proj_ptr, |
| 28 | rel_out_ptr, |
| 29 | log_scaling_ptr, |
| 30 | rows, |
| 31 | stride_x_t, |
| 32 | R_OFFSET: tl.constexpr, |
| 33 | NUM_Q_HEADS: tl.constexpr, |
| 34 | REL_EXTENT: tl.constexpr, |
| 35 | BLOCK_M: tl.constexpr, |
| 36 | BLOCK_N: tl.constexpr, |
| 37 | APPLY_LOG_SCALING: tl.constexpr, |
| 38 | ): |
| 39 | row = tl.program_id(0) * BLOCK_M + tl.arange(0, BLOCK_M) |
| 40 | col = tl.program_id(1) * BLOCK_N + tl.arange(0, BLOCK_N) |
| 41 | inner = tl.arange(0, 16) |
| 42 | token = row // NUM_Q_HEADS |
| 43 | head = row % NUM_Q_HEADS |
| 44 | relative = tl.load( |
| 45 | qkvr_ptr |
| 46 | + token[:, None] * stride_x_t |
| 47 | + R_OFFSET |
| 48 | + head[:, None] * 16 |
| 49 | + inner[None, :], |
| 50 | mask=row[:, None] < rows, |
| 51 | other=0.0, |
| 52 | ) |
| 53 | projection = tl.load( |
| 54 | rel_proj_ptr + inner[:, None] * REL_EXTENT + col[None, :], |
| 55 | mask=col[None, :] < REL_EXTENT, |
| 56 | other=0.0, |
| 57 | ) |
| 58 | values = tl.dot(relative, projection, out_dtype=tl.float32).to( |
| 59 | rel_out_ptr.dtype.element_ty |
| 60 | ) |
| 61 | values = values.to(tl.float32) |
| 62 | if APPLY_LOG_SCALING: |
| 63 | values *= tl.load(log_scaling_ptr + token, mask=row < rows, other=1.0)[:, None] |
| 64 | tl.store( |
| 65 | rel_out_ptr + row[:, None] * REL_EXTENT + col[None, :], |
| 66 | values.to(rel_out_ptr.dtype.element_ty), |
| 67 | mask=(row[:, None] < rows) & (col[None, :] < REL_EXTENT), |
| 68 | ) |
| 69 | |
| 70 | |
| 71 | @triton.jit(do_not_specialize=["rows"]) |
| 72 | def _rel_proj_throughput_kernel( |
| 73 | qkvr_ptr, |
| 74 | rel_proj_ptr, |
| 75 | rel_out_ptr, |
| 76 | log_scaling_ptr, |
| 77 | rows, |
| 78 | stride_x_t, |
| 79 | R_OFFSET: tl.constexpr, |
| 80 | NUM_Q_HEADS: tl.constexpr, |
| 81 | REL_EXTENT: tl.constexpr, |
| 82 | BLOCK_M: tl.constexpr, |
| 83 | BLOCK_N: tl.constexpr, |
| 84 | GROUP_M: tl.constexpr, |
| 85 | APPLY_LOG_SCALING: tl.constexpr, |
| 86 | ): |
| 87 | row_group = tl.program_id(0) |
| 88 | col = tl.program_id(1) * BLOCK_N + tl.arange(0, BLOCK_N) |
| 89 | inner = tl.arange(0, 16) |
| 90 | projection = tl.load( |
| 91 | rel_proj_ptr + inner[:, None] * REL_EXTENT + col[None, :], |
| 92 | mask=col[None, :] < REL_EXTENT, |
| 93 | other=0.0, |
| 94 | ) |
| 95 | row_offsets = tl.arange(0, BLOCK_M) |
| 96 | for group_offset in tl.static_range(GROUP_M): |
| 97 | row = (row_group * GROUP_M + group_offset) * BLOCK_M + row_offsets |
| 98 | token = row // NUM_Q_HEADS |
| 99 | head = row % NUM_Q_HEADS |
| 100 | relative = tl.load( |
| 101 | qkvr_ptr |
| 102 | + token[:, None] * stride_x_t |
| 103 | + R_OFFSET |
| 104 | + head[:, None] * 16 |
| 105 | + inner[None, :], |
| 106 | mask=row[:, None] < rows, |
| 107 | other=0.0, |
| 108 | ) |
| 109 | values = tl.dot(relative, projection, out_dtype=tl.float32).to( |
| 110 | rel_out_ptr.dtype.element_ty |
| 111 | ) |
| 112 | values = values.to(tl.float32) |
| 113 | if APPLY_LOG_SCALING: |
| 114 | values *= tl.load(log_scaling_ptr + token, mask=row < rows, other=1.0)[ |
| 115 | :, None |
| 116 | ] |
| 117 | tl.store( |
| 118 | rel_out_ptr + row[:, None] * REL_EXTENT + col[None, :], |
| 119 | values.to(rel_out_ptr.dtype.element_ty), |
| 120 | mask=(row[:, None] < rows) & (col[None, :] < REL_EXTENT), |
| 121 | ) |
| 122 | |
| 123 | |
| 124 | def use_rel_proj_throughput(rows: int, rel_extent: int) -> bool: |
| 125 | min_rows = 8192 if rel_extent == 512 else 2048 |
| 126 | return rows >= min_rows |
| 127 | |
| 128 | |
| 129 | def qkvr_rel_proj( |
| 130 | qkvr: torch.Tensor, |
| 131 | rel_proj: torch.Tensor, |
| 132 | rel_out: torch.Tensor, |
| 133 | log_scaling: torch.Tensor | None, |
| 134 | *, |
| 135 | num_q_heads: int, |
| 136 | num_kv_heads: int, |
| 137 | head_dim: int, |
| 138 | d_rel: int, |
| 139 | ) -> None: |
| 140 | rows = qkvr.shape[0] * num_q_heads |
| 141 | rel_extent = rel_proj.shape[1] |
| 142 | assert d_rel == 16 and rel_proj.shape[0] == 16 |
| 143 | r_offset = num_q_heads * head_dim + 2 * num_kv_heads * head_dim |
| 144 | log_scaling_ptr = log_scaling if log_scaling is not None else qkvr |
| 145 | common = dict( |
| 146 | R_OFFSET=r_offset, |
| 147 | NUM_Q_HEADS=num_q_heads, |
| 148 | REL_EXTENT=rel_extent, |
| 149 | APPLY_LOG_SCALING=log_scaling is not None, |
| 150 | ) |
| 151 | |
| 152 | if use_rel_proj_throughput(rows, rel_extent): |
| 153 | grid = ( |
| 154 | triton.cdiv(rows, THROUGHPUT_BLOCK_M * THROUGHPUT_GROUP_M), |
| 155 | triton.cdiv(rel_extent, THROUGHPUT_BLOCK_N), |
| 156 | ) |
| 157 | _rel_proj_throughput_kernel[grid]( |
| 158 | qkvr, |
| 159 | rel_proj, |
| 160 | rel_out, |
| 161 | log_scaling_ptr, |
| 162 | rows, |
| 163 | qkvr.stride(0), |
| 164 | BLOCK_M=THROUGHPUT_BLOCK_M, |
| 165 | BLOCK_N=THROUGHPUT_BLOCK_N, |
| 166 | GROUP_M=THROUGHPUT_GROUP_M, |
| 167 | num_warps=THROUGHPUT_NUM_WARPS, |
| 168 | **common, |
| 169 | ) |
| 170 | return |
| 171 | |
| 172 | grid = ( |
| 173 | triton.cdiv(rows, LOW_BLOCK_M), |
| 174 | triton.cdiv(rel_extent, LOW_BLOCK_N), |
| 175 | ) |
| 176 | _rel_proj_low_latency_kernel[grid]( |
| 177 | qkvr, |
| 178 | rel_proj, |
| 179 | rel_out, |
| 180 | log_scaling_ptr, |
| 181 | rows, |
| 182 | qkvr.stride(0), |
| 183 | BLOCK_M=LOW_BLOCK_M, |
| 184 | BLOCK_N=LOW_BLOCK_N, |
| 185 | num_warps=LOW_NUM_WARPS, |
| 186 | **common, |
| 187 | ) |
| 188 | |
| 189 | |
| 190 | @triton.jit(do_not_specialize=["tokens", "stride_block_table_req", "max_blocks"]) |
| 191 | def _qkvr_qkv_kernel( |
| 192 | qkvr_ptr, |
| 193 | q_norm_weight_ptr, |
| 194 | q_out_ptr, |
| 195 | rel_proj_ptr, |
| 196 | rel_out_ptr, |
| 197 | k_weight_ptr, |
| 198 | v_weight_ptr, |
| 199 | k_norm_weight_ptr, |
| 200 | conv_cache_ptr, |
| 201 | key_cache_ptr, |
| 202 | value_cache_ptr, |
| 203 | positions_ptr, |
| 204 | seq_idx_ptr, |
| 205 | conv_slot_mapping_ptr, |
| 206 | conv_block_table_ptr, |
| 207 | query_start_ptr, |
| 208 | attention_slot_mapping_ptr, |
| 209 | log_scaling_ptr, |
| 210 | tokens, |
| 211 | eps, |
| 212 | stride_x_t, |
| 213 | stride_cc_block, |
| 214 | stride_cc_head, |
| 215 | stride_cc_token, |
| 216 | stride_cc_dim, |
| 217 | stride_kc_block, |
| 218 | stride_kc_token, |
| 219 | stride_kc_head, |
| 220 | stride_vc_block, |
| 221 | stride_vc_token, |
| 222 | stride_vc_head, |
| 223 | stride_block_table_req, |
| 224 | max_blocks, |
| 225 | conv_block_size, |
| 226 | attention_page_size, |
| 227 | Q_WIDTH: tl.constexpr, |
| 228 | KV_WIDTH: tl.constexpr, |
| 229 | NUM_Q_HEADS: tl.constexpr, |
| 230 | NUM_KV_HEADS: tl.constexpr, |
| 231 | HEAD_DIM: tl.constexpr, |
| 232 | WINDOW_SIZE: tl.constexpr, |
| 233 | OFF_K: tl.constexpr, |
| 234 | OFF_V: tl.constexpr, |
| 235 | APPLY_LOG_SCALING: tl.constexpr, |
| 236 | D_REL: tl.constexpr, |
| 237 | REL_EXTENT: tl.constexpr, |
| 238 | REL_EXTENT_PADDED: tl.constexpr, |
| 239 | ): |
| 240 | block = tl.program_id(0) |
| 241 | num_q_rows = tokens * NUM_Q_HEADS |
| 242 | dims = tl.arange(0, HEAD_DIM) |
| 243 | |
| 244 | if block < num_q_rows: |
| 245 | row = block |
| 246 | token = row // NUM_Q_HEADS |
| 247 | head = row % NUM_Q_HEADS |
| 248 | values = tl.load( |
| 249 | qkvr_ptr + token * stride_x_t + head * HEAD_DIM + dims, |
| 250 | ).to(tl.float32) |
| 251 | weight = tl.load(q_norm_weight_ptr + dims).to(tl.float32) |
| 252 | rstd = tl.rsqrt(tl.sum(values * values, axis=0) / HEAD_DIM + eps) |
| 253 | normalized = values * rstd * weight |
| 254 | if APPLY_LOG_SCALING: |
| 255 | normalized = normalized.to(q_out_ptr.dtype.element_ty).to(tl.float32) |
| 256 | normalized *= tl.load(log_scaling_ptr + token) |
| 257 | tl.store( |
| 258 | q_out_ptr + row * HEAD_DIM + dims, |
| 259 | normalized.to(q_out_ptr.dtype.element_ty), |
| 260 | ) |
| 261 | rel_cols = tl.arange(0, REL_EXTENT_PADDED) |
| 262 | rel_mask = rel_cols < REL_EXTENT |
| 263 | projected = tl.zeros([REL_EXTENT_PADDED], dtype=tl.float32) |
| 264 | rel_offset = Q_WIDTH + 2 * KV_WIDTH + head * D_REL |
| 265 | for rel_dim in tl.static_range(D_REL): |
| 266 | rel_value = tl.load( |
| 267 | qkvr_ptr + token * stride_x_t + rel_offset + rel_dim |
| 268 | ).to(tl.float32) |
| 269 | proj = tl.load( |
| 270 | rel_proj_ptr + rel_dim * REL_EXTENT + rel_cols, |
| 271 | mask=rel_mask, |
| 272 | other=0.0, |
| 273 | ).to(tl.float32) |
| 274 | projected += rel_value * proj |
| 275 | projected = projected.to(rel_out_ptr.dtype.element_ty).to(tl.float32) |
| 276 | if APPLY_LOG_SCALING: |
| 277 | projected *= tl.load(log_scaling_ptr + token) |
| 278 | tl.store( |
| 279 | rel_out_ptr + row * REL_EXTENT + rel_cols, |
| 280 | projected.to(rel_out_ptr.dtype.element_ty), |
| 281 | mask=rel_mask, |
| 282 | ) |
| 283 | else: |
| 284 | row = block - num_q_rows |
| 285 | if row < tokens * NUM_KV_HEADS: |
| 286 | token = row // NUM_KV_HEADS |
| 287 | head = row % NUM_KV_HEADS |
| 288 | position = tl.load(positions_ptr + token) |
| 289 | request = tl.load(seq_idx_ptr + token) |
| 290 | conv_slot = tl.load(conv_slot_mapping_ptr + token) |
| 291 | query_start = tl.load(query_start_ptr + token) |
| 292 | attention_slot = tl.load(attention_slot_mapping_ptr + token) |
| 293 | valid = conv_slot >= 0 |
| 294 | |
| 295 | k_col = Q_WIDTH + head * HEAD_DIM |
| 296 | v_col = Q_WIDTH + KV_WIDTH + head * HEAD_DIM |
| 297 | k_value = tl.load(qkvr_ptr + token * stride_x_t + k_col + dims) |
| 298 | v_value = tl.load(qkvr_ptr + token * stride_x_t + v_col + dims) |
| 299 | |
| 300 | safe_slot = tl.maximum(conv_slot, 0) |
| 301 | cache_base = ( |
| 302 | conv_cache_ptr |
| 303 | + (safe_slot // conv_block_size) * stride_cc_block |
| 304 | + head * stride_cc_head |
| 305 | + (safe_slot % conv_block_size) * stride_cc_token |
| 306 | ) |
| 307 | tl.store( |
| 308 | cache_base + (OFF_K + dims) * stride_cc_dim, |
| 309 | k_value, |
| 310 | mask=valid, |
| 311 | ) |
| 312 | tl.store( |
| 313 | cache_base + (OFF_V + dims) * stride_cc_dim, |
| 314 | v_value, |
| 315 | mask=valid, |
| 316 | ) |
| 317 | |
| 318 | acc_k = tl.zeros([HEAD_DIM], dtype=tl.float32) |
| 319 | acc_v = tl.zeros([HEAD_DIM], dtype=tl.float32) |
| 320 | for tap in tl.static_range(WINDOW_SIZE): |
| 321 | source_position = position - (WINDOW_SIZE - 1) + tap |
| 322 | source_row = token - (WINDOW_SIZE - 1) + tap |
| 323 | in_window = valid & (source_position >= 0) |
| 324 | intra = in_window & (source_row >= query_start) |
| 325 | cached = in_window & (source_row < query_start) |
| 326 | safe_row = tl.maximum(source_row, 0) |
| 327 | source_k = tl.load( |
| 328 | qkvr_ptr + safe_row * stride_x_t + k_col + dims, |
| 329 | mask=intra, |
| 330 | other=0.0, |
| 331 | ).to(tl.float32) |
| 332 | source_v = tl.load( |
| 333 | qkvr_ptr + safe_row * stride_x_t + v_col + dims, |
| 334 | mask=intra, |
| 335 | other=0.0, |
| 336 | ).to(tl.float32) |
| 337 | safe_position = tl.maximum(source_position, 0) |
| 338 | logical_block = tl.minimum( |
| 339 | safe_position // conv_block_size, max_blocks - 1 |
| 340 | ) |
| 341 | physical_block = tl.load( |
| 342 | conv_block_table_ptr |
| 343 | + request * stride_block_table_req |
| 344 | + logical_block, |
| 345 | mask=cached, |
| 346 | other=0, |
| 347 | ).to(tl.int64) |
| 348 | tap_base = ( |
| 349 | conv_cache_ptr |
| 350 | + physical_block * stride_cc_block |
| 351 | + head * stride_cc_head |
| 352 | + (safe_position % conv_block_size) * stride_cc_token |
| 353 | ) |
| 354 | source_k += tl.load( |
| 355 | tap_base + (OFF_K + dims) * stride_cc_dim, |
| 356 | mask=cached, |
| 357 | other=0.0, |
| 358 | ).to(tl.float32) |
| 359 | source_v += tl.load( |
| 360 | tap_base + (OFF_V + dims) * stride_cc_dim, |
| 361 | mask=cached, |
| 362 | other=0.0, |
| 363 | ).to(tl.float32) |
| 364 | k_weight = tl.load( |
| 365 | k_weight_ptr + (head * HEAD_DIM + dims) * WINDOW_SIZE + tap |
| 366 | ).to(tl.float32) |
| 367 | v_weight = tl.load( |
| 368 | v_weight_ptr + (head * HEAD_DIM + dims) * WINDOW_SIZE + tap |
| 369 | ).to(tl.float32) |
| 370 | acc_k += source_k * k_weight |
| 371 | acc_v += source_v * v_weight |
| 372 | |
| 373 | k_rounded = (acc_k + k_value.to(tl.float32)).to(qkvr_ptr.dtype.element_ty) |
| 374 | v_rounded = (acc_v + v_value.to(tl.float32)).to(qkvr_ptr.dtype.element_ty) |
| 375 | k_float = k_rounded.to(tl.float32) |
| 376 | k_norm_weight = tl.load(k_norm_weight_ptr + dims).to(tl.float32) |
| 377 | rstd = tl.rsqrt(tl.sum(k_float * k_float, axis=0) / HEAD_DIM + eps) |
| 378 | k_normalized = (k_float * rstd * k_norm_weight).to( |
| 379 | qkvr_ptr.dtype.element_ty |
| 380 | ) |
| 381 | |
| 382 | safe_attention_slot = tl.maximum(attention_slot, 0) |
| 383 | attention_block = safe_attention_slot // attention_page_size |
| 384 | attention_offset = safe_attention_slot % attention_page_size |
| 385 | tl.store( |
| 386 | key_cache_ptr |
| 387 | + attention_block * stride_kc_block |
| 388 | + attention_offset * stride_kc_token |
| 389 | + head * stride_kc_head |
| 390 | + dims, |
| 391 | k_normalized, |
| 392 | mask=attention_slot >= 0, |
| 393 | ) |
| 394 | tl.store( |
| 395 | value_cache_ptr |
| 396 | + attention_block * stride_vc_block |
| 397 | + attention_offset * stride_vc_token |
| 398 | + head * stride_vc_head |
| 399 | + dims, |
| 400 | v_rounded, |
| 401 | mask=attention_slot >= 0, |
| 402 | ) |
| 403 | |
| 404 | |
| 405 | @triton.jit(do_not_specialize=["num_rows"]) |
| 406 | def _q_kernel( |
| 407 | qkvr_ptr, |
| 408 | q_norm_weight_ptr, |
| 409 | q_out_ptr, |
| 410 | log_scaling_ptr, |
| 411 | num_rows, |
| 412 | stride_x_t, |
| 413 | eps, |
| 414 | NUM_Q_HEADS: tl.constexpr, |
| 415 | HEAD_DIM: tl.constexpr, |
| 416 | BLOCK_ROWS: tl.constexpr, |
| 417 | APPLY_LOG_SCALING: tl.constexpr, |
| 418 | ): |
| 419 | rows = tl.program_id(0) * BLOCK_ROWS + tl.arange(0, BLOCK_ROWS) |
| 420 | dims = tl.arange(0, HEAD_DIM) |
| 421 | row_mask = rows < num_rows |
| 422 | tokens = rows // NUM_Q_HEADS |
| 423 | heads = rows % NUM_Q_HEADS |
| 424 | values = tl.load( |
| 425 | qkvr_ptr |
| 426 | + tokens[:, None] * stride_x_t |
| 427 | + heads[:, None] * HEAD_DIM |
| 428 | + dims[None, :], |
| 429 | mask=row_mask[:, None], |
| 430 | other=0.0, |
| 431 | ).to(tl.float32) |
| 432 | weight = tl.load(q_norm_weight_ptr + dims).to(tl.float32) |
| 433 | rstd = tl.rsqrt(tl.sum(values * values, axis=1) / HEAD_DIM + eps) |
| 434 | normalized = values * rstd[:, None] * weight[None, :] |
| 435 | if APPLY_LOG_SCALING: |
| 436 | normalized = normalized.to(q_out_ptr.dtype.element_ty).to(tl.float32) |
| 437 | tau = tl.load(log_scaling_ptr + tokens, mask=row_mask, other=1.0) |
| 438 | normalized *= tau[:, None] |
| 439 | tl.store( |
| 440 | q_out_ptr + rows[:, None] * HEAD_DIM + dims[None, :], |
| 441 | normalized.to(q_out_ptr.dtype.element_ty), |
| 442 | mask=row_mask[:, None], |
| 443 | ) |
| 444 | |
| 445 | |
| 446 | @triton.jit(do_not_specialize=["tokens", "stride_block_table_req", "max_blocks"]) |
| 447 | def _kv_kernel( |
| 448 | qkvr_ptr, |
| 449 | k_weight_ptr, |
| 450 | v_weight_ptr, |
| 451 | k_norm_weight_ptr, |
| 452 | conv_cache_ptr, |
| 453 | key_cache_ptr, |
| 454 | value_cache_ptr, |
| 455 | positions_ptr, |
| 456 | seq_idx_ptr, |
| 457 | conv_slot_mapping_ptr, |
| 458 | conv_block_table_ptr, |
| 459 | query_start_ptr, |
| 460 | attention_slot_mapping_ptr, |
| 461 | tokens, |
| 462 | eps, |
| 463 | stride_x_t, |
| 464 | stride_cc_block, |
| 465 | stride_cc_head, |
| 466 | stride_cc_token, |
| 467 | stride_cc_dim, |
| 468 | stride_kc_block, |
| 469 | stride_kc_token, |
| 470 | stride_kc_head, |
| 471 | stride_vc_block, |
| 472 | stride_vc_token, |
| 473 | stride_vc_head, |
| 474 | stride_block_table_req, |
| 475 | max_blocks, |
| 476 | conv_block_size, |
| 477 | attention_page_size, |
| 478 | Q_WIDTH: tl.constexpr, |
| 479 | KV_WIDTH: tl.constexpr, |
| 480 | NUM_KV_HEADS: tl.constexpr, |
| 481 | HEAD_DIM: tl.constexpr, |
| 482 | WINDOW_SIZE: tl.constexpr, |
| 483 | OFF_K: tl.constexpr, |
| 484 | OFF_V: tl.constexpr, |
| 485 | BLOCK_ROWS: tl.constexpr, |
| 486 | ): |
| 487 | token = tl.program_id(0) * BLOCK_ROWS + tl.arange(0, BLOCK_ROWS) |
| 488 | dims = tl.arange(0, HEAD_DIM) |
| 489 | row_mask = token < tokens |
| 490 | head_id = tl.program_id(1) |
| 491 | head = tl.full([BLOCK_ROWS], head_id, tl.int64) |
| 492 | position = tl.load(positions_ptr + token, mask=row_mask, other=0) |
| 493 | request = tl.load(seq_idx_ptr + token, mask=row_mask, other=0) |
| 494 | conv_slot = tl.load(conv_slot_mapping_ptr + token, mask=row_mask, other=-1) |
| 495 | query_start = tl.load(query_start_ptr + token, mask=row_mask, other=0) |
| 496 | attention_slot = tl.load( |
| 497 | attention_slot_mapping_ptr + token, mask=row_mask, other=-1 |
| 498 | ) |
| 499 | valid = row_mask & (conv_slot >= 0) |
| 500 | |
| 501 | k_col = Q_WIDTH + head * HEAD_DIM |
| 502 | v_col = Q_WIDTH + KV_WIDTH + head * HEAD_DIM |
| 503 | k_value = tl.load( |
| 504 | qkvr_ptr + token[:, None] * stride_x_t + k_col[:, None] + dims[None, :], |
| 505 | mask=row_mask[:, None], |
| 506 | other=0.0, |
| 507 | ) |
| 508 | v_value = tl.load( |
| 509 | qkvr_ptr + token[:, None] * stride_x_t + v_col[:, None] + dims[None, :], |
| 510 | mask=row_mask[:, None], |
| 511 | other=0.0, |
| 512 | ) |
| 513 | safe_slot = tl.maximum(conv_slot, 0) |
| 514 | cache_base = ( |
| 515 | conv_cache_ptr |
| 516 | + (safe_slot // conv_block_size) * stride_cc_block |
| 517 | + head * stride_cc_head |
| 518 | + (safe_slot % conv_block_size) * stride_cc_token |
| 519 | ) |
| 520 | tl.store( |
| 521 | cache_base[:, None] + (OFF_K + dims[None, :]) * stride_cc_dim, |
| 522 | k_value, |
| 523 | mask=valid[:, None], |
| 524 | ) |
| 525 | tl.store( |
| 526 | cache_base[:, None] + (OFF_V + dims[None, :]) * stride_cc_dim, |
| 527 | v_value, |
| 528 | mask=valid[:, None], |
| 529 | ) |
| 530 | |
| 531 | acc_k = tl.zeros([BLOCK_ROWS, HEAD_DIM], dtype=tl.float32) |
| 532 | acc_v = tl.zeros([BLOCK_ROWS, HEAD_DIM], dtype=tl.float32) |
| 533 | for tap in tl.static_range(WINDOW_SIZE): |
| 534 | source_position = position - (WINDOW_SIZE - 1) + tap |
| 535 | source_row = token - (WINDOW_SIZE - 1) + tap |
| 536 | in_window = valid & (source_position >= 0) |
| 537 | intra = in_window & (source_row >= query_start) |
| 538 | cached = in_window & (source_row < query_start) |
| 539 | safe_row = tl.maximum(source_row, 0) |
| 540 | source_k = tl.load( |
| 541 | qkvr_ptr + safe_row[:, None] * stride_x_t + k_col[:, None] + dims[None, :], |
| 542 | mask=intra[:, None], |
| 543 | other=0.0, |
| 544 | ).to(tl.float32) |
| 545 | source_v = tl.load( |
| 546 | qkvr_ptr + safe_row[:, None] * stride_x_t + v_col[:, None] + dims[None, :], |
| 547 | mask=intra[:, None], |
| 548 | other=0.0, |
| 549 | ).to(tl.float32) |
| 550 | safe_position = tl.maximum(source_position, 0) |
| 551 | logical_block = tl.minimum(safe_position // conv_block_size, max_blocks - 1) |
| 552 | physical_block = tl.load( |
| 553 | conv_block_table_ptr + request * stride_block_table_req + logical_block, |
| 554 | mask=cached, |
| 555 | other=0, |
| 556 | ).to(tl.int64) |
| 557 | tap_base = ( |
| 558 | conv_cache_ptr |
| 559 | + physical_block * stride_cc_block |
| 560 | + head * stride_cc_head |
| 561 | + (safe_position % conv_block_size) * stride_cc_token |
| 562 | ) |
| 563 | source_k += tl.load( |
| 564 | tap_base[:, None] + (OFF_K + dims[None, :]) * stride_cc_dim, |
| 565 | mask=cached[:, None], |
| 566 | other=0.0, |
| 567 | ).to(tl.float32) |
| 568 | source_v += tl.load( |
| 569 | tap_base[:, None] + (OFF_V + dims[None, :]) * stride_cc_dim, |
| 570 | mask=cached[:, None], |
| 571 | other=0.0, |
| 572 | ).to(tl.float32) |
| 573 | k_weight = tl.load( |
| 574 | k_weight_ptr + (head_id * HEAD_DIM + dims) * WINDOW_SIZE + tap |
| 575 | ).to(tl.float32) |
| 576 | v_weight = tl.load( |
| 577 | v_weight_ptr + (head_id * HEAD_DIM + dims) * WINDOW_SIZE + tap |
| 578 | ).to(tl.float32) |
| 579 | acc_k += source_k * k_weight[None, :] |
| 580 | acc_v += source_v * v_weight[None, :] |
| 581 | |
| 582 | k_rounded = (acc_k + k_value.to(tl.float32)).to(qkvr_ptr.dtype.element_ty) |
| 583 | v_rounded = (acc_v + v_value.to(tl.float32)).to(qkvr_ptr.dtype.element_ty) |
| 584 | k_float = k_rounded.to(tl.float32) |
| 585 | k_norm_weight = tl.load(k_norm_weight_ptr + dims).to(tl.float32) |
| 586 | rstd = tl.rsqrt(tl.sum(k_float * k_float, axis=1) / HEAD_DIM + eps) |
| 587 | k_normalized = (k_float * rstd[:, None] * k_norm_weight[None, :]).to( |
| 588 | qkvr_ptr.dtype.element_ty |
| 589 | ) |
| 590 | |
| 591 | safe_attention_slot = tl.maximum(attention_slot, 0) |
| 592 | attention_block = safe_attention_slot // attention_page_size |
| 593 | attention_offset = safe_attention_slot % attention_page_size |
| 594 | attention_mask = row_mask & (attention_slot >= 0) |
| 595 | tl.store( |
| 596 | key_cache_ptr |
| 597 | + attention_block[:, None] * stride_kc_block |
| 598 | + attention_offset[:, None] * stride_kc_token |
| 599 | + head[:, None] * stride_kc_head |
| 600 | + dims[None, :], |
| 601 | k_normalized, |
| 602 | mask=attention_mask[:, None], |
| 603 | ) |
| 604 | tl.store( |
| 605 | value_cache_ptr |
| 606 | + attention_block[:, None] * stride_vc_block |
| 607 | + attention_offset[:, None] * stride_vc_token |
| 608 | + head[:, None] * stride_vc_head |
| 609 | + dims[None, :], |
| 610 | v_rounded, |
| 611 | mask=attention_mask[:, None], |
| 612 | ) |
| 613 | |
| 614 | |
| 615 | def _run_tiled_q( |
| 616 | qkvr: torch.Tensor, |
| 617 | q_norm_weight: torch.Tensor, |
| 618 | q_out: torch.Tensor, |
| 619 | positions: torch.Tensor, |
| 620 | *, |
| 621 | eps: float, |
| 622 | num_q_heads: int, |
| 623 | head_dim: int, |
| 624 | log_scaling: torch.Tensor | None, |
| 625 | ) -> None: |
| 626 | num_rows = qkvr.shape[0] * num_q_heads |
| 627 | _q_kernel[(triton.cdiv(num_rows, Q_BLOCK_ROWS),)]( |
| 628 | qkvr, |
| 629 | q_norm_weight, |
| 630 | q_out, |
| 631 | log_scaling if log_scaling is not None else positions, |
| 632 | num_rows, |
| 633 | qkvr.stride(0), |
| 634 | eps, |
| 635 | NUM_Q_HEADS=num_q_heads, |
| 636 | HEAD_DIM=head_dim, |
| 637 | BLOCK_ROWS=Q_BLOCK_ROWS, |
| 638 | APPLY_LOG_SCALING=log_scaling is not None, |
| 639 | num_warps=Q_NUM_WARPS, |
| 640 | ) |
| 641 | |
| 642 | |
| 643 | def _run_tiled_kv( |
| 644 | qkvr: torch.Tensor, |
| 645 | k_weight: torch.Tensor, |
| 646 | v_weight: torch.Tensor, |
| 647 | k_norm_weight: torch.Tensor, |
| 648 | conv_cache: torch.Tensor, |
| 649 | key_cache: torch.Tensor, |
| 650 | value_cache: torch.Tensor, |
| 651 | positions: torch.Tensor, |
| 652 | seq_idx: torch.Tensor, |
| 653 | conv_slot_mapping: torch.Tensor, |
| 654 | conv_block_table: torch.Tensor, |
| 655 | query_start: torch.Tensor, |
| 656 | attention_slot_mapping: torch.Tensor, |
| 657 | *, |
| 658 | eps: float, |
| 659 | num_q_heads: int, |
| 660 | num_kv_heads: int, |
| 661 | head_dim: int, |
| 662 | off_k: int, |
| 663 | off_v: int, |
| 664 | conv_block_size: int, |
| 665 | ) -> None: |
| 666 | tokens = qkvr.shape[0] |
| 667 | _kv_kernel[(triton.cdiv(tokens, KV_BLOCK_ROWS), num_kv_heads)]( |
| 668 | qkvr, |
| 669 | k_weight, |
| 670 | v_weight, |
| 671 | k_norm_weight, |
| 672 | conv_cache, |
| 673 | key_cache, |
| 674 | value_cache, |
| 675 | positions, |
| 676 | seq_idx, |
| 677 | conv_slot_mapping, |
| 678 | conv_block_table, |
| 679 | query_start, |
| 680 | attention_slot_mapping, |
| 681 | tokens, |
| 682 | eps, |
| 683 | qkvr.stride(0), |
| 684 | conv_cache.stride(0), |
| 685 | conv_cache.stride(1), |
| 686 | conv_cache.stride(2), |
| 687 | conv_cache.stride(3), |
| 688 | key_cache.stride(0), |
| 689 | key_cache.stride(1), |
| 690 | key_cache.stride(2), |
| 691 | value_cache.stride(0), |
| 692 | value_cache.stride(1), |
| 693 | value_cache.stride(2), |
| 694 | conv_block_table.stride(0), |
| 695 | conv_block_table.shape[1], |
| 696 | conv_block_size, |
| 697 | key_cache.shape[1], |
| 698 | Q_WIDTH=num_q_heads * head_dim, |
| 699 | KV_WIDTH=num_kv_heads * head_dim, |
| 700 | NUM_KV_HEADS=num_kv_heads, |
| 701 | HEAD_DIM=head_dim, |
| 702 | WINDOW_SIZE=k_weight.shape[1], |
| 703 | OFF_K=off_k, |
| 704 | OFF_V=off_v, |
| 705 | BLOCK_ROWS=KV_BLOCK_ROWS, |
| 706 | num_warps=KV_NUM_WARPS, |
| 707 | ) |
| 708 | |
| 709 | |
| 710 | def _run_fused_small( |
| 711 | qkvr: torch.Tensor, |
| 712 | q_norm_weight: torch.Tensor, |
| 713 | q_out: torch.Tensor, |
| 714 | rel_proj: torch.Tensor, |
| 715 | rel_out: torch.Tensor, |
| 716 | k_weight: torch.Tensor, |
| 717 | v_weight: torch.Tensor, |
| 718 | k_norm_weight: torch.Tensor, |
| 719 | conv_cache: torch.Tensor, |
| 720 | key_cache: torch.Tensor, |
| 721 | value_cache: torch.Tensor, |
| 722 | positions: torch.Tensor, |
| 723 | seq_idx: torch.Tensor, |
| 724 | conv_slot_mapping: torch.Tensor, |
| 725 | conv_block_table: torch.Tensor, |
| 726 | query_start: torch.Tensor, |
| 727 | attention_slot_mapping: torch.Tensor, |
| 728 | *, |
| 729 | eps: float, |
| 730 | num_q_heads: int, |
| 731 | num_kv_heads: int, |
| 732 | head_dim: int, |
| 733 | off_k: int, |
| 734 | off_v: int, |
| 735 | conv_block_size: int, |
| 736 | log_scaling: torch.Tensor | None, |
| 737 | ) -> None: |
| 738 | tokens = qkvr.shape[0] |
| 739 | |
| 740 | num_q_rows = tokens * num_q_heads |
| 741 | grid = (num_q_rows + tokens * num_kv_heads,) |
| 742 | _qkvr_qkv_kernel[grid]( |
| 743 | qkvr, |
| 744 | q_norm_weight, |
| 745 | q_out, |
| 746 | rel_proj, |
| 747 | rel_out, |
| 748 | k_weight, |
| 749 | v_weight, |
| 750 | k_norm_weight, |
| 751 | conv_cache, |
| 752 | key_cache, |
| 753 | value_cache, |
| 754 | positions, |
| 755 | seq_idx, |
| 756 | conv_slot_mapping, |
| 757 | conv_block_table, |
| 758 | query_start, |
| 759 | attention_slot_mapping, |
| 760 | log_scaling if log_scaling is not None else positions, |
| 761 | tokens, |
| 762 | eps, |
| 763 | qkvr.stride(0), |
| 764 | conv_cache.stride(0), |
| 765 | conv_cache.stride(1), |
| 766 | conv_cache.stride(2), |
| 767 | conv_cache.stride(3), |
| 768 | key_cache.stride(0), |
| 769 | key_cache.stride(1), |
| 770 | key_cache.stride(2), |
| 771 | value_cache.stride(0), |
| 772 | value_cache.stride(1), |
| 773 | value_cache.stride(2), |
| 774 | conv_block_table.stride(0), |
| 775 | conv_block_table.shape[1], |
| 776 | conv_block_size, |
| 777 | key_cache.shape[1], |
| 778 | Q_WIDTH=num_q_heads * head_dim, |
| 779 | KV_WIDTH=num_kv_heads * head_dim, |
| 780 | NUM_Q_HEADS=num_q_heads, |
| 781 | NUM_KV_HEADS=num_kv_heads, |
| 782 | HEAD_DIM=head_dim, |
| 783 | WINDOW_SIZE=k_weight.shape[1], |
| 784 | OFF_K=off_k, |
| 785 | OFF_V=off_v, |
| 786 | APPLY_LOG_SCALING=log_scaling is not None, |
| 787 | D_REL=16, |
| 788 | REL_EXTENT=rel_proj.shape[1], |
| 789 | REL_EXTENT_PADDED=triton.next_power_of_2(rel_proj.shape[1]), |
| 790 | num_warps=SMALL_NUM_WARPS, |
| 791 | ) |
| 792 | |
| 793 | |
| 794 | def fused_qkvr_prep( |
| 795 | qkvr: torch.Tensor, |
| 796 | k_weight: torch.Tensor, |
| 797 | v_weight: torch.Tensor, |
| 798 | q_norm_weight: torch.Tensor, |
| 799 | k_norm_weight: torch.Tensor, |
| 800 | rel_proj: torch.Tensor, |
| 801 | eps: float, |
| 802 | num_q_heads: int, |
| 803 | num_kv_heads: int, |
| 804 | head_dim: int, |
| 805 | d_rel: int, |
| 806 | conv_cache: torch.Tensor, |
| 807 | key_cache: torch.Tensor, |
| 808 | value_cache: torch.Tensor, |
| 809 | positions: torch.Tensor, |
| 810 | conv_block_table: torch.Tensor, |
| 811 | seq_idx: torch.Tensor, |
| 812 | conv_slot_mapping: torch.Tensor, |
| 813 | query_start: torch.Tensor, |
| 814 | attention_slot_mapping: torch.Tensor, |
| 815 | off_k: int, |
| 816 | off_v: int, |
| 817 | conv_block_size: int, |
| 818 | log_scaling: torch.Tensor | None = None, |
| 819 | ) -> tuple[torch.Tensor, torch.Tensor]: |
| 820 | assert d_rel == 16 and rel_proj.shape[0] == 16 |
| 821 | assert head_dim == 128 |
| 822 | assert qkvr.is_contiguous() |
| 823 | assert k_weight.stride() == (k_weight.shape[1], 1) |
| 824 | assert v_weight.stride() == (v_weight.shape[1], 1) |
| 825 | assert rel_proj.stride() == (rel_proj.shape[1], 1) |
| 826 | assert conv_cache.stride(3) == 1 |
| 827 | assert key_cache.stride(3) == 1 and value_cache.stride(3) == 1 |
| 828 | tokens = qkvr.shape[0] |
| 829 | q_out = torch.empty( |
| 830 | (tokens, num_q_heads * head_dim), dtype=qkvr.dtype, device=qkvr.device |
| 831 | ) |
| 832 | rel_out = torch.empty( |
| 833 | (tokens, num_q_heads, rel_proj.shape[1]), |
| 834 | dtype=qkvr.dtype, |
| 835 | device=qkvr.device, |
| 836 | ) |
| 837 | if tokens == 0: |
| 838 | return q_out, rel_out |
| 839 | |
| 840 | if tokens < SMALL_TOKEN_THRESHOLD: |
| 841 | _run_fused_small( |
| 842 | qkvr, |
| 843 | q_norm_weight, |
| 844 | q_out, |
| 845 | rel_proj, |
| 846 | rel_out, |
| 847 | k_weight, |
| 848 | v_weight, |
| 849 | k_norm_weight, |
| 850 | conv_cache, |
| 851 | key_cache, |
| 852 | value_cache, |
| 853 | positions, |
| 854 | seq_idx, |
| 855 | conv_slot_mapping, |
| 856 | conv_block_table, |
| 857 | query_start, |
| 858 | attention_slot_mapping, |
| 859 | eps=eps, |
| 860 | num_q_heads=num_q_heads, |
| 861 | num_kv_heads=num_kv_heads, |
| 862 | head_dim=head_dim, |
| 863 | off_k=off_k, |
| 864 | off_v=off_v, |
| 865 | conv_block_size=conv_block_size, |
| 866 | log_scaling=log_scaling, |
| 867 | ) |
| 868 | return q_out, rel_out |
| 869 | |
| 870 | kv_stream = aux_stream() |
| 871 | assert kv_stream is not None |
| 872 | current_stream = torch.cuda.current_stream() |
| 873 | kv_stream.wait_stream(current_stream) |
| 874 | with torch.cuda.stream(kv_stream): |
| 875 | _run_tiled_kv( |
| 876 | qkvr, |
| 877 | k_weight, |
| 878 | v_weight, |
| 879 | k_norm_weight, |
| 880 | conv_cache, |
| 881 | key_cache, |
| 882 | value_cache, |
| 883 | positions, |
| 884 | seq_idx, |
| 885 | conv_slot_mapping, |
| 886 | conv_block_table, |
| 887 | query_start, |
| 888 | attention_slot_mapping, |
| 889 | eps=eps, |
| 890 | num_q_heads=num_q_heads, |
| 891 | num_kv_heads=num_kv_heads, |
| 892 | head_dim=head_dim, |
| 893 | off_k=off_k, |
| 894 | off_v=off_v, |
| 895 | conv_block_size=conv_block_size, |
| 896 | ) |
| 897 | _run_tiled_q( |
| 898 | qkvr, |
| 899 | q_norm_weight, |
| 900 | q_out, |
| 901 | positions, |
| 902 | eps=eps, |
| 903 | num_q_heads=num_q_heads, |
| 904 | head_dim=head_dim, |
| 905 | log_scaling=log_scaling, |
| 906 | ) |
| 907 | qkvr_rel_proj( |
| 908 | qkvr, |
| 909 | rel_proj, |
| 910 | rel_out, |
| 911 | log_scaling, |
| 912 | num_q_heads=num_q_heads, |
| 913 | num_kv_heads=num_kv_heads, |
| 914 | head_dim=head_dim, |
| 915 | d_rel=d_rel, |
| 916 | ) |
| 917 | current_stream.wait_stream(kv_stream) |
| 918 | return q_out, rel_out |