main
cu 3,849 lines 146 KB
Raw
1 /*
2 * Copyright (c) 2024, The vLLM 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 #include <torch/all.h>
18 #include <ATen/cuda/CUDAContext.h>
19 #include <c10/cuda/CUDAGuard.h>
20 #include <hip/hip_fp8.h>
21 #include <hip/hip_bf16.h>
22 #include "../cuda_compat.h"
23
24 #include <algorithm>
25 #include "../attention/dtype_fp8.cuh"
26 #include "../quantization/w8a8/fp8/amd/quant_utils.cuh"
27
28 // ROCm 6.2 compatibility: map OCP fp8 types to FNUZ variants if OCP is absent
29 #if !defined(HIP_FP8_TYPE_OCP)
30 using __hip_fp8_e4m3 = __hip_fp8_e4m3_fnuz;
31 using __hip_fp8_e5m2 = __hip_fp8_e5m2_fnuz;
32 #endif
33
34 #if defined(__HIPCC__) && \
35 (defined(__gfx90a__) || defined(__gfx942__) || defined(__gfx950__))
36 #define __HIP__GFX9__
37 #endif
38
39 #if defined(__HIPCC__) && (defined(__gfx942__) || defined(__gfx950__))
40 #define __HIP__FP8MFMA__
41 #endif
42
43 #if defined(NDEBUG)
44 #undef NDEBUG
45 #include <assert.h>
46 #define UNREACHABLE_CODE assert(false);
47 #define NDEBUG
48 #else
49 #define UNREACHABLE_CODE assert(false);
50 #endif
51
52 #define MAX(a, b) ((a) > (b) ? (a) : (b))
53 #define MIN(a, b) ((a) < (b) ? (a) : (b))
54 #define DIVIDE_ROUND_UP(a, b) (((a) + (b) - 1) / (b))
55
56 enum class MFMAType {
57 F16 = 0,
58 Fp8 = 1,
59 Fp4 = 2,
60 };
61
62 // Score masking is insufficient for unwritten V padding: 0 * NaN is NaN.
63 template <typename cache_t, typename Vec>
64 __device__ __forceinline__ Vec mask_v_cache_padding(Vec values,
65 const int first_token_idx,
66 const int seq_len) {
67 constexpr int NUM_ELEMS = sizeof(Vec) / sizeof(cache_t);
68 if (first_token_idx + NUM_ELEMS <= seq_len) {
69 return values;
70 }
71 if (first_token_idx >= seq_len) {
72 return Vec{};
73 }
74 constexpr int WORD_BYTES = sizeof(uint64_t);
75 constexpr int NUM_WORDS = sizeof(Vec) / WORD_BYTES;
76 static_assert(sizeof(Vec) % WORD_BYTES == 0);
77 const int valid_bytes = (seq_len - first_token_idx) * sizeof(cache_t);
78 uint64_t* words = reinterpret_cast<uint64_t*>(&values);
79 #pragma unroll
80 for (int i = 0; i < NUM_WORDS; i++) {
81 const int word_valid_bytes = valid_bytes - i * WORD_BYTES;
82 const uint64_t word_mask =
83 word_valid_bytes >= WORD_BYTES
84 ? UINT64_MAX
85 : (word_valid_bytes <= 0
86 ? 0
87 : (uint64_t{1} << (word_valid_bytes * 8)) - 1);
88 words[i] &= word_mask;
89 }
90 return values;
91 }
92
93 #if defined(__HIP__GFX9__)
94
95 #define GCN_MFMA_INSTR1 __builtin_amdgcn_mfma_f32_16x16x4f32
96 #define GCN_MFMA_INSTR __builtin_amdgcn_mfma_f32_4x4x4f16
97
98 using floatx4 = __attribute__((__vector_size__(4 * sizeof(float)))) float;
99 using float16x4 =
100 __attribute__((__vector_size__(4 * sizeof(_Float16)))) _Float16;
101 typedef float16x4 _Half4;
102 using float16x2 =
103 __attribute__((__vector_size__(2 * sizeof(_Float16)))) _Float16;
104 typedef float16x2 _Half2;
105 typedef struct _Half8 {
106 _Half4 xy[2];
107 } _Half8;
108
109 using bit16_t = uint16_t;
110 using bit16x4 = __attribute__((__vector_size__(4 * sizeof(uint16_t)))) uint16_t;
111 typedef bit16x4 _B16x4;
112 typedef struct _B16x8 {
113 _B16x4 xy[2];
114 } _B16x8;
115
116 __device__ __forceinline__ _B16x8
117 mask_16b_v_cache_boundary(_B16x8 values, const int valid_bytes) {
118 // The caller guarantees 0 < valid_bytes < 16.
119 uint64_t* words = reinterpret_cast<uint64_t*>(&values);
120 if (valid_bytes <= 8) {
121 const uint64_t low_mask =
122 valid_bytes == 8 ? UINT64_MAX : (uint64_t{1} << (valid_bytes * 8)) - 1;
123 words[0] &= low_mask;
124 words[1] = 0;
125 } else {
126 const uint64_t high_mask = (uint64_t{1} << ((valid_bytes - 8) * 8)) - 1;
127 words[1] &= high_mask;
128 }
129 return values;
130 }
131
132 __device__ __forceinline__ uint2
133 mask_8b_v_cache_boundary(uint2 values, const int valid_bytes) {
134 // The caller guarantees 0 < valid_bytes < 8.
135 uint64_t* word = reinterpret_cast<uint64_t*>(&values);
136 *word &= (uint64_t{1} << (valid_bytes * 8)) - 1;
137 return values;
138 }
139
140 using _B8x8 = uint2;
141 using _B8x4 = int32_t; // used in builtins
142 using bit8_t = uint8_t;
143
144 typedef struct _B8x16 {
145 _B8x8 xy[2];
146 } _B8x16;
147
148 template <typename T, int absz, int cbid, int blgp>
149 __device__ __forceinline__ floatx4 gcn_mfma4x4x4_instr(const _B16x4& inpA,
150 const _B16x4& inpB,
151 const floatx4& inpC) {
152 if constexpr (std::is_same<T, _Float16>::value) {
153 return __builtin_amdgcn_mfma_f32_4x4x4f16(inpA, inpB, inpC, absz, cbid,
154 blgp);
155 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
156 return __builtin_amdgcn_mfma_f32_4x4x4bf16_1k(inpA, inpB, inpC, absz, cbid,
157 blgp);
158 } else {
159 static_assert(false, "unsupported 16b dtype");
160 }
161 }
162
163 template <typename T, int absz, int cbid, int blgp>
164 __device__ __forceinline__ floatx4 gcn_mfma16x16x16_instr(const _B16x4& inpA,
165 const _B16x4& inpB,
166 const floatx4& inpC) {
167 if constexpr (std::is_same<T, _Float16>::value) {
168 return __builtin_amdgcn_mfma_f32_16x16x16f16(inpA, inpB, inpC, absz, cbid,
169 blgp);
170 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
171 return __builtin_amdgcn_mfma_f32_16x16x16bf16_1k(inpA, inpB, inpC, absz,
172 cbid, blgp);
173 } else {
174 static_assert(false, "unsupported 16b dtype");
175 }
176 }
177
178 template <typename T, int absz, int cbid, int blgp>
179 __device__ __forceinline__ floatx4 gcn_mfma16x16x32_instr(const long& inpA,
180 const long& inpB,
181 const floatx4& inpC) {
182 if constexpr (std::is_same<T, __hip_fp8_e4m3>::value) {
183 return __builtin_amdgcn_mfma_f32_16x16x32_fp8_fp8(inpA, inpB, inpC, absz,
184 cbid, blgp);
185 } else if constexpr (std::is_same<T, __hip_fp8_e5m2>::value) {
186 return __builtin_amdgcn_mfma_f32_16x16x32_bf8_bf8(inpA, inpB, inpC, absz,
187 cbid, blgp);
188 } else {
189 static_assert(false, "unsupported 8b dtype");
190 }
191 }
192
193 template <typename T>
194 __device__ __forceinline__ float to_float(const T& inp) {
195 if constexpr (std::is_same<T, _Float16>::value) {
196 return (float)inp;
197 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
198 return __bfloat162float(inp);
199 } else {
200 static_assert(false, "unsupported 16b dtype");
201 }
202 }
203
204 template <typename T>
205 __device__ __forceinline__ T from_float(const float& inp) {
206 if constexpr (std::is_same<T, _Float16>::value) {
207 return (_Float16)inp;
208 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
209 return __float2bfloat16(inp);
210 } else {
211 static_assert(false, "unsupported 16b dtype");
212 }
213 }
214
215 template <typename T>
216 __device__ __forceinline__ _B16x4 from_floatx4(const floatx4& inp) {
217 _B16x4 ret;
218 if constexpr (std::is_same<T, _Float16>::value) {
219 union h2cvt {
220 __half2 h2[2];
221 _B16x4 b16x4;
222 } u;
223 u.h2[0] = __float22half2_rn(make_float2(inp[0], inp[1]));
224 u.h2[1] = __float22half2_rn(make_float2(inp[2], inp[3]));
225 return u.b16x4;
226 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
227 for (int i = 0; i < 4; i++) {
228 union fcvt {
229 uint32_t u32;
230 float f32;
231 } u;
232 u.f32 = inp[i];
233 u.u32 += 0x7fff + ((u.u32 >> 16) & 1); // BF16 RNE with no nan/inf check
234 ret[i] = uint16_t(u.u32 >> 16);
235 }
236 return ret;
237 } else {
238 static_assert(false, "unsupported 16b dtype");
239 }
240 }
241
242 template <typename T>
243 __device__ __forceinline__ _B16x4 addx4(const _B16x4& inp1,
244 const _B16x4& inp2) {
245 _B16x4 ret;
246 if constexpr (std::is_same<T, _Float16>::value) {
247 union h2cvt {
248 _B16x4 b16x4;
249 __half2 h2[2];
250 } u1, u2, s;
251 u1.b16x4 = inp1;
252 u2.b16x4 = inp2;
253 s.h2[0] = u1.h2[0] + u2.h2[0];
254 s.h2[1] = u1.h2[1] + u2.h2[1];
255 return s.b16x4;
256 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
257 for (int i = 0; i < 4; i++) {
258 union fcvt {
259 float f32;
260 uint32_t i32;
261 } u1, u2, s;
262 u1.i32 = uint32_t(inp1[i]) << 16;
263 u2.i32 = uint32_t(inp2[i]) << 16;
264 s.f32 = u1.f32 + u2.f32;
265 ret[i] = uint16_t(s.i32 >> 16);
266 }
267 return ret;
268 } else {
269 static_assert(false, "unsupported 16b dtype");
270 }
271 }
272
273 __device__ __forceinline__ floatx4 to_float_fp8x4(const _B8x4& inp) {
274 // From MI300+ platforms, we have v_cvt_pk_f32_fp8 instruction
275 // to convert 2 packed fp8 to 2 packed fp32 values.
276 // However, in MI200 platforms, we only have v_cvt_f32_fp8
277 // to convert fp8 values individually. So we added
278 // #else case for fewer instructions (# inst=2) in MI300+,
279 // and fallback to
280 // #if case for other platforms (# inst=4).
281 #if defined(__gfx90a__)
282 float4 f32x4 = vllm::fp8::vec_conversion<float4, uint32_t>(
283 *reinterpret_cast<const uint32_t*>(&inp));
284 return *reinterpret_cast<floatx4*>(&f32x4);
285 #else // MI3xx+ optimized builtins
286 const auto f0 = __builtin_amdgcn_cvt_pk_f32_fp8(inp, false);
287 const auto f1 = __builtin_amdgcn_cvt_pk_f32_fp8(inp, true);
288 floatx4 ret;
289 ret[0] = f0[0];
290 ret[1] = f0[1];
291 ret[2] = f1[0];
292 ret[3] = f1[1];
293 return ret;
294 #endif
295 }
296
297 template <typename T>
298 __device__ __forceinline__ _B16x4 from_floatx4_rtz(const floatx4& inp) {
299 _B16x4 ret;
300 if constexpr (std::is_same<T, _Float16>::value) {
301 union h2cvt {
302 _Half2 h2[2];
303 _B16x4 b16x4;
304 } u;
305 u.h2[0] = __builtin_amdgcn_cvt_pkrtz(inp[0], inp[1]);
306 u.h2[1] = __builtin_amdgcn_cvt_pkrtz(inp[2], inp[3]);
307 return u.b16x4;
308 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
309 for (int i = 0; i < 4; i++) {
310 union fcvt {
311 uint32_t i32;
312 float f32;
313 } u;
314 u.f32 = inp[i];
315 ret[i] = uint16_t(u.i32 >> 16);
316 }
317 return ret;
318 } else {
319 static_assert(false, "unsupported 16b dtype");
320 }
321 }
322
323 template <typename T>
324 __device__ __forceinline__ _B16x8 convert_b8x8_custom(const _B8x8 input) {
325 union {
326 _B8x8 b8x8;
327 _B8x4 b8x4[2];
328 } tmp;
329 tmp.b8x8 = input;
330 _B16x8 ret;
331 for (int i = 0; i < 2; i++) {
332 ret.xy[i] = from_floatx4_rtz<T>(to_float_fp8x4(tmp.b8x4[i]));
333 }
334 return ret;
335 }
336
337 typedef union u64_cvt {
338 half f16x4[4];
339 int16_t b16x4[4];
340 _B8x8 b8x8;
341 _B16x4 b64;
342 int64_t i64;
343 } _T8x8;
344
345 __device__ float warpReduceMax(float val) {
346 for (int offset = warpSize / 2; offset > 0; offset /= 2) {
347 val = max(
348 val, __shfl_down(val, offset, WARP_SIZE)); // Using max() for reduction
349 }
350 return val;
351 }
352
353 // grid (num_seqs, num_partitions,num_kv_heads)
354 // block (256)
355 // clang-format off
356 template <typename scalar_t, typename cache_t,
357 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
358 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED, int GQA_RATIO, MFMAType MFMA_TYPE>
359 __global__
360 __launch_bounds__(NUM_THREADS, 4) void paged_attention_ll4mi_QKV_mfma16_kernel(
361 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
362 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
363 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
364 const int num_kv_heads,
365 const float scale,
366 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
367 const int* __restrict__ seq_lens, // [num_seqs]
368 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
369 const int max_num_blocks_per_seq,
370 const float* __restrict__ alibi_slopes, // [num_heads]
371 const int q_stride,
372 const int kv_block_stride,
373 const int kv_head_stride,
374 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
375 float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
376 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions, head_size]
377 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
378 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
379 // clang-format on
380 constexpr int NWARPS = NUM_THREADS / WARP_SIZE;
381 const auto warpid = threadIdx.x / WARP_SIZE;
382 const auto laneid = threadIdx.x % WARP_SIZE;
383 const int lane4id = laneid % 4;
384 const int lane16id = laneid % 16;
385 const int rowid = laneid / 16;
386
387 const auto seq_idx = blockIdx.x;
388 // NOTE queries with sequence len > 1 are prefills and taken care by another
389 // kernel.
390 if (query_start_loc_ptr != nullptr &&
391 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx]) != 1) {
392 return;
393 }
394
395 const auto partition_idx = blockIdx.y;
396
397 constexpr int T_PAR_SIZE = 256; // token partition size set to 256
398
399 const auto max_num_partitions = gridDim.y;
400
401 const int seq_len = seq_lens[seq_idx];
402
403 const int partition_start_token_idx =
404 partition_idx * T_PAR_SIZE; // partition_size;
405 // exit if partition is out of context for seq
406 if (partition_start_token_idx >= seq_len) {
407 return;
408 }
409
410 constexpr int GQA_RATIO4 = DIVIDE_ROUND_UP(GQA_RATIO, 4);
411
412 // shared_logits is used for multiple purposes
413 __shared__ _B16x4 shared_logits[NWARPS][4][16][4];
414
415 // for QK mfma16x16, layout is QHead/Tokenx16 across every 16 lanes, 16 Bytes
416 // HeadElements in each lane, 4x16B HeadElements across 4 rows of warp
417 constexpr int ROWS_PER_WARP =
418 WARP_SIZE / 16; // rows refers to 16 lanes; refer DDP (Data Parallel
419 // Processing) terminology
420 constexpr int CONTIGUOUS_KV_ELEMS_16B_LOAD =
421 16 / sizeof(cache_t); // 8 for 16 bit cache type, 16 for 8 bit types
422 constexpr int QKHE_PER_FETCH =
423 CONTIGUOUS_KV_ELEMS_16B_LOAD *
424 ROWS_PER_WARP; // each fetch across a warp fetches these many elements
425 constexpr int QK_SIZE_RATIO =
426 sizeof(scalar_t) /
427 sizeof(cache_t); // 1 for 16bit types, 2 for 8bit types
428 constexpr int QKHELOOP = HEAD_SIZE / QKHE_PER_FETCH; // 4xQKHE_16B across
429 // warp
430
431 _B16x8 Qlocal[QKHELOOP]
432 [QK_SIZE_RATIO]; // note that 16 contiguous elements of Q should
433 // be fetched per lane for 8 bit cache types :
434 // QK_SIZE_RATIO changes for this
435
436 constexpr int CONTIGUOUS_SCALAR_ELEMS_16B = 16 / sizeof(scalar_t);
437
438 constexpr int TOKENS_PER_WARP =
439 T_PAR_SIZE /
440 NWARPS; // sub partition of tokens per warp for qk calculation
441 constexpr int TLOOP =
442 TOKENS_PER_WARP /
443 16; // each mfma16x16x16 instruction processes 16 tokens
444
445 // can be interpreted as B8x16 for 8 bit types
446 _B16x8 Klocal[TLOOP][QKHELOOP];
447
448 const auto wg_start_head_idx = blockIdx.z * GQA_RATIO;
449 const auto wg_start_kv_head_idx = blockIdx.z;
450 const auto total_num_heads = gridDim.z * GQA_RATIO;
451
452 // for QK mfma, tokens in multiples of TOKENS_PER_WARP are spread across warps
453 // each mfma takes QH16xT16x16HE across warp
454 // repeat mfmas across QKHELOOP dimension
455 // output layout from QKmfma : QH16xT4x4 16 qheads across 16 lanes, 16 tokens
456 // across 4 rows x 4 tokens per lane
457
458 const int num_seq_blocks = DIVIDE_ROUND_UP(seq_len, BLOCK_SIZE);
459 const int last_seq_block = num_seq_blocks - 1;
460
461 const int* block_table_seq = block_tables + seq_idx * max_num_blocks_per_seq;
462
463 int kphysical_block_number[TLOOP];
464 #if defined(__HIP__FP8MFMA__)
465 float q_max = 0;
466 float q_scale = 1.0;
467 #endif
468
469 // fetch k physical block numbers
470 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
471 const int klocal_token_idx =
472 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
473 const int kglobal_token_idx = partition_start_token_idx + klocal_token_idx;
474 const int kblock_idx = (kglobal_token_idx < seq_len)
475 ? kglobal_token_idx / BLOCK_SIZE
476 : last_seq_block;
477 kphysical_block_number[token_depth] = block_table_seq[kblock_idx];
478 }
479
480 // fetch Q in shared across warps and then write to registers
481 const int local_qhead_idx = 4 * warpid + rowid;
482 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
483 const int64_t query_start_off = static_cast<int64_t>(
484 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
485 const scalar_t* q_ptr =
486 q + query_start_off * q_stride + global_qhead_idx * HEAD_SIZE;
487
488 const int qhead_element = lane16id * CONTIGUOUS_SCALAR_ELEMS_16B;
489 if ((local_qhead_idx < GQA_RATIO) && (qhead_element < HEAD_SIZE)) {
490 const scalar_t* q_fetch_ptr = q_ptr + qhead_element;
491 const _B16x8* q_fetch_ptr_16B =
492 reinterpret_cast<const _B16x8*>(q_fetch_ptr);
493 _B16x8 tmp = *q_fetch_ptr_16B;
494 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) {
495 const int offset1 =
496 lane16id /
497 4; // 16 contiguous chunks of head elems are spread across 4x4lanes
498 shared_logits[offset1][lane4id][local_qhead_idx][0] = tmp.xy[0];
499 shared_logits[offset1][lane4id][local_qhead_idx][1] = tmp.xy[1];
500 } else {
501 for (int i = 0; i < 2; i++) {
502 const int head_elem = lane16id * 2 + i; // element id in _B16x4 terms
503 const int offset3 = head_elem % 4;
504 const int offset2 = (head_elem / 4) % 4;
505 const int offset1 = head_elem / 4 / 4;
506 shared_logits[offset1][offset2][local_qhead_idx][offset3] = tmp.xy[i];
507 }
508 }
509 }
510 __syncthreads();
511 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
512 for (int qkratio = 0; qkratio < QK_SIZE_RATIO; qkratio++) {
513 for (int i = 0; i < 2; i++) {
514 Qlocal[qkhe_depth][qkratio].xy[i] =
515 shared_logits[qkhe_depth][rowid][lane16id % GQA_RATIO]
516 [2 * qkratio + i];
517 #if defined(__HIP__FP8MFMA__)
518 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto &&
519 MFMA_TYPE == MFMAType::Fp8) {
520 scalar_t* qptr =
521 reinterpret_cast<scalar_t*>(&Qlocal[qkhe_depth][qkratio].xy[i]);
522 for (int k = 0; k < 4; k++)
523 q_max = fmax(fabs(to_float<scalar_t>(qptr[k])), q_max);
524 }
525 #endif
526 }
527 }
528 }
529
530 constexpr int KX =
531 16 / sizeof(cache_t); // vLLM defines x as 16 Bytes of kv cache elements
532 const cache_t* k_ptr = k_cache + wg_start_kv_head_idx * kv_head_stride;
533
534 const int row_head_elem = rowid * CONTIGUOUS_KV_ELEMS_16B_LOAD;
535 // fetch K values
536 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
537 const int64_t kblock_number =
538 static_cast<int64_t>(kphysical_block_number[token_depth]);
539 const cache_t* k_ptr2 = k_ptr + kblock_number * kv_block_stride;
540 const int klocal_token_idx =
541 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
542 const int kphysical_block_offset = klocal_token_idx % BLOCK_SIZE;
543 const cache_t* k_ptr3 = k_ptr2 + kphysical_block_offset * KX;
544
545 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
546 const int head_elem = row_head_elem + qkhe_depth * QKHE_PER_FETCH;
547 const int offset1 = head_elem / KX;
548 const int offset2 = head_elem % KX;
549 const cache_t* k_fetch_ptr = k_ptr3 + offset1 * BLOCK_SIZE * KX + offset2;
550 const _B16x8* k_fetch_ptr_16B =
551 reinterpret_cast<const _B16x8*>(k_fetch_ptr);
552 Klocal[token_depth][qkhe_depth] = *k_fetch_ptr_16B;
553 }
554 }
555
556 float alibi_slope;
557 if constexpr (ALIBI_ENABLED) {
558 const int alibi_head_idx = wg_start_head_idx + lane16id;
559 alibi_slope = (lane16id < GQA_RATIO) ? alibi_slopes[alibi_head_idx] : 0.f;
560 }
561
562 constexpr int VTOKENS_PER_LANE =
563 TOKENS_PER_WARP / ROWS_PER_WARP; // 64/4 = 16 contiguous vtokens per lane
564 constexpr int VBLOCKS_PER_LANE =
565 1; // assumes block size >=16, each lane can correspond to 1 block only
566 constexpr int VTLOOP = NWARPS; // corresponds to tokens across warps
567 constexpr int VTLANELOOP = DIVIDE_ROUND_UP(
568 VTOKENS_PER_LANE,
569 CONTIGUOUS_KV_ELEMS_16B_LOAD); // optimized for 16B fetches; assumes
570 // minimum block size is 16
571 constexpr int VHELOOP = HEAD_SIZE / 16 / NWARPS;
572
573 int vphysical_block_number[VTLOOP][VBLOCKS_PER_LANE];
574
575 // fetch v physical block numbers
576 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
577 for (int vblock_depth = 0; vblock_depth < VBLOCKS_PER_LANE;
578 vblock_depth++) {
579 const int vlocal_token_idx =
580 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
581 rowid * VTOKENS_PER_LANE + vblock_depth * BLOCK_SIZE;
582 // Safe to use an int32_t here assuming we are working with < 2 billion
583 // tokens
584 const int vglobal_token_idx =
585 partition_start_token_idx + vlocal_token_idx;
586 const int vblock_idx = (vglobal_token_idx < seq_len)
587 ? vglobal_token_idx / BLOCK_SIZE
588 : last_seq_block;
589 vphysical_block_number[vtoken_depth][vblock_depth] =
590 block_table_seq[vblock_idx];
591 }
592 }
593
594 _B16x8 Vlocal[VTLOOP][VHELOOP][VTLANELOOP]; // this could be B8x16 too
595
596 const cache_t* v_ptr = v_cache + wg_start_kv_head_idx * kv_head_stride +
597 ((rowid * VTOKENS_PER_LANE) % BLOCK_SIZE);
598
599 // v fetches are 16head elems across lanes x 16 tokens per lane
600 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
601 const int vhead_elem = vhe_depth * NWARPS * 16 + warpid * 16 + lane16id;
602 const cache_t* v_ptr2 = v_ptr + vhead_elem * BLOCK_SIZE;
603
604 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
605 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
606 const int vblock_depth = 0;
607 const int64_t vblock_number = static_cast<int64_t>(
608 vphysical_block_number[vtoken_depth][vblock_depth]);
609 const cache_t* v_ptr3 = v_ptr2 + (vblock_number * kv_block_stride);
610
611 const cache_t* v_fetch_ptr =
612 v_ptr3 + vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
613 const _B16x8* v_fetch_ptr_16B =
614 reinterpret_cast<const _B16x8*>(v_fetch_ptr);
615 Vlocal[vtoken_depth][vhe_depth][vfetch_depth] = *v_fetch_ptr_16B;
616 }
617 }
618 }
619
620 // calculate post qk mfma scale
621 float scale2 = scale;
622 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto) {
623 // multiply by k_scale if fp8 kv cache
624 scale2 *= *k_scale;
625 #if defined(__HIP__FP8MFMA__)
626 q_max = warpReduceMax(q_max);
627 constexpr float FP8_E4M3_SCALE_TARGET = 224.0f;
628 if constexpr (MFMA_TYPE == MFMAType::Fp8) {
629 q_scale = q_max > 0 ? FP8_E4M3_SCALE_TARGET / q_max : 1.0f;
630 scale2 /= q_scale;
631 }
632 #endif
633 }
634
635 floatx4 d_out[TLOOP];
636 // qk mfma
637 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
638 d_out[token_depth] = {0};
639 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
640 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) {
641 for (int qkratio = 0; qkratio < QK_SIZE_RATIO; qkratio++) {
642 for (int i = 0; i < 2; i++) {
643 d_out[token_depth] = gcn_mfma16x16x16_instr<scalar_t, 0, 0, 0>(
644 Klocal[token_depth][qkhe_depth].xy[i],
645 Qlocal[qkhe_depth][qkratio].xy[i], d_out[token_depth]);
646 }
647 }
648 } else { // kv cache dtype fp8
649 auto Ktmp = Klocal[token_depth][qkhe_depth];
650 _B8x16 Ktmp8x16 = *reinterpret_cast<_B8x16*>(&Ktmp);
651 for (int qkratio = 0; qkratio < QK_SIZE_RATIO; qkratio++) {
652 if constexpr (MFMA_TYPE == MFMAType::F16) {
653 _B8x8 Ktmp8x8 = Ktmp8x16.xy[qkratio];
654 _B16x8 Klocaltmp = convert_b8x8_custom<scalar_t>(Ktmp8x8);
655 for (int i = 0; i < 2; i++) {
656 d_out[token_depth] = gcn_mfma16x16x16_instr<scalar_t, 0, 0, 0>(
657 Klocaltmp.xy[i], Qlocal[qkhe_depth][qkratio].xy[i],
658 d_out[token_depth]);
659 }
660 } else {
661 #if defined(__HIP__FP8MFMA__)
662 _T8x8 Ktmp8x8, Qtmp8x8;
663 Ktmp8x8.b8x8 = Ktmp8x16.xy[qkratio];
664
665 for (int n = 0; n < 2; n++) {
666 scalar_t* qptr = reinterpret_cast<scalar_t*>(
667 &Qlocal[qkhe_depth][qkratio].xy[n]);
668
669 Qtmp8x8.b16x4[n * 2] =
670 vllm::fp8::scaled_vec_conversion<uint16_t, float2>(
671 make_float2(to_float<scalar_t>(qptr[0]),
672 to_float<scalar_t>(qptr[1])),
673 q_scale);
674 Qtmp8x8.b16x4[n * 2 + 1] =
675 vllm::fp8::scaled_vec_conversion<uint16_t, float2>(
676 make_float2(to_float<scalar_t>(qptr[2]),
677 to_float<scalar_t>(qptr[3])),
678 q_scale);
679 }
680
681 d_out[token_depth] =
682 gcn_mfma16x16x32_instr<__hip_fp8_e4m3, 0, 0, 0>(
683 Ktmp8x8.i64, Qtmp8x8.i64, d_out[token_depth]);
684 #else
685 UNREACHABLE_CODE
686 #endif
687 }
688 }
689 }
690 }
691 d_out[token_depth] *= scale2;
692 }
693
694 const int qkout_token_idx =
695 partition_start_token_idx + TOKENS_PER_WARP * warpid + rowid * 4;
696
697 // apply alibi
698 if constexpr (ALIBI_ENABLED) {
699 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
700 const int local_token_idx = qkout_token_idx + token_depth * 16;
701 const int alibi_offset = local_token_idx - seq_len + 1;
702 for (int i = 0; i < 4; i++) {
703 d_out[token_depth][i] += alibi_slope * (alibi_offset + i);
704 }
705 }
706 }
707
708 // calculate qk_max and exp_sum per warp and write to shared memory
709 float qk_max = -FLT_MAX;
710 float exp_sum = 0.0f;
711
712 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
713 const int local_token_idx = qkout_token_idx + token_depth * 16;
714 for (int i = 0; i < 4; i++) {
715 const float tmp =
716 (local_token_idx + i < seq_len) ? d_out[token_depth][i] : -FLT_MAX;
717 qk_max = fmaxf(qk_max, tmp);
718 }
719 }
720
721 for (int mask = WARP_SIZE / 2; mask >= 16; mask /= 2) {
722 qk_max = fmaxf(qk_max, __shfl_xor(qk_max, mask));
723 }
724
725 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
726 const int local_token_idx = qkout_token_idx + token_depth * 16;
727 for (int i = 0; i < 4; i++) {
728 const float tmp = (local_token_idx + i < seq_len)
729 ? __expf(d_out[token_depth][i] - qk_max)
730 : 0.0f;
731 d_out[token_depth][i] = tmp;
732 exp_sum += tmp;
733 }
734 }
735
736 for (int mask = WARP_SIZE / 2; mask >= 16; mask /= 2) {
737 exp_sum += __shfl_xor(exp_sum, mask);
738 }
739
740 __syncthreads(); // sync before writing to shared mem
741
742 float* shared_mem = reinterpret_cast<float*>(shared_logits);
743 if (laneid < 16) {
744 const int qk_max_offset = warpid * 16 + lane16id;
745 shared_mem[qk_max_offset] = qk_max;
746 const int exp_sum_offset = NWARPS * 16 + qk_max_offset;
747 shared_mem[exp_sum_offset] = exp_sum;
748 }
749
750 __syncthreads();
751
752 // calculate partition qk_max and exp_sum
753 float partition_qk_max = -FLT_MAX;
754 float warp_qk_max_exp[NWARPS];
755 float partition_exp_sum = 0.0f;
756
757 for (int w = 0; w < NWARPS; w++) {
758 warp_qk_max_exp[w] = shared_mem[w * 16 + lane16id];
759 partition_qk_max = fmaxf(partition_qk_max, warp_qk_max_exp[w]);
760 }
761
762 for (int w = 0; w < NWARPS; w++) {
763 warp_qk_max_exp[w] = __expf(warp_qk_max_exp[w] - partition_qk_max);
764 partition_exp_sum +=
765 shared_mem[NWARPS * 16 + w * 16 + lane16id] * warp_qk_max_exp[w];
766 }
767
768 const float inv_sum_scale =
769 __fdividef(1.f, partition_exp_sum + 1e-6f) * warp_qk_max_exp[warpid];
770
771 __syncthreads();
772
773 // disable rtz conversion due to its impact on accuracy.
774 constexpr bool LOGITS_RTZ_CONVERSION = false;
775
776 #if defined(__HIP__FP8MFMA__)
777 int rowid_8x8 = rowid / 2;
778 int offset = rowid % 2;
779 #endif
780
781 // write logits to shared mem
782 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
783 d_out[token_depth] *= inv_sum_scale;
784 if constexpr (MFMA_TYPE != MFMAType::Fp8) {
785 if constexpr (LOGITS_RTZ_CONVERSION) {
786 // use rtz conversion for better performance, with negligible impact on
787 // accuracy
788 shared_logits[warpid][token_depth][lane16id][rowid] =
789 from_floatx4_rtz<scalar_t>(d_out[token_depth]);
790 } else {
791 shared_logits[warpid][token_depth][lane16id][rowid] =
792 from_floatx4<scalar_t>(d_out[token_depth]);
793 }
794 } else {
795 #if defined(__HIP__FP8MFMA__)
796 // cast _B16x4* to _B8x8*
797 _T8x8& logits_8x8 = *reinterpret_cast<_T8x8*>(
798 &shared_logits[warpid][token_depth][lane16id][rowid_8x8]);
799 logits_8x8.b16x4[offset * 2] = __builtin_amdgcn_cvt_pk_fp8_f32(
800 d_out[token_depth][0], d_out[token_depth][1], 0, false);
801 logits_8x8.b16x4[offset * 2 + 1] = __builtin_amdgcn_cvt_pk_fp8_f32(
802 d_out[token_depth][2], d_out[token_depth][3], 0, false);
803 #else
804 UNREACHABLE_CODE
805 #endif
806 }
807 }
808
809 // write out partition max_logits and exp_sum
810 if (threadIdx.x < GQA_RATIO) {
811 const int qhead_idx = lane16id;
812 const int64_t offset = static_cast<int64_t>(seq_idx) *
813 static_cast<int64_t>(total_num_heads) *
814 static_cast<int64_t>(max_num_partitions) +
815 (static_cast<int64_t>(wg_start_head_idx) +
816 static_cast<int64_t>(qhead_idx)) *
817 static_cast<int64_t>(max_num_partitions) +
818 static_cast<int64_t>(partition_idx);
819 max_logits[offset] = partition_qk_max;
820 exp_sums[offset] = partition_exp_sum;
821 }
822
823 __syncthreads();
824
825 constexpr int ELEMS8_ELEMS4_RATIO = 8 / 4;
826 constexpr int ELEMS16_ELEMS8_RATIO = 16 / 8;
827
828 _B16x4 outelems[VHELOOP];
829 // Softmax V mfma
830 // v layout: 16he across lanes x 16 tokens per lane
831 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) {
832 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
833 floatx4 tmp_out = {0};
834 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
835 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
836 if (__builtin_expect(partition_start_token_idx + T_PAR_SIZE > seq_len,
837 0)) {
838 const int vglobal_fetch_start =
839 partition_start_token_idx +
840 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
841 rowid * VTOKENS_PER_LANE +
842 vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
843 if (vglobal_fetch_start >= seq_len) {
844 continue;
845 }
846 if (vglobal_fetch_start + CONTIGUOUS_KV_ELEMS_16B_LOAD > seq_len) {
847 Vlocal[vtoken_depth][vhe_depth][vfetch_depth] =
848 mask_16b_v_cache_boundary(
849 Vlocal[vtoken_depth][vhe_depth][vfetch_depth],
850 (seq_len - vglobal_fetch_start) * sizeof(cache_t));
851 }
852 }
853 for (int i = 0; i < ELEMS8_ELEMS4_RATIO; i++) {
854 const int offset = rowid * VTLANELOOP * ELEMS8_ELEMS4_RATIO +
855 vfetch_depth * ELEMS8_ELEMS4_RATIO + i;
856 const int offset1 = offset % ROWS_PER_WARP;
857 const int offset2 = offset / ROWS_PER_WARP;
858 tmp_out = gcn_mfma16x16x16_instr<scalar_t, 0, 0, 0>(
859 Vlocal[vtoken_depth][vhe_depth][vfetch_depth].xy[i],
860 shared_logits[vtoken_depth][offset2][lane16id][offset1],
861 tmp_out);
862 }
863 }
864 }
865 outelems[vhe_depth] = from_floatx4<scalar_t>(tmp_out);
866 }
867 } else {
868 const int valid_vtoken_depths =
869 min(VTLOOP, DIVIDE_ROUND_UP(seq_len - partition_start_token_idx,
870 VTOKENS_PER_LANE * ROWS_PER_WARP));
871 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
872 floatx4 tmp_out = {0};
873 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
874 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
875 _B16x8 Vtmp = Vlocal[vtoken_depth][vhe_depth][vfetch_depth];
876 if (__builtin_expect(partition_start_token_idx + T_PAR_SIZE > seq_len,
877 0)) {
878 if (vtoken_depth >= valid_vtoken_depths) {
879 continue;
880 }
881 if (vtoken_depth + 1 == valid_vtoken_depths) {
882 const int vglobal_fetch_start =
883 partition_start_token_idx +
884 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
885 rowid * VTOKENS_PER_LANE +
886 vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
887 if (vglobal_fetch_start >= seq_len) {
888 continue;
889 }
890 if (vglobal_fetch_start + CONTIGUOUS_KV_ELEMS_16B_LOAD >
891 seq_len) {
892 Vtmp = mask_16b_v_cache_boundary(Vtmp,
893 seq_len - vglobal_fetch_start);
894 }
895 }
896 }
897 // reinterpret V format as 16 elements of 8bits
898 _B8x16 Vtmp8x16 = *reinterpret_cast<_B8x16*>(&Vtmp);
899 for (int j = 0; j < ELEMS16_ELEMS8_RATIO; j++) {
900 _B8x8 Vtmp8x8 = Vtmp8x16.xy[j];
901 if constexpr (MFMA_TYPE == MFMAType::F16) {
902 _B16x8 Vlocaltmp = convert_b8x8_custom<scalar_t>(Vtmp8x8);
903 for (int i = 0; i < ELEMS8_ELEMS4_RATIO; i++) {
904 const int offset =
905 rowid * ELEMS16_ELEMS8_RATIO * ELEMS8_ELEMS4_RATIO +
906 j * ELEMS8_ELEMS4_RATIO + i;
907 const int offset1 = offset % ROWS_PER_WARP;
908 const int offset2 = offset / ROWS_PER_WARP;
909 // output format is 16 qheads across 16 lanes, 16 head elems
910 // spread across 4 rows
911 tmp_out = gcn_mfma16x16x16_instr<scalar_t, 0, 0, 0>(
912 Vlocaltmp.xy[i],
913 shared_logits[vtoken_depth][offset2][lane16id][offset1],
914 tmp_out);
915 }
916 } else {
917 #if defined(__HIP__FP8MFMA__)
918 for (int i = 0; i < ELEMS8_ELEMS4_RATIO / 2; i++) {
919 const int offset =
920 rowid * ELEMS16_ELEMS8_RATIO * ELEMS8_ELEMS4_RATIO +
921 j * ELEMS8_ELEMS4_RATIO + i;
922 const int offset1 = (offset % ROWS_PER_WARP) / 2;
923 const int offset2 = offset / ROWS_PER_WARP;
924 // output format is 16 qheads across 16 lanes, 16 head elems
925 // spread across 4 rows
926 tmp_out = gcn_mfma16x16x32_instr<__hip_fp8_e4m3, 0, 0, 0>(
927 reinterpret_cast<_T8x8*>(&Vtmp8x8)->i64,
928 reinterpret_cast<_T8x8*>(
929 &shared_logits[vtoken_depth][offset2][lane16id]
930 [offset1])
931 ->i64,
932 tmp_out);
933 }
934 #else
935 UNREACHABLE_CODE
936 #endif
937 }
938 }
939 }
940 }
941 tmp_out *= *v_scale;
942 outelems[vhe_depth] = from_floatx4<scalar_t>(tmp_out);
943 }
944 }
945
946 __syncthreads();
947
948 // store Softmax-V mfma output to shared mem
949 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
950 // lane16 id head dimension; rowid head element dimension
951 shared_logits[warpid][vhe_depth][lane16id][rowid] = outelems[vhe_depth];
952 }
953
954 __syncthreads();
955
956 // write to tmp_out with coalesced writes after reading from shared mem
957 if (warpid == 0) {
958 _B16x8 vout[GQA_RATIO4];
959 // each lane writes out 16Bytes of tmp_out along head elem dimension
960 const int head_elem_idx = lane16id * 8;
961 if (head_elem_idx < HEAD_SIZE) {
962 for (int h = 0; h < GQA_RATIO4; h++) {
963 const int local_head_idx = 4 * h + rowid;
964 const int offset1 = (head_elem_idx / 16) % 4;
965 const int offset2 = head_elem_idx / 16 / NWARPS;
966 const int offset3 = (head_elem_idx / 4) % 4;
967 for (int i = 0; i < 2; i++) {
968 vout[h].xy[i] =
969 shared_logits[offset1][offset2][local_head_idx][offset3 + i];
970 }
971 }
972
973 const int64_t hsz_maxp_mult =
974 static_cast<int64_t>(HEAD_SIZE * max_num_partitions);
975 scalar_t* out_ptr = out + seq_idx * total_num_heads * hsz_maxp_mult +
976 partition_idx * HEAD_SIZE;
977 for (int h = 0; h < GQA_RATIO4; h++) {
978 const int local_head_idx = 4 * h + rowid;
979 if (local_head_idx < GQA_RATIO) {
980 const int64_t out_head_idx =
981 static_cast<int64_t>(wg_start_head_idx + local_head_idx);
982 scalar_t* out_ptr2 = out_ptr + out_head_idx * hsz_maxp_mult;
983 scalar_t* out_ptr3 = out_ptr2 + head_elem_idx;
984 _B16x8* out_ptr_B16x8 = reinterpret_cast<_B16x8*>(out_ptr3);
985 *out_ptr_B16x8 = vout[h];
986 }
987 }
988 }
989 }
990 }
991
992 // grid (num_seqs, num_partitions, num_kv_heads)
993 // block (256 : partition size)
994 // each WG handles 1 partition per sequence
995 // clang-format off
996 template <typename scalar_t, typename cache_t,
997 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
998 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED,
999 int GQA_RATIO>
1000 __global__
1001 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
1002 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
1003 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
1004 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
1005 const int num_kv_heads,
1006 const float scale,
1007 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
1008 const int* __restrict__ seq_lens, // [num_seqs]
1009 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1010 const int max_num_blocks_per_seq,
1011 const float* __restrict__ alibi_slopes, // [num_heads]
1012 const int q_stride,
1013 const int kv_block_stride,
1014 const int kv_head_stride,
1015 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
1016 float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
1017 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions, head_size]
1018 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
1019 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
1020 // clang-format on
1021 constexpr int NWARPS = NUM_THREADS / WARP_SIZE;
1022 const auto warpid = threadIdx.x / WARP_SIZE;
1023 const auto laneid = threadIdx.x % WARP_SIZE;
1024 const int lane4id = laneid % 4;
1025
1026 const auto seq_idx = blockIdx.x;
1027 // NOTE queries with sequence len > 1 are prefills and taken care by another
1028 // kernel.
1029 if (query_start_loc_ptr != nullptr &&
1030 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
1031 return;
1032 }
1033 const auto partition_idx = blockIdx.y;
1034 const auto partition_size = blockDim.x;
1035 const auto max_num_partitions = gridDim.y;
1036
1037 const int seq_len = seq_lens[seq_idx];
1038 const int partition_start_token_idx = partition_idx * partition_size;
1039 // exit if partition is out of context for seq
1040 if (partition_start_token_idx >= seq_len) {
1041 return;
1042 }
1043 // every 4 lanes fetch 4 different qheads
1044 // qhloop = num loops over qhead dimension
1045 constexpr int QHLOOP = DIVIDE_ROUND_UP(GQA_RATIO, 4);
1046 constexpr int GQA_RATIO4 = 4 * QHLOOP;
1047 __shared__ float shared_qk_max[NWARPS][GQA_RATIO4 + 1];
1048 __shared__ float shared_exp_sum[NWARPS][GQA_RATIO4 + 1];
1049 _B16x8 Qlocal[QHLOOP];
1050 constexpr int x = 16 / sizeof(scalar_t);
1051 // kheloop = num loops over head_size for 16Bytes of Q/dequantized K elements
1052 constexpr int KHELOOP = HEAD_SIZE / x;
1053 _B16x8 Klocal[KHELOOP];
1054 _B8x8 Klocalb8[KHELOOP];
1055 // for SoftMax-V Gemm, V head_size dimension is distributed across warp
1056 // vheloop = num loops to cover v head size dimension
1057 constexpr int VHELOOP = HEAD_SIZE / WARP_SIZE;
1058 // softmax out has warp_size tokens across warp
1059 // vtloop = num loops to cover warp_size(64) tokens with 16Bytes of
1060 // dequantized V elements
1061 constexpr int VTLOOP = WARP_SIZE / 8;
1062 // num vblocks to cover warp_size(64) v elements
1063 constexpr int VBLOCKS = 8 * VTLOOP / BLOCK_SIZE;
1064 int vphysical_blocks[VBLOCKS];
1065 _B16x8 Vlocal[VHELOOP][VTLOOP];
1066 _B8x8 Vlocalb8[VHELOOP][VTLOOP];
1067 floatx4 d_out[QHLOOP];
1068 float qk_max[QHLOOP];
1069
1070 __shared__ _B16x4 vout_shared[QHLOOP][VHELOOP][WARP_SIZE][NWARPS + 1];
1071
1072 for (int h = 0; h < QHLOOP; h++) {
1073 d_out[h] = {0};
1074 qk_max[h] = -FLT_MAX;
1075 }
1076
1077 const auto wg_start_head_idx = blockIdx.z * GQA_RATIO;
1078 const auto wg_start_kv_head_idx = blockIdx.z;
1079
1080 const int warp_start_token_idx =
1081 partition_start_token_idx + warpid * WARP_SIZE;
1082
1083 if (warp_start_token_idx >= seq_len) { // warp out of context
1084 #pragma unroll
1085 for (int h = 0; h < GQA_RATIO4; h++) {
1086 shared_qk_max[warpid][h] = -FLT_MAX;
1087 shared_exp_sum[warpid][h] = 0.0f;
1088 }
1089 } else { // warp within context
1090
1091 const int num_seq_blocks = DIVIDE_ROUND_UP(seq_len, BLOCK_SIZE);
1092 const int last_seq_block = num_seq_blocks - 1;
1093
1094 const int* block_table = block_tables + seq_idx * max_num_blocks_per_seq;
1095 // token id within partition
1096 const auto local_token_idx = threadIdx.x;
1097 // token id within sequence
1098 const int global_token_idx = partition_start_token_idx + local_token_idx;
1099
1100 // fetch block number for k
1101 const int block_idx = (global_token_idx < seq_len)
1102 ? global_token_idx / BLOCK_SIZE
1103 : last_seq_block;
1104
1105 // fetch k physical block number
1106 // int32 physical_block_number leads to overflow when multiplied with
1107 // kv_block_stride
1108 const int64_t physical_block_number =
1109 static_cast<int64_t>(block_table[block_idx]);
1110
1111 // fetch vphysical block numbers up front
1112 const int warp_start_block_idx = warp_start_token_idx / BLOCK_SIZE;
1113 for (int b = 0; b < VBLOCKS; b++) {
1114 const int vblock_idx = warp_start_block_idx + b;
1115 const int vblock_idx_ctx =
1116 (vblock_idx <= last_seq_block) ? vblock_idx : last_seq_block;
1117 vphysical_blocks[b] = block_table[vblock_idx_ctx];
1118 }
1119
1120 // fetch q elements
1121 // every 4 lanes fetch 8 elems, so warp fetches 8*16 = 128 elemsc
1122 const int64_t query_start_off = static_cast<int64_t>(
1123 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
1124 const scalar_t* q_ptr =
1125 q + query_start_off * q_stride + wg_start_head_idx * HEAD_SIZE;
1126 const _B16x8* q_ptrh8 = reinterpret_cast<const _B16x8*>(q_ptr);
1127 const int qhead_elemh8 = MIN(laneid / 4, HEAD_SIZE / 8 - 1);
1128
1129 for (int h = 0; h < QHLOOP - 1; h++) {
1130 const int qhead_idx = h * 4 + lane4id;
1131 Qlocal[h] = q_ptrh8[qhead_idx * HEAD_SIZE / 8 + qhead_elemh8];
1132 }
1133 const int final_qhead_idx = 4 * (QHLOOP - 1) + lane4id;
1134 if (final_qhead_idx < GQA_RATIO) {
1135 Qlocal[QHLOOP - 1] =
1136 q_ptrh8[final_qhead_idx * HEAD_SIZE / 8 + qhead_elemh8];
1137 } else {
1138 Qlocal[QHLOOP - 1].xy[0] = {0};
1139 Qlocal[QHLOOP - 1].xy[1] = {0};
1140 }
1141
1142 // fetch k elements
1143 const cache_t* k_ptr = k_cache + physical_block_number * kv_block_stride +
1144 wg_start_kv_head_idx * kv_head_stride;
1145
1146 // physical_block_offset is already cast in terms of _B16x8
1147 const int physical_block_offset = local_token_idx % BLOCK_SIZE;
1148
1149 // each K fetch is for 8 elements of cache_t which are later dequantized to
1150 // scalar_t for fp8
1151 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) {
1152 const _B16x8* k_ptrh8 = reinterpret_cast<const _B16x8*>(k_ptr);
1153 for (int d = 0; d < KHELOOP; d++) {
1154 Klocal[d] = k_ptrh8[d * BLOCK_SIZE + physical_block_offset];
1155 }
1156 } else {
1157 // vllm defines X as 16 Bytes of elements of cache_t
1158 constexpr int X = 16 / sizeof(cache_t);
1159 const cache_t* k_ptr2 = k_ptr + physical_block_offset * X;
1160 for (int d = 0; d < KHELOOP; d++) {
1161 const int head_elem = d * 8;
1162 const int offset1 = head_elem / X;
1163 const int offset2 = head_elem % X;
1164 const cache_t* k_ptr3 = k_ptr2 + offset1 * BLOCK_SIZE * X + offset2;
1165 Klocalb8[d] = *reinterpret_cast<const _B8x8*>(k_ptr3);
1166 }
1167 }
1168
1169 // optional alibi fetch
1170 float alibi_slope[QHLOOP];
1171 if constexpr (ALIBI_ENABLED) {
1172 for (int h = 0; h < QHLOOP; h++) {
1173 const int qhead_idx = h * 4 + lane4id;
1174 alibi_slope[h] = (qhead_idx < GQA_RATIO)
1175 ? alibi_slopes[wg_start_head_idx + qhead_idx]
1176 : 0.f;
1177 }
1178 }
1179
1180 const cache_t* v_ptr = v_cache + wg_start_kv_head_idx * kv_head_stride;
1181 // fetch vcache in kv cache auto case
1182 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) {
1183 const _B16x8* v_ptrh8 = reinterpret_cast<const _B16x8*>(v_ptr);
1184 // iterate over each v block
1185 for (int b = 0; b < VBLOCKS; b++) {
1186 // int32 physical_block_number leads to overflow when multiplied with
1187 // kv_block_stride
1188 const int64_t vphysical_block_number =
1189 static_cast<int64_t>(vphysical_blocks[b]);
1190 const _B16x8* v_ptrh8b =
1191 v_ptrh8 + (vphysical_block_number * kv_block_stride) / 8;
1192 // iterate over each head elem (within head_size)
1193 for (int h = 0; h < VHELOOP; h++) {
1194 const int head_size_elem = h * WARP_SIZE + laneid;
1195 const _B16x8* v_ptrh8be = v_ptrh8b + head_size_elem * BLOCK_SIZE / 8;
1196 // iterate over all velems within block
1197 for (int d = 0; d < BLOCK_SIZE / 8; d++) {
1198 Vlocal[h][b * BLOCK_SIZE / 8 + d] = v_ptrh8be[d];
1199 }
1200 }
1201 }
1202 } // if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto)
1203 // fetch vcache in fp8 case
1204 else { // if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto)
1205 const _B8x8* v_ptrh8 = reinterpret_cast<const _B8x8*>(v_ptr);
1206 // iterate over each v block
1207 for (int b = 0; b < VBLOCKS; b++) {
1208 // int32 physical_block_number leads to overflow when multiplied with
1209 // kv_block_stride
1210 const int64_t vphysical_block_number =
1211 static_cast<int64_t>(vphysical_blocks[b]);
1212 const _B8x8* v_ptrh8b =
1213 v_ptrh8 + (vphysical_block_number * kv_block_stride) / 8;
1214 // iterate over each head elem (within head_size)
1215 for (int h = 0; h < VHELOOP; h++) {
1216 const int head_size_elem = h * WARP_SIZE + laneid;
1217 const _B8x8* v_ptrh8be = v_ptrh8b + head_size_elem * BLOCK_SIZE / 8;
1218 // iterate over all velems within block
1219 for (int d = 0; d < BLOCK_SIZE / 8; d++) {
1220 Vlocalb8[h][b * BLOCK_SIZE / 8 + d] = v_ptrh8be[d];
1221 }
1222 }
1223 }
1224 }
1225
1226 #define QK_mfma(x) \
1227 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto) { \
1228 Klocal[x] = convert_b8x8_custom<scalar_t>(Klocalb8[x]); \
1229 } \
1230 for (int h = 0; h < QHLOOP; h++) { \
1231 d_out[h] = gcn_mfma4x4x4_instr<scalar_t, 4, x, 0>( \
1232 Qlocal[h].xy[0], Klocal[x].xy[0], d_out[h]); \
1233 d_out[h] = gcn_mfma4x4x4_instr<scalar_t, 4, x, 0>( \
1234 Qlocal[h].xy[1], Klocal[x].xy[1], d_out[h]); \
1235 }
1236 // QK mfma with Q mfma block broadcast
1237 // Q values across head_size dimension stored across lanes
1238 // K values across head_size dimension are stored depthwise within lane
1239 // Q broadcast with absz, cbid of mfma instruction
1240 QK_mfma(0);
1241 QK_mfma(1);
1242 QK_mfma(2);
1243 QK_mfma(3);
1244 QK_mfma(4);
1245 QK_mfma(5);
1246 QK_mfma(6);
1247 QK_mfma(7);
1248 // below only needed for head size 128
1249 if constexpr (KHELOOP > 8) {
1250 QK_mfma(8);
1251 QK_mfma(9);
1252 QK_mfma(10);
1253 QK_mfma(11);
1254 QK_mfma(12);
1255 QK_mfma(13);
1256 QK_mfma(14);
1257 QK_mfma(15);
1258 }
1259 #undef QK_mfma
1260
1261 float scale2 = scale;
1262 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto) {
1263 // post mfma scaling for fp8
1264 scale2 *= *k_scale;
1265 }
1266
1267 for (int h = 0; h < QHLOOP; h++) {
1268 d_out[h] *= scale2;
1269 }
1270
1271 // transpose d_out so that 4 token ids are in each lane, and 4 heads are
1272 // across 4 lanes
1273 for (int h = 0; h < QHLOOP; h++) {
1274 floatx4 tmp = {0};
1275 for (int i = 0; i < 4; i++) {
1276 const float B = (lane4id == i) ? 1.0f : 0.0f;
1277 tmp = __builtin_amdgcn_mfma_f32_4x4x1f32(d_out[h][i], B, tmp, 0, 0, 0);
1278 }
1279 d_out[h] = tmp;
1280 }
1281
1282 const int lane4_token_idx = 4 * (global_token_idx >> 2);
1283
1284 if constexpr (ALIBI_ENABLED) {
1285 const int alibi_offset = lane4_token_idx - seq_len + 1;
1286 for (int h = 0; h < QHLOOP; h++) {
1287 for (int i = 0; i < 4; i++) {
1288 d_out[h][i] += alibi_slope[h] * (alibi_offset + i);
1289 }
1290 }
1291 }
1292
1293 const int bpermute_mask = 4 * (16 * ((laneid >> 2) % 4) + lane4id);
1294
1295 for (int h = 0; h < QHLOOP; h++) {
1296 qk_max[h] = -FLT_MAX;
1297 for (int i = 0; i < 4; i++) {
1298 qk_max[h] = (lane4_token_idx + i < seq_len)
1299 ? fmaxf(qk_max[h], d_out[h][i])
1300 : qk_max[h];
1301 }
1302
1303 // for (int mask = WARP_SIZE / 2; mask >= 4; mask /= 2) {
1304 // qk_max[h] = fmaxf(qk_max[h], __shfl_xor(qk_max[h], mask));
1305 // }
1306 // faster version of above code with dpp
1307 asm("v_nop\n v_nop\n v_max_f32_dpp %0, %1, %2 row_ror:4"
1308 : "=v"(qk_max[h])
1309 : "v"(qk_max[h]), "v"(qk_max[h]));
1310 asm("v_nop\n v_nop\n v_max_f32_dpp %0, %1, %2 row_ror:8"
1311 : "=v"(qk_max[h])
1312 : "v"(qk_max[h]), "v"(qk_max[h]));
1313
1314 auto tmp = __builtin_amdgcn_ds_bpermute(
1315 bpermute_mask, *reinterpret_cast<int*>(&qk_max[h]));
1316 qk_max[h] = *reinterpret_cast<float*>(&tmp);
1317 asm("v_nop\n v_nop\n v_max_f32_dpp %0, %1, %2 row_ror:4"
1318 : "=v"(qk_max[h])
1319 : "v"(qk_max[h]), "v"(qk_max[h]));
1320 asm("v_nop\n v_nop\n v_max_f32_dpp %0, %1, %2 row_ror:8"
1321 : "=v"(qk_max[h])
1322 : "v"(qk_max[h]), "v"(qk_max[h]));
1323 }
1324
1325 float exp_sum[QHLOOP];
1326 for (int h = 0; h < QHLOOP; h++) {
1327 exp_sum[h] = 0.0f;
1328 for (int i = 0; i < 4; i++) {
1329 d_out[h][i] = (lane4_token_idx + i < seq_len)
1330 ? __expf(d_out[h][i] - qk_max[h])
1331 : 0.0f;
1332 exp_sum[h] += d_out[h][i];
1333 }
1334 // for (int mask = WARP_SIZE / 2; mask >= 4; mask /= 2) {
1335 // exp_sum[h] += __shfl_xor(exp_sum[h], mask);
1336 // }
1337 // faster version of above code with dpp
1338 asm("v_nop\n v_nop\n v_add_f32_dpp %0, %1, %2 row_ror:4"
1339 : "=v"(exp_sum[h])
1340 : "v"(exp_sum[h]), "v"(exp_sum[h]));
1341 asm("v_nop\n v_nop\n v_add_f32_dpp %0, %1, %2 row_ror:8"
1342 : "=v"(exp_sum[h])
1343 : "v"(exp_sum[h]), "v"(exp_sum[h]));
1344
1345 auto tmp = __builtin_amdgcn_ds_bpermute(
1346 bpermute_mask, *reinterpret_cast<int*>(&exp_sum[h]));
1347 exp_sum[h] = *reinterpret_cast<float*>(&tmp);
1348 asm("v_nop\n v_nop\n v_add_f32_dpp %0, %1, %2 row_ror:4"
1349 : "=v"(exp_sum[h])
1350 : "v"(exp_sum[h]), "v"(exp_sum[h]));
1351 asm("v_nop\n v_nop\n v_add_f32_dpp %0, %1, %2 row_ror:8"
1352 : "=v"(exp_sum[h])
1353 : "v"(exp_sum[h]), "v"(exp_sum[h]));
1354 }
1355
1356 if (laneid < 4) {
1357 for (int h = 0; h < QHLOOP; h++) {
1358 const int head_idx = 4 * h + lane4id;
1359 shared_qk_max[warpid][head_idx] = qk_max[h];
1360 shared_exp_sum[warpid][head_idx] = exp_sum[h];
1361 }
1362 }
1363 } // warp within context
1364
1365 __syncthreads();
1366
1367 const auto num_heads = gridDim.z * GQA_RATIO;
1368 float* max_logits_ptr =
1369 max_logits + seq_idx * num_heads * max_num_partitions + partition_idx;
1370 float* exp_sums_ptr =
1371 exp_sums + seq_idx * num_heads * max_num_partitions + partition_idx;
1372 // calculate qk_max and exp_sums for partition
1373 for (int h = 0; h < QHLOOP; h++) {
1374 float global_qk_max = -FLT_MAX;
1375 float warp_qk_max[NWARPS];
1376 const int head_idx = 4 * h + lane4id;
1377 for (int w = 0; w < NWARPS; w++) {
1378 warp_qk_max[w] = shared_qk_max[w][head_idx];
1379 global_qk_max = fmaxf(global_qk_max, warp_qk_max[w]);
1380 }
1381 float global_exp_sum = 0.0f;
1382 for (int w = 0; w < NWARPS; w++) {
1383 global_exp_sum +=
1384 shared_exp_sum[w][head_idx] * __expf(warp_qk_max[w] - global_qk_max);
1385 }
1386 if (head_idx < GQA_RATIO) {
1387 max_logits_ptr[(wg_start_head_idx + head_idx) * max_num_partitions] =
1388 global_qk_max;
1389 exp_sums_ptr[(wg_start_head_idx + head_idx) * max_num_partitions] =
1390 global_exp_sum;
1391 }
1392 const float global_inv_sum_scale = __fdividef(1.f, global_exp_sum + 1e-6f) *
1393 __expf(qk_max[h] - global_qk_max);
1394 d_out[h] *= global_inv_sum_scale;
1395 }
1396 constexpr bool LOGITS_RTZ_CONVERSION = false;
1397 // logits[h] -> every 4 lanes hold 4 heads, each lane holds 4 tokens, there
1398 // are 4x16 tokens across warp
1399 _B16x4 logits[QHLOOP];
1400 for (int h = 0; h < QHLOOP; h++) {
1401 if constexpr (LOGITS_RTZ_CONVERSION) {
1402 // use rtz for faster performance with no perceivable accuracy loss
1403 logits[h] = from_floatx4_rtz<scalar_t>(d_out[h]);
1404 } else {
1405 logits[h] = from_floatx4<scalar_t>(d_out[h]);
1406 }
1407 }
1408
1409 if (warp_start_token_idx >= seq_len) { // warp out of context
1410 for (int qh = 0; qh < QHLOOP; qh++) {
1411 for (int vh = 0; vh < VHELOOP; vh++) {
1412 vout_shared[qh][vh][laneid][warpid] = {0};
1413 }
1414 }
1415 } else { // warp in context
1416 #define SV_mfma_value(x, value) \
1417 for (int qh = 0; qh < QHLOOP; qh++) { \
1418 acc[qh] = gcn_mfma4x4x4_instr<scalar_t, 4, 2 * x, 0>( \
1419 logits[qh], value.xy[0], acc[qh]); \
1420 acc[qh] = gcn_mfma4x4x4_instr<scalar_t, 4, 2 * x + 1, 0>( \
1421 logits[qh], value.xy[1], acc[qh]); \
1422 }
1423
1424 #define SV_mfma(x) \
1425 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto) { \
1426 Vlocal[vh][x] = convert_b8x8_custom<scalar_t>(Vlocalb8[vh][x]); \
1427 } \
1428 SV_mfma_value(x, Vlocal[vh][x])
1429
1430 #define SV_mfma_tail(x) \
1431 if (x * 8 < valid_warp_tokens) { \
1432 _B16x8 Vtmp; \
1433 if constexpr (KV_DTYPE == vllm::Fp8KVCacheDataType::kAuto) { \
1434 Vtmp = Vlocal[vh][x]; \
1435 if ((x + 1) * 8 > valid_warp_tokens) { \
1436 Vtmp = mask_16b_v_cache_boundary( \
1437 Vtmp, (valid_warp_tokens - x * 8) * sizeof(cache_t)); \
1438 } \
1439 } else { \
1440 _B8x8 Vtmp8 = Vlocalb8[vh][x]; \
1441 if ((x + 1) * 8 > valid_warp_tokens) { \
1442 Vtmp8 = mask_8b_v_cache_boundary(Vtmp8, valid_warp_tokens - x * 8); \
1443 } \
1444 Vtmp = convert_b8x8_custom<scalar_t>(Vtmp8); \
1445 } \
1446 SV_mfma_value(x, Vtmp) \
1447 }
1448
1449 for (int vh = 0; vh < VHELOOP; vh++) {
1450 floatx4 acc[QHLOOP];
1451 for (int qh = 0; qh < QHLOOP; qh++) {
1452 acc[qh] = {0};
1453 }
1454 // SoftMax-V calculation. Skip fully invalid V vectors in the partial
1455 // tail warp, and only mask the one vector crossing seq_len.
1456 if (__builtin_expect(warp_start_token_idx + WARP_SIZE > seq_len, 0)) {
1457 const int valid_warp_tokens = seq_len - warp_start_token_idx;
1458 SV_mfma_tail(0);
1459 SV_mfma_tail(1);
1460 SV_mfma_tail(2);
1461 SV_mfma_tail(3);
1462 SV_mfma_tail(4);
1463 SV_mfma_tail(5);
1464 SV_mfma_tail(6);
1465 SV_mfma_tail(7);
1466 } else {
1467 SV_mfma(0);
1468 SV_mfma(1);
1469 SV_mfma(2);
1470 SV_mfma(3);
1471 SV_mfma(4);
1472 SV_mfma(5);
1473 SV_mfma(6);
1474 SV_mfma(7);
1475 }
1476
1477 for (int qh = 0; qh < QHLOOP; qh++) {
1478 if constexpr (KV_DTYPE != vllm::Fp8KVCacheDataType::kAuto) {
1479 // post mfma v scale for fp8
1480 acc[qh] *= *v_scale;
1481 }
1482 vout_shared[qh][vh][laneid][warpid] = from_floatx4<scalar_t>(acc[qh]);
1483 }
1484 }
1485
1486 #undef SV_mfma
1487 #undef SV_mfma_tail
1488 #undef SV_mfma_value
1489 } // warp in context
1490
1491 __syncthreads();
1492
1493 // final write to tmp_out after vout accumulation
1494 if (warpid == 0) {
1495 _B16x4 vout[QHLOOP][VHELOOP];
1496 // iterate across heads
1497 for (int qh = 0; qh < QHLOOP; qh++) {
1498 // iterate over each v head elem (within head_size)
1499 for (int vh = 0; vh < VHELOOP; vh++) {
1500 vout[qh][vh] = {0};
1501 for (int w = 0; w < NWARPS; w++) {
1502 vout[qh][vh] =
1503 addx4<scalar_t>(vout[qh][vh], vout_shared[qh][vh][laneid][w]);
1504 }
1505 }
1506 }
1507
1508 scalar_t* out_ptr = out +
1509 seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
1510 partition_idx * HEAD_SIZE;
1511 const int out_num_partitions = max_num_partitions;
1512 bit16_t* out_ptr_b16 = reinterpret_cast<bit16_t*>(out_ptr);
1513 for (int qh = 0; qh < QHLOOP; qh++) {
1514 for (int vh = 0; vh < VHELOOP; vh++) {
1515 const int head_size_elem = vh * WARP_SIZE + laneid;
1516 for (int i = 0; i < 4; i++) {
1517 const int head_idx = 4 * qh + i;
1518 if (head_idx < GQA_RATIO) {
1519 out_ptr_b16[(wg_start_head_idx + head_idx) * out_num_partitions *
1520 HEAD_SIZE +
1521 head_size_elem] = vout[qh][vh][i];
1522 }
1523 }
1524 }
1525 }
1526 } // warpid == 0
1527 }
1528
1529 // Grid: (num_heads, num_seqs).
1530 template <typename scalar_t, typename OUTT, int HEAD_SIZE, int NUM_THREADS,
1531 int PARTITION_SIZE, int NPAR_LOOPS>
1532 __global__
1533 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
1534 OUTT* __restrict__ out, // [num_seqs, num_heads, head_size]
1535 const float* __restrict__ exp_sums, // [num_seqs, num_heads,
1536 // max_num_partitions]
1537 const float* __restrict__ max_logits, // [num_seqs, num_heads,
1538 // max_num_partitions]
1539 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
1540 // max_num_partitions, head_size]
1541 const int* __restrict__ seq_lens, // [num_seqs]
1542 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1543 const int max_num_partitions, const float* __restrict__ fp8_out_scale_ptr) {
1544 const auto num_heads = gridDim.x;
1545 const auto head_idx = blockIdx.x;
1546 const auto seq_idx = blockIdx.y;
1547
1548 // NOTE queries with sequence len > 1 are prefills and taken care by another
1549 // kernel.
1550 if (query_start_loc_ptr != nullptr &&
1551 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
1552 return;
1553 }
1554
1555 const int seq_len = seq_lens[seq_idx];
1556 const int num_partitions = DIVIDE_ROUND_UP(seq_len, PARTITION_SIZE);
1557 const auto warpid = threadIdx.x / WARP_SIZE;
1558
1559 __shared__ float shared_global_exp_sum;
1560 // max num partitions supported is warp_size * NPAR_LOOPS
1561 __shared__ float shared_exp_sums[NPAR_LOOPS * WARP_SIZE];
1562
1563 if (warpid == 0) {
1564 const float* max_logits_ptr = max_logits +
1565 seq_idx * num_heads * max_num_partitions +
1566 head_idx * max_num_partitions;
1567
1568 // valid partition is the last valid partition in case threadid > num
1569 // partitions
1570 int valid_partition[NPAR_LOOPS];
1571 float reg_max_logit[NPAR_LOOPS];
1572 const int last_valid_partition = num_partitions - 1;
1573
1574 #pragma unroll
1575 for (int i = 0; i < NPAR_LOOPS; i++) {
1576 const auto partition_no = i * WARP_SIZE + threadIdx.x;
1577 valid_partition[i] =
1578 (partition_no < num_partitions) ? partition_no : last_valid_partition;
1579 }
1580 #pragma unroll
1581 for (int i = 0; i < NPAR_LOOPS; i++) {
1582 reg_max_logit[i] = max_logits_ptr[valid_partition[i]];
1583 }
1584 float max_logit = reg_max_logit[0];
1585 #pragma unroll
1586 for (int i = 1; i < NPAR_LOOPS; i++) {
1587 max_logit = fmaxf(max_logit, reg_max_logit[i]);
1588 }
1589
1590 #pragma unroll
1591 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
1592 max_logit = fmaxf(max_logit, __shfl_xor(max_logit, mask));
1593 }
1594
1595 const float* exp_sums_ptr = exp_sums +
1596 seq_idx * num_heads * max_num_partitions +
1597 head_idx * max_num_partitions;
1598
1599 float rescaled_exp_sum[NPAR_LOOPS];
1600 #pragma unroll
1601 for (int i = 0; i < NPAR_LOOPS; i++) {
1602 rescaled_exp_sum[i] = exp_sums_ptr[valid_partition[i]];
1603 }
1604 #pragma unroll
1605 for (int i = 0; i < NPAR_LOOPS; i++) {
1606 const auto partition_no = i * WARP_SIZE + threadIdx.x;
1607 rescaled_exp_sum[i] *= (partition_no < num_partitions)
1608 ? expf(reg_max_logit[i] - max_logit)
1609 : 0.0f;
1610 }
1611 float global_exp_sum = rescaled_exp_sum[0];
1612 #pragma unroll
1613 for (int i = 1; i < NPAR_LOOPS; i++) {
1614 global_exp_sum += rescaled_exp_sum[i];
1615 }
1616 #pragma unroll
1617 for (int i = 0; i < NPAR_LOOPS; i++) {
1618 const auto partition_no = i * WARP_SIZE + threadIdx.x;
1619 shared_exp_sums[partition_no] = rescaled_exp_sum[i];
1620 }
1621
1622 #pragma unroll
1623 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
1624 global_exp_sum += __shfl_xor(global_exp_sum, mask);
1625 }
1626 if (threadIdx.x == 0) {
1627 shared_global_exp_sum = global_exp_sum;
1628 }
1629 } // warpid == 0
1630 const scalar_t* tmp_out_ptr =
1631 tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
1632 head_idx * max_num_partitions * HEAD_SIZE + threadIdx.x;
1633 constexpr int MAX_NPAR = 64;
1634 scalar_t tmps[MAX_NPAR];
1635 const float dzero = 0.0f;
1636 #pragma unroll
1637 for (int j = 0; j < MAX_NPAR; j++) {
1638 tmps[j] = from_float<scalar_t>(dzero);
1639 }
1640 const int last_partition_offset = (num_partitions - 1) * HEAD_SIZE;
1641 const int num_partition_offset = (num_partitions)*HEAD_SIZE;
1642 int idx = 0;
1643
1644 constexpr int JCHUNK = 16;
1645
1646 #pragma unroll
1647 for (int j = 0; j < JCHUNK * HEAD_SIZE; j += HEAD_SIZE) {
1648 // lastj is last valid partition
1649 const int lastj_offset =
1650 (j < num_partition_offset) ? j : last_partition_offset;
1651 tmps[idx] = tmp_out_ptr[lastj_offset];
1652 idx++;
1653 }
1654 __syncthreads();
1655
1656 if (num_partitions > JCHUNK) {
1657 #pragma unroll
1658 for (int j = JCHUNK * HEAD_SIZE; j < 2 * JCHUNK * HEAD_SIZE;
1659 j += HEAD_SIZE) {
1660 const int lastj_offset =
1661 (j < num_partition_offset) ? j : last_partition_offset;
1662 tmps[idx] = tmp_out_ptr[lastj_offset];
1663 idx++;
1664 }
1665
1666 if (num_partitions > 2 * JCHUNK) {
1667 #pragma unroll
1668 for (int j = 2 * JCHUNK * HEAD_SIZE; j < MAX_NPAR * HEAD_SIZE;
1669 j += HEAD_SIZE) {
1670 const int lastj_offset =
1671 (j < num_partition_offset) ? j : last_partition_offset;
1672 tmps[idx] = tmp_out_ptr[lastj_offset];
1673 idx++;
1674 }
1675 }
1676 } // num_partitions > JCHUNK
1677
1678 // Aggregate tmp_out to out.
1679 float acc = 0.0f;
1680 #pragma unroll
1681 for (int j = 0; j < JCHUNK; j++) {
1682 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
1683 }
1684 if (num_partitions > JCHUNK) {
1685 #pragma unroll
1686 for (int j = JCHUNK; j < 2 * JCHUNK; j++) {
1687 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
1688 }
1689 if (num_partitions > 2 * JCHUNK) {
1690 #pragma unroll
1691 for (int j = 2 * JCHUNK; j < MAX_NPAR; j++) {
1692 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
1693 }
1694 }
1695 }
1696
1697 for (int p = 1; p < NPAR_LOOPS; p++) {
1698 if (num_partitions > p * MAX_NPAR) {
1699 idx = 0;
1700 #pragma unroll
1701 for (int j = p * MAX_NPAR * HEAD_SIZE; j < (p + 1) * MAX_NPAR * HEAD_SIZE;
1702 j += HEAD_SIZE) {
1703 // lastj is last valid partition
1704 const int lastj_offset =
1705 (j < num_partition_offset) ? j : last_partition_offset;
1706 tmps[idx] = tmp_out_ptr[lastj_offset];
1707 idx++;
1708 }
1709
1710 #pragma unroll
1711 for (int j = 0; j < MAX_NPAR; j++) {
1712 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j + p * MAX_NPAR];
1713 }
1714 }
1715 }
1716
1717 const float inv_global_exp_sum =
1718 __fdividef(1.0f, shared_global_exp_sum + 1e-6f);
1719 const float out_scale =
1720 (fp8_out_scale_ptr != nullptr) ? 1.0f / (*fp8_out_scale_ptr) : 1.0f;
1721 acc *= inv_global_exp_sum;
1722 acc *= out_scale;
1723 const int64_t query_start_off = static_cast<int64_t>(
1724 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
1725 OUTT* out_ptr = out + query_start_off * num_heads * HEAD_SIZE +
1726 static_cast<int64_t>(head_idx) * HEAD_SIZE;
1727 if constexpr (std::is_same<OUTT, bit8_t>::value) {
1728 out_ptr[threadIdx.x] =
1729 __hip_cvt_float_to_fp8(acc, vllm::fp8::fp8_type::__default_saturation,
1730 vllm::fp8::fp8_type::__default_interpret);
1731 } else {
1732 out_ptr[threadIdx.x] = from_float<scalar_t>(acc);
1733 }
1734 }
1735
1736 #elif defined(__GFX11__)
1737
1738 using floatx8 = __attribute__((__vector_size__(8 * sizeof(float)))) float;
1739
1740 using bit16_t = uint16_t;
1741 using bit16x4 = __attribute__((__vector_size__(4 * sizeof(uint16_t)))) uint16_t;
1742 typedef bit16x4 _B16x4;
1743
1744 using bit16x8 = __attribute__((__vector_size__(8 * sizeof(uint16_t)))) uint16_t;
1745 union b16x8_u {
1746 bit16x8 u16x8;
1747 _B16x4 xy[2];
1748 };
1749 typedef b16x8_u _B16x8;
1750
1751 using bit16x16 =
1752 __attribute__((__vector_size__(16 * sizeof(uint16_t)))) uint16_t;
1753 union b16x16_u {
1754 bit16x16 u16x16;
1755 _B16x8 xy[2];
1756 };
1757 typedef b16x16_u _B16x16;
1758
1759 using _B8x8 = uint2;
1760 using bit8_t = uint8_t;
1761
1762 typedef struct _B8x16 {
1763 _B8x8 xy[2];
1764 } _B8x16;
1765
1766 template <typename T, int absz, int cbid, int blgp>
1767 __device__ __forceinline__ floatx8 gcn_wmma16x16x16_instr(const bit16x16& inpA,
1768 const bit16x16& inpB,
1769 const floatx8& inpC) {
1770 if constexpr (std::is_same<T, _Float16>::value) {
1771 return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(inpA, inpB, inpC);
1772 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
1773 return __builtin_amdgcn_wmma_f32_16x16x16_bf16_w32(inpA, inpB, inpC);
1774 } else {
1775 static_assert(false, "unsupported 16b dtype");
1776 }
1777 }
1778
1779 template <typename T>
1780 __device__ __forceinline__ float to_float(const T& inp) {
1781 if constexpr (std::is_same<T, _Float16>::value) {
1782 return (float)inp;
1783 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
1784 return __bfloat162float(inp);
1785 } else {
1786 static_assert(false, "unsupported 16b dtype");
1787 }
1788 }
1789
1790 template <typename T>
1791 __device__ __forceinline__ T from_float(const float& inp) {
1792 if constexpr (std::is_same<T, _Float16>::value) {
1793 return (_Float16)inp;
1794 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
1795 return __float2bfloat16(inp);
1796 } else {
1797 static_assert(false, "unsupported 16b dtype");
1798 }
1799 }
1800
1801 template <typename T>
1802 __device__ __forceinline__ _B16x8 from_floatx8(const floatx8& inp) {
1803 if constexpr (std::is_same<T, _Float16>::value) {
1804 union h2cvt {
1805 __half2 h2[4];
1806 _B16x8 b16x8;
1807 } u;
1808 u.h2[0] = __float22half2_rn(make_float2(inp[0], inp[1]));
1809 u.h2[1] = __float22half2_rn(make_float2(inp[2], inp[3]));
1810 u.h2[2] = __float22half2_rn(make_float2(inp[4], inp[5]));
1811 u.h2[3] = __float22half2_rn(make_float2(inp[6], inp[7]));
1812 return u.b16x8;
1813 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
1814 union b2cvt {
1815 __hip_bfloat162 b2[4];
1816 _B16x8 b16x8;
1817 } u;
1818
1819 u.b2[0] = __float22bfloat162_rn(make_float2(inp[0], inp[1]));
1820 u.b2[1] = __float22bfloat162_rn(make_float2(inp[2], inp[3]));
1821 u.b2[2] = __float22bfloat162_rn(make_float2(inp[4], inp[5]));
1822 u.b2[3] = __float22bfloat162_rn(make_float2(inp[6], inp[7]));
1823
1824 return u.b16x8;
1825 } else {
1826 static_assert(false, "unsupported 16b dtype");
1827 }
1828 }
1829
1830 // clang-format off
1831 template <typename scalar_t, typename cache_t,
1832 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
1833 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED, int GQA_RATIO,
1834 MFMAType MFMA_TYPE>
1835 __global__
1836 __launch_bounds__(NUM_THREADS, 3) void paged_attention_ll4mi_QKV_mfma16_kernel(
1837 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
1838 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
1839 // head_size/x, block_size, x]
1840 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
1841 // head_size, block_size]
1842 const int num_kv_heads, const float scale,
1843 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
1844 const int* __restrict__ seq_lens, // [num_seqs]
1845 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1846 const int max_num_blocks_per_seq,
1847 const float* __restrict__ alibi_slopes, // [num_heads]
1848 const int q_stride, const int kv_block_stride, const int kv_head_stride,
1849 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
1850 float* __restrict__ max_logits, // [num_seqs, num_heads,
1851 // max_num_partitions]
1852 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions,
1853 // head_size]
1854 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
1855 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
1856 // clang-format on
1857 constexpr int NWARPS = NUM_THREADS / WARP_SIZE; // 8 warps on gfx11
1858 const int warpid = threadIdx.x / WARP_SIZE;
1859 const int laneid = threadIdx.x % WARP_SIZE;
1860 const int lane2id = laneid % 2;
1861 const int lane16id = laneid % 16;
1862 const int rowid = laneid / 16;
1863
1864 const int seq_idx = blockIdx.x;
1865 // NOTE queries with sequence len > 1 are prefills and taken care by another
1866 // kernel.
1867 if (query_start_loc_ptr != nullptr &&
1868 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx]) != 1) {
1869 return;
1870 }
1871
1872 const int partition_idx = blockIdx.y;
1873
1874 constexpr int T_PAR_SIZE = 256; // token partition size set to 256
1875
1876 const int max_num_partitions = gridDim.y;
1877
1878 const int seq_len = seq_lens[seq_idx]; // length of a seq
1879
1880 const int partition_start_token_idx = partition_idx * T_PAR_SIZE;
1881 // exit if partition is out of context for seq
1882 if (partition_start_token_idx >= seq_len) {
1883 return;
1884 }
1885
1886 constexpr int GQA_RATIO2 = DIVIDE_ROUND_UP(GQA_RATIO, 2);
1887
1888 __shared__ float shared_qk_max[NWARPS][16 + 1];
1889 __shared__ float shared_exp_sum[NWARPS][16 + 1];
1890 // shared_logits is used for multiple purposes
1891 __shared__ _B16x16 shared_logits[NWARPS][2][16][2];
1892
1893 // for QK wmma16x16, layout is QHead/Tokenx16 across every 16 lanes,
1894 // 32 Bytes HeadElements in each lane, 2x16B HeadElements across a row of warp
1895 constexpr int ROWS_PER_WARP =
1896 WARP_SIZE / 16 / 2; // rows refers to 16 lanes; refer dpp terminology
1897 constexpr int CONTIGUOUS_KV_ELEMS_16B_LOAD =
1898 16 / sizeof(cache_t); // 8 for 16 bit cache type, 16 for 8 bit types
1899 constexpr int QKHE_PER_FETCH =
1900 CONTIGUOUS_KV_ELEMS_16B_LOAD *
1901 ROWS_PER_WARP; // each fetch across a warp fetches these many elements
1902 constexpr int QKHELOOP = HEAD_SIZE / QKHE_PER_FETCH; // 2xQKHE_16B across
1903 // warp
1904
1905 _B16x16 Qlocal[QKHELOOP / 2]; // note that 16 contiguous elements of Q should
1906 // be fetched per lane for 16 bit cache types
1907
1908 constexpr int CONTIGUOUS_SCALAR_ELEMS_16B = 16 / sizeof(scalar_t);
1909
1910 constexpr int TOKENS_PER_WARP =
1911 T_PAR_SIZE /
1912 NWARPS; // sub partition of tokens per warp for qk calculation
1913 constexpr int TLOOP =
1914 TOKENS_PER_WARP /
1915 16; // each wmma16x16x16 instruction processes 16 tokens
1916
1917 _B16x16 Klocal[TLOOP]
1918 [QKHELOOP / 2]; // can be interpreted as B8x16 for 8 bit types
1919
1920 const int wg_start_head_idx = blockIdx.z * GQA_RATIO;
1921 const int wg_start_kv_head_idx = blockIdx.z;
1922 const int total_num_heads = gridDim.z * GQA_RATIO;
1923
1924 // for QK wmma, tokens in multiples of TOKENS_PER_WARP are spread across warps
1925 // each wmma takes QH16xT16x16HE across warp
1926 // repeat wmma across QKHELOOP dimension
1927 // output layout from QKwmma : QH16xT8x2 16 qheads across 16 lanes, 16 tokens
1928 // across 2 rows x 8 tokens per lane
1929
1930 const int64_t query_start_off = static_cast<int64_t>(
1931 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
1932
1933 if (GQA_RATIO == 1) {
1934 const int local_qhead_idx = lane16id % GQA_RATIO;
1935 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
1936 const scalar_t* q_ptr =
1937 q + query_start_off * q_stride + global_qhead_idx * HEAD_SIZE;
1938 if (lane16id < GQA_RATIO) {
1939 #pragma unroll
1940 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP / 2; qkhe_depth++) {
1941 const scalar_t* q_fetch_ptr = q_ptr + qkhe_depth * QKHE_PER_FETCH * 2;
1942 const _B16x16* q_fetch_ptr_32B =
1943 reinterpret_cast<const _B16x16*>(q_fetch_ptr);
1944 Qlocal[qkhe_depth] = *q_fetch_ptr_32B;
1945 }
1946 }
1947 } else {
1948 // fetch Q in shared across warps and then write to registers
1949 const int local_qhead_idx = 2 * warpid + rowid;
1950 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
1951 const scalar_t* q_ptr =
1952 q + query_start_off * q_stride + global_qhead_idx * HEAD_SIZE;
1953
1954 const int qhead_element = lane16id * CONTIGUOUS_SCALAR_ELEMS_16B;
1955 if ((local_qhead_idx < GQA_RATIO) && (qhead_element < HEAD_SIZE)) {
1956 const scalar_t* q_fetch_ptr = q_ptr + qhead_element;
1957 const _B16x8* q_fetch_ptr_16B =
1958 reinterpret_cast<const _B16x8*>(q_fetch_ptr);
1959 _B16x8 tmp = *q_fetch_ptr_16B;
1960
1961 const int offset1 =
1962 lane16id /
1963 2; // 16 contiguous chunks of head elems are spread across 8x2lanes
1964 shared_logits[offset1][lane2id][local_qhead_idx][0].xy[0] = tmp;
1965 }
1966
1967 __syncthreads();
1968
1969 #pragma unroll
1970 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP / 2; qkhe_depth++) {
1971 Qlocal[qkhe_depth].xy[0] =
1972 shared_logits[qkhe_depth][0][lane16id % GQA_RATIO][0].xy[0];
1973 Qlocal[qkhe_depth].xy[1] =
1974 shared_logits[qkhe_depth][1][lane16id % GQA_RATIO][0].xy[0];
1975 }
1976 }
1977
1978 const int num_seq_blocks = DIVIDE_ROUND_UP(seq_len, BLOCK_SIZE);
1979 const int last_seq_block = num_seq_blocks - 1;
1980
1981 const int* block_table_seq = block_tables + seq_idx * max_num_blocks_per_seq;
1982
1983 int kphysical_block_number[TLOOP];
1984
1985 // fetch k physical block numbers
1986 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
1987 const int klocal_token_idx =
1988 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
1989 const int kglobal_token_idx = partition_start_token_idx + klocal_token_idx;
1990 const int kblock_idx = (kglobal_token_idx < seq_len)
1991 ? kglobal_token_idx / BLOCK_SIZE
1992 : last_seq_block;
1993 kphysical_block_number[token_depth] = block_table_seq[kblock_idx];
1994 }
1995
1996 constexpr int KX = 16 / sizeof(cache_t);
1997 const cache_t* k_ptr = k_cache + wg_start_kv_head_idx * kv_head_stride;
1998
1999 const int row_head_elem = 0;
2000
2001 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2002 const int64_t kblock_number =
2003 static_cast<int64_t>(kphysical_block_number[token_depth]);
2004 const cache_t* k_ptr2 = k_ptr + kblock_number * kv_block_stride;
2005 const int klocal_token_idx =
2006 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
2007 const int kphysical_block_offset = klocal_token_idx % BLOCK_SIZE;
2008 const cache_t* k_ptr3 = k_ptr2 + kphysical_block_offset * KX;
2009
2010 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
2011 const int head_elem = row_head_elem + qkhe_depth * QKHE_PER_FETCH;
2012 const int offset1 = head_elem / KX;
2013 const int offset2 = head_elem % KX;
2014 const cache_t* k_fetch_ptr = k_ptr3 + offset1 * BLOCK_SIZE * KX + offset2;
2015 const _B16x8* k_fetch_ptr_16B =
2016 reinterpret_cast<const _B16x8*>(k_fetch_ptr);
2017 Klocal[token_depth][qkhe_depth / 2].xy[qkhe_depth % 2] = *k_fetch_ptr_16B;
2018 }
2019 }
2020
2021 constexpr int VTOKENS_PER_LANE =
2022 TOKENS_PER_WARP / ROWS_PER_WARP; // 32/1 = 32 vtokens per lane
2023 constexpr int VBLOCKS_PER_LANE = 2; // assumes block size >=16
2024 constexpr int VTLOOP = NWARPS; // corresponds to tokens across warps
2025 constexpr int VTLANELOOP = DIVIDE_ROUND_UP(
2026 VTOKENS_PER_LANE,
2027 CONTIGUOUS_KV_ELEMS_16B_LOAD); // optimized for 16B fetches; assumes
2028 // minimum block size is 16
2029 constexpr int VHELOOP = DIVIDE_ROUND_UP(
2030 (HEAD_SIZE / 16), NWARPS); // head_size distributed across warps; each
2031 // wmma instr works on 16 head elements
2032
2033 int vphysical_block_number[VTLOOP][VBLOCKS_PER_LANE];
2034
2035 // fetch v physical block numbers
2036 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2037 for (int vblock_depth = 0; vblock_depth < VBLOCKS_PER_LANE;
2038 vblock_depth++) {
2039 const int vlocal_token_idx =
2040 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
2041 vblock_depth * BLOCK_SIZE;
2042 const int vglobal_token_idx =
2043 partition_start_token_idx + vlocal_token_idx;
2044 const int vblock_idx = (vglobal_token_idx < seq_len)
2045 ? vglobal_token_idx / BLOCK_SIZE
2046 : last_seq_block;
2047 vphysical_block_number[vtoken_depth][vblock_depth] =
2048 block_table_seq[vblock_idx];
2049 }
2050 }
2051
2052 _B16x16 Vlocal[VTLOOP][VHELOOP]
2053 [VTLANELOOP / 2]; // this can be interpreted as B8x16 too
2054
2055 const cache_t* v_ptr = v_cache + wg_start_kv_head_idx * kv_head_stride;
2056 // v fetches are 16head elems across lanes x (16x2) tokens per lane
2057 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2058 const int vhead_elem = vhe_depth * NWARPS * 16 + warpid * 16 + lane16id;
2059 const cache_t* v_ptr2 = v_ptr + vhead_elem * BLOCK_SIZE;
2060
2061 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2062 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
2063 const int64_t vblock_number = static_cast<int64_t>(
2064 vphysical_block_number[vtoken_depth]
2065 [vfetch_depth / VBLOCKS_PER_LANE]);
2066 const cache_t* v_ptr3 = v_ptr2 + (vblock_number * kv_block_stride);
2067
2068 const cache_t* v_fetch_ptr =
2069 v_ptr3 +
2070 (vfetch_depth % VBLOCKS_PER_LANE) * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2071 const _B16x8* v_fetch_ptr_16B =
2072 reinterpret_cast<const _B16x8*>(v_fetch_ptr);
2073 Vlocal[vtoken_depth][vhe_depth][vfetch_depth / 2].xy[vfetch_depth % 2] =
2074 *v_fetch_ptr_16B;
2075 }
2076 }
2077 }
2078
2079 if (partition_start_token_idx + T_PAR_SIZE > seq_len) {
2080 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2081 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2082 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
2083 const int vglobal_fetch_start =
2084 partition_start_token_idx +
2085 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
2086 vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2087 Vlocal[vtoken_depth][vhe_depth][vfetch_depth / 2]
2088 .xy[vfetch_depth % 2] = mask_v_cache_padding<cache_t>(
2089 Vlocal[vtoken_depth][vhe_depth][vfetch_depth / 2]
2090 .xy[vfetch_depth % 2],
2091 vglobal_fetch_start, seq_len);
2092 }
2093 }
2094 }
2095 }
2096
2097 floatx8 dout[TLOOP];
2098 // qk wmma
2099 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2100 dout[token_depth] = {0};
2101 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP / 2; qkhe_depth++) {
2102 dout[token_depth] = gcn_wmma16x16x16_instr<scalar_t, 0, 0, 0>(
2103 Klocal[token_depth][qkhe_depth].u16x16, Qlocal[qkhe_depth].u16x16,
2104 dout[token_depth]);
2105 }
2106 dout[token_depth] *= scale;
2107 }
2108
2109 // calculate qk_max and exp_sum per warp and write to shared memory
2110 float qk_max = -FLT_MAX;
2111 float exp_sum = 0.0f;
2112 const int qkout_token_idx =
2113 partition_start_token_idx + TOKENS_PER_WARP * warpid + rowid;
2114 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2115 const int local_token_idx = qkout_token_idx + token_depth * 16;
2116 for (int i = 0; i < 8; i++) {
2117 const float tmp =
2118 (local_token_idx + 2 * i < seq_len) ? dout[token_depth][i] : -FLT_MAX;
2119 qk_max = fmaxf(qk_max, tmp);
2120 }
2121 }
2122
2123 qk_max = fmaxf(qk_max, __shfl_xor(qk_max, 16));
2124
2125 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2126 const int local_token_idx = qkout_token_idx + token_depth * 16;
2127 for (int i = 0; i < 8; i++) {
2128 const float tmp = (local_token_idx + 2 * i < seq_len)
2129 ? __expf(dout[token_depth][i] - qk_max)
2130 : 0.0f;
2131 dout[token_depth][i] = tmp;
2132 exp_sum += tmp;
2133 }
2134 }
2135
2136 exp_sum += __shfl_xor(exp_sum, 16);
2137
2138 __syncthreads();
2139
2140 if (laneid < 16) {
2141 shared_qk_max[warpid][lane16id] = qk_max;
2142 shared_exp_sum[warpid][lane16id] = exp_sum;
2143 }
2144
2145 __syncthreads();
2146
2147 // calculate partition qk_max and exp_sum
2148 float partition_qk_max = -FLT_MAX;
2149 float warp_qk_max_exp[NWARPS];
2150 float partition_exp_sum = 0.0f;
2151
2152 #pragma unroll
2153 for (int w = 0; w < NWARPS; w++) {
2154 warp_qk_max_exp[w] = shared_qk_max[w][lane16id];
2155 partition_qk_max = fmaxf(partition_qk_max, warp_qk_max_exp[w]);
2156 }
2157
2158 for (int w = 0; w < NWARPS; w++) {
2159 warp_qk_max_exp[w] = __expf(warp_qk_max_exp[w] - partition_qk_max);
2160 partition_exp_sum += shared_exp_sum[w][lane16id] * warp_qk_max_exp[w];
2161 }
2162
2163 const float inv_sum_scale =
2164 __fdividef(1.f, partition_exp_sum + 1e-6f) * warp_qk_max_exp[warpid];
2165
2166 __syncthreads();
2167
2168 // write logits to shared mem
2169 #pragma unroll
2170 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2171 dout[token_depth] *= inv_sum_scale;
2172 shared_logits[warpid][token_depth][lane16id][0].xy[rowid] =
2173 from_floatx8<scalar_t>(dout[token_depth]);
2174 }
2175 __syncthreads();
2176
2177 _B16x8 swp_buf[TLOOP][2];
2178 #pragma unroll
2179 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2180 swp_buf[token_depth][0] =
2181 shared_logits[warpid][token_depth][lane16id][0].xy[0];
2182 swp_buf[token_depth][1] =
2183 shared_logits[warpid][token_depth][lane16id][0].xy[1];
2184 }
2185
2186 #pragma unroll
2187 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2188 #pragma unroll
2189 for (int i = 0; i < 8; i++) {
2190 shared_logits[warpid][token_depth][lane16id][0].xy[rowid].u16x8[i] =
2191 swp_buf[token_depth][i % 2].u16x8[4 * rowid + (i / 2)];
2192 }
2193 }
2194
2195 // write out partition max_logits and exp_sum
2196 if (threadIdx.x < GQA_RATIO) {
2197 const int qhead_idx = lane16id;
2198 const int offset = seq_idx * total_num_heads * max_num_partitions +
2199 (wg_start_head_idx + qhead_idx) * max_num_partitions +
2200 partition_idx;
2201 max_logits[offset] = partition_qk_max;
2202 exp_sums[offset] = partition_exp_sum;
2203 }
2204
2205 __syncthreads();
2206
2207 _B16x8 outelems[VHELOOP];
2208 // Softmax V wmma
2209 // v layout: 16he across lanes x (16x2) tokens per lane
2210 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2211 floatx8 tmp_out = {0};
2212 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2213 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP / 2;
2214 vfetch_depth++) {
2215 const int offset = vfetch_depth;
2216 // if output format is 16 qheads across 16 lanes, 16 head elems spread
2217 // across rows
2218 tmp_out = gcn_wmma16x16x16_instr<scalar_t, 0, 0, 0>(
2219 Vlocal[vtoken_depth][vhe_depth][vfetch_depth].u16x16,
2220 shared_logits[vtoken_depth][offset][lane16id][0].u16x16, tmp_out);
2221 }
2222 }
2223 outelems[vhe_depth] = from_floatx8<scalar_t>(tmp_out);
2224 }
2225
2226 __syncthreads();
2227
2228 #pragma unroll
2229 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2230 shared_logits[warpid][vhe_depth][lane16id][0].xy[rowid] =
2231 outelems[vhe_depth]; // lane16 id head dimension; rowid head element
2232 // dimension
2233 }
2234
2235 __syncthreads();
2236
2237 #pragma unroll
2238 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2239 swp_buf[vhe_depth][0] = shared_logits[warpid][vhe_depth][lane16id][0].xy[0];
2240 swp_buf[vhe_depth][1] = shared_logits[warpid][vhe_depth][lane16id][0].xy[1];
2241 }
2242
2243 #pragma unroll
2244 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2245 #pragma unroll
2246 for (int i = 0; i < 8; i++) {
2247 shared_logits[warpid][vhe_depth][lane16id][0].xy[rowid].u16x8[i] =
2248 swp_buf[vhe_depth][i % 2].u16x8[4 * rowid + (i / 2)];
2249 }
2250 }
2251
2252 __syncthreads();
2253
2254 // write to tmp_out with coalesced writes after reading from shared mem
2255 if (warpid == 0) {
2256 _B16x8 vout[GQA_RATIO2];
2257 // each lane writes out 16Bytes of tmp_out along head elem dimension
2258 const int head_elem_idx = lane16id * 8;
2259 if (head_elem_idx < HEAD_SIZE) {
2260 for (int h = 0; h < GQA_RATIO2; h++) {
2261 const int local_head_idx = 2 * h + rowid;
2262 const int offset1 = (head_elem_idx / 16) % NWARPS;
2263 const int offset2 = head_elem_idx / 16 / NWARPS;
2264 const int offset3 = (head_elem_idx / 8) % 2; // num_he % num_row
2265 vout[h] =
2266 shared_logits[offset1][offset2][local_head_idx][0].xy[offset3];
2267 }
2268
2269 const int hsz_maxp_mult = HEAD_SIZE * max_num_partitions;
2270 scalar_t* out_ptr = out + seq_idx * total_num_heads * hsz_maxp_mult +
2271 partition_idx * HEAD_SIZE;
2272 for (int h = 0; h < GQA_RATIO2; h++) {
2273 const int local_head_idx = 2 * h + rowid;
2274 if (local_head_idx < GQA_RATIO) {
2275 const int out_head_idx = wg_start_head_idx + local_head_idx;
2276 scalar_t* out_ptr2 = out_ptr + out_head_idx * hsz_maxp_mult;
2277 scalar_t* out_ptr3 = out_ptr2 + head_elem_idx;
2278 _B16x8* out_ptr_B16x8 = reinterpret_cast<_B16x8*>(out_ptr3);
2279 *out_ptr_B16x8 = vout[h];
2280 }
2281 }
2282 }
2283 }
2284 }
2285
2286 template <typename scalar_t, typename cache_t,
2287 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
2288 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED,
2289 int GQA_RATIO>
2290 __global__
2291 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
2292 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
2293 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
2294 // head_size/x, block_size, x]
2295 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
2296 // head_size, block_size]
2297 const int num_kv_heads, const float scale,
2298 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
2299 const int* __restrict__ seq_lens, // [num_seqs]
2300 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
2301 const int max_num_blocks_per_seq,
2302 const float* __restrict__ alibi_slopes, // [num_heads]
2303 const int q_stride, const int kv_block_stride, const int kv_head_stride,
2304 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
2305 float* __restrict__ max_logits, // [num_seqs, num_heads,
2306 // max_num_partitions]
2307 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions,
2308 // head_size]
2309 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
2310 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
2311 UNREACHABLE_CODE
2312 }
2313
2314 // Grid: (num_heads, num_seqs).
2315 template <typename scalar_t, typename OUTT, int HEAD_SIZE, int NUM_THREADS,
2316 int PARTITION_SIZE, int NPAR_LOOPS>
2317 __global__
2318 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
2319 OUTT* __restrict__ out, // [num_seqs, num_heads, head_size]
2320 const float* __restrict__ exp_sums, // [num_seqs, num_heads,
2321 // max_num_partitions]
2322 const float* __restrict__ max_logits, // [num_seqs, num_heads,
2323 // max_num_partitions]
2324 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
2325 // max_num_partitions, head_size]
2326 const int* __restrict__ seq_lens, // [num_seqs]
2327 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
2328 const int max_num_partitions, const float* __restrict__ fp8_out_scale_ptr) {
2329 const auto num_heads = gridDim.x;
2330 const auto head_idx = blockIdx.x;
2331 const auto seq_idx = blockIdx.y;
2332
2333 // NOTE queries with sequence len > 1 are prefills and taken care by another
2334 // kernel.
2335 if (query_start_loc_ptr != nullptr &&
2336 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
2337 return;
2338 }
2339
2340 const int seq_len = seq_lens[seq_idx];
2341 const int num_partitions = DIVIDE_ROUND_UP(seq_len, PARTITION_SIZE);
2342 const int warpid = threadIdx.x / WARP_SIZE;
2343
2344 __shared__ float shared_global_exp_sum;
2345 // max num partitions supported is warp_size * NPAR_LOOPS
2346 __shared__ float shared_exp_sums[NPAR_LOOPS * WARP_SIZE];
2347
2348 if (warpid == 0) {
2349 const float* max_logits_ptr = max_logits +
2350 seq_idx * num_heads * max_num_partitions +
2351 head_idx * max_num_partitions;
2352
2353 // valid partition is the last valid partition in case threadid > num
2354 // partitions
2355 int valid_partition[NPAR_LOOPS];
2356 float reg_max_logit[NPAR_LOOPS];
2357 const int last_valid_partition = num_partitions - 1;
2358
2359 #pragma unroll
2360 for (int i = 0; i < NPAR_LOOPS; i++) {
2361 const int partition_no = i * WARP_SIZE + threadIdx.x;
2362 valid_partition[i] =
2363 (partition_no < num_partitions) ? partition_no : last_valid_partition;
2364 }
2365 #pragma unroll
2366 for (int i = 0; i < NPAR_LOOPS; i++) {
2367 reg_max_logit[i] = max_logits_ptr[valid_partition[i]];
2368 }
2369 float max_logit = reg_max_logit[0];
2370 #pragma unroll
2371 for (int i = 1; i < NPAR_LOOPS; i++) {
2372 max_logit = fmaxf(max_logit, reg_max_logit[i]);
2373 }
2374
2375 #pragma unroll
2376 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
2377 max_logit = fmaxf(max_logit, __shfl_xor(max_logit, mask));
2378 }
2379
2380 const float* exp_sums_ptr = exp_sums +
2381 seq_idx * num_heads * max_num_partitions +
2382 head_idx * max_num_partitions;
2383
2384 float rescaled_exp_sum[NPAR_LOOPS];
2385 #pragma unroll
2386 for (int i = 0; i < NPAR_LOOPS; i++) {
2387 rescaled_exp_sum[i] = exp_sums_ptr[valid_partition[i]];
2388 }
2389 #pragma unroll
2390 for (int i = 0; i < NPAR_LOOPS; i++) {
2391 const int partition_no = i * WARP_SIZE + threadIdx.x;
2392 rescaled_exp_sum[i] *= (partition_no < num_partitions)
2393 ? expf(reg_max_logit[i] - max_logit)
2394 : 0.0f;
2395 }
2396 float global_exp_sum = rescaled_exp_sum[0];
2397 #pragma unroll
2398 for (int i = 1; i < NPAR_LOOPS; i++) {
2399 global_exp_sum += rescaled_exp_sum[i];
2400 }
2401 #pragma unroll
2402 for (int i = 0; i < NPAR_LOOPS; i++) {
2403 const int partition_no = i * WARP_SIZE + threadIdx.x;
2404 shared_exp_sums[partition_no] = rescaled_exp_sum[i];
2405 }
2406
2407 #pragma unroll
2408 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
2409 global_exp_sum += __shfl_xor(global_exp_sum, mask);
2410 }
2411 if (threadIdx.x == 0) {
2412 shared_global_exp_sum = global_exp_sum;
2413 }
2414 } // warpid == 0
2415 const scalar_t* tmp_out_ptr =
2416 tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
2417 head_idx * max_num_partitions * HEAD_SIZE + threadIdx.x;
2418 constexpr int MAX_NPAR = 32;
2419 scalar_t tmps[MAX_NPAR];
2420 const float dzero = 0.0f;
2421 #pragma unroll
2422 for (int j = 0; j < MAX_NPAR; j++) {
2423 tmps[j] = from_float<scalar_t>(dzero);
2424 }
2425 const int last_partition_offset = (num_partitions - 1) * HEAD_SIZE;
2426 const int num_partition_offset = (num_partitions)*HEAD_SIZE;
2427 int idx = 0;
2428
2429 constexpr int JCHUNK = 16;
2430
2431 #pragma unroll
2432 for (int j = 0; j < JCHUNK * HEAD_SIZE; j += HEAD_SIZE) {
2433 // lastj is last valid partition
2434 const int lastj_offset =
2435 (j < num_partition_offset) ? j : last_partition_offset;
2436 tmps[idx] = tmp_out_ptr[lastj_offset];
2437 idx++;
2438 }
2439 __syncthreads();
2440
2441 if (num_partitions > JCHUNK) {
2442 #pragma unroll
2443 for (int j = JCHUNK * HEAD_SIZE; j < 2 * JCHUNK * HEAD_SIZE;
2444 j += HEAD_SIZE) {
2445 const int lastj_offset =
2446 (j < num_partition_offset) ? j : last_partition_offset;
2447 tmps[idx] = tmp_out_ptr[lastj_offset];
2448 idx++;
2449 }
2450
2451 if (num_partitions > 2 * JCHUNK) {
2452 #pragma unroll
2453 for (int j = 2 * JCHUNK * HEAD_SIZE; j < MAX_NPAR * HEAD_SIZE;
2454 j += HEAD_SIZE) {
2455 const int lastj_offset =
2456 (j < num_partition_offset) ? j : last_partition_offset;
2457 tmps[idx] = tmp_out_ptr[lastj_offset];
2458 idx++;
2459 }
2460 }
2461 } // num_partitions > JCHUNK
2462
2463 // Aggregate tmp_out to out.
2464 float acc = 0.0f;
2465 #pragma unroll
2466 for (int j = 0; j < JCHUNK; j++) {
2467 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
2468 }
2469 if (num_partitions > JCHUNK) {
2470 #pragma unroll
2471 for (int j = JCHUNK; j < 2 * JCHUNK; j++) {
2472 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
2473 }
2474 if (num_partitions > 2 * JCHUNK) {
2475 #pragma unroll
2476 for (int j = 2 * JCHUNK; j < MAX_NPAR; j++) {
2477 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
2478 }
2479 }
2480 }
2481
2482 for (int p = 1; p < NPAR_LOOPS; p++) {
2483 if (num_partitions > p * MAX_NPAR) {
2484 idx = 0;
2485 #pragma unroll
2486 for (int j = p * MAX_NPAR * HEAD_SIZE; j < (p + 1) * MAX_NPAR * HEAD_SIZE;
2487 j += HEAD_SIZE) {
2488 // lastj is last valid partition
2489 const int lastj_offset =
2490 (j < num_partition_offset) ? j : last_partition_offset;
2491 tmps[idx] = tmp_out_ptr[lastj_offset];
2492 idx++;
2493 }
2494
2495 #pragma unroll
2496 for (int j = 0; j < MAX_NPAR; j++) {
2497 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j + p * MAX_NPAR];
2498 }
2499 }
2500 }
2501
2502 const float inv_global_exp_sum =
2503 __fdividef(1.0f, shared_global_exp_sum + 1e-6f);
2504 acc *= inv_global_exp_sum;
2505
2506 const int64_t query_start_off = static_cast<int64_t>(
2507 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
2508 OUTT* out_ptr = out + query_start_off * num_heads * HEAD_SIZE +
2509 static_cast<int64_t>(head_idx) * HEAD_SIZE;
2510 out_ptr[threadIdx.x] = from_float<scalar_t>(acc);
2511 }
2512
2513 #elif defined(__GFX12__)
2514
2515 using floatx8 = __attribute__((__vector_size__(8 * sizeof(float)))) float;
2516
2517 using bit16_t = uint16_t;
2518 using bit16x4 = __attribute__((__vector_size__(4 * sizeof(uint16_t)))) uint16_t;
2519 typedef bit16x4 _B16x4;
2520
2521 using bit16x8 = __attribute__((__vector_size__(8 * sizeof(uint16_t)))) uint16_t;
2522 union b16x8_u {
2523 bit16x8 u16x8;
2524 _B16x4 xy[2];
2525 };
2526 typedef b16x8_u _B16x8;
2527
2528 using _B8x8 = uint2;
2529 using bit8_t = uint8_t;
2530
2531 typedef struct _B8x16 {
2532 _B8x8 xy[2];
2533 } _B8x16;
2534
2535 template <typename T, int absz, int cbid, int blgp>
2536 __device__ __forceinline__ floatx8 gcn_wmma16x16x16_instr(const bit16x8& inpA,
2537 const bit16x8& inpB,
2538 const floatx8& inpC) {
2539 #if defined(__gfx1250__)
2540 // gfx1250 (gfx12 family) does not provide the gfx12 WMMA variant used by
2541 // gfx1200/1201 (needs wmma-128b-insts). This custom-attention WMMA path is
2542 // unsupported on gfx1250; trap if ever launched (fail loud, not
2543 // silent-wrong).
2544 (void)inpA;
2545 (void)inpB;
2546 __builtin_trap();
2547 return inpC;
2548 #else
2549 if constexpr (std::is_same<T, _Float16>::value) {
2550 return __builtin_amdgcn_wmma_f32_16x16x16_f16_w32_gfx12(inpA, inpB, inpC);
2551 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
2552 return __builtin_amdgcn_wmma_f32_16x16x16_bf16_w32_gfx12(inpA, inpB, inpC);
2553 } else {
2554 static_assert(false, "unsupported 16b dtype");
2555 }
2556 #endif
2557 }
2558
2559 template <typename T>
2560 __device__ __forceinline__ float to_float(const T& inp) {
2561 if constexpr (std::is_same<T, _Float16>::value) {
2562 return (float)inp;
2563 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
2564 return __bfloat162float(inp);
2565 } else {
2566 static_assert(false, "unsupported 16b dtype");
2567 }
2568 }
2569
2570 template <typename T>
2571 __device__ __forceinline__ T from_float(const float& inp) {
2572 if constexpr (std::is_same<T, _Float16>::value) {
2573 return (_Float16)inp;
2574 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
2575 return __float2bfloat16(inp);
2576 } else {
2577 static_assert(false, "unsupported 16b dtype");
2578 }
2579 }
2580
2581 template <typename T>
2582 __device__ __forceinline__ _B16x8 from_floatx8(const floatx8& inp) {
2583 if constexpr (std::is_same<T, _Float16>::value) {
2584 union h2cvt {
2585 __half2 h2[4];
2586 _B16x8 b16x8;
2587 } u;
2588 u.h2[0] = __float22half2_rn(make_float2(inp[0], inp[1]));
2589 u.h2[1] = __float22half2_rn(make_float2(inp[2], inp[3]));
2590 u.h2[2] = __float22half2_rn(make_float2(inp[4], inp[5]));
2591 u.h2[3] = __float22half2_rn(make_float2(inp[6], inp[7]));
2592 return u.b16x8;
2593 } else if constexpr (std::is_same<T, __hip_bfloat16>::value) {
2594 union b2cvt {
2595 __hip_bfloat162 b2[4];
2596 _B16x8 b16x8;
2597 } u;
2598
2599 u.b2[0] = __float22bfloat162_rn(make_float2(inp[0], inp[1]));
2600 u.b2[1] = __float22bfloat162_rn(make_float2(inp[2], inp[3]));
2601 u.b2[2] = __float22bfloat162_rn(make_float2(inp[4], inp[5]));
2602 u.b2[3] = __float22bfloat162_rn(make_float2(inp[6], inp[7]));
2603
2604 return u.b16x8;
2605 } else {
2606 static_assert(false, "unsupported 16b dtype");
2607 }
2608 }
2609
2610 // clang-format off
2611 template <typename scalar_t, typename cache_t,
2612 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
2613 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED, int GQA_RATIO,
2614 MFMAType MFMA_TYPE>
2615 __global__
2616 __launch_bounds__(NUM_THREADS, 3) void paged_attention_ll4mi_QKV_mfma16_kernel(
2617 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
2618 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
2619 // head_size/x, block_size, x]
2620 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
2621 // head_size, block_size]
2622 const int num_kv_heads, const float scale,
2623 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
2624 const int* __restrict__ seq_lens, // [num_seqs]
2625 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
2626 const int max_num_blocks_per_seq,
2627 const float* __restrict__ alibi_slopes, // [num_heads]
2628 const int q_stride, const int kv_block_stride, const int kv_head_stride,
2629 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
2630 float* __restrict__ max_logits, // [num_seqs, num_heads,
2631 // max_num_partitions]
2632 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions,
2633 // head_size]
2634 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
2635 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
2636 // clang-format on
2637 constexpr int NWARPS = NUM_THREADS / WARP_SIZE; // 8 warps on gfx11
2638 const int warpid = threadIdx.x / WARP_SIZE;
2639 const int laneid = threadIdx.x % WARP_SIZE;
2640 const int lane2id = laneid % 2;
2641 const int lane16id = laneid % 16;
2642 const int rowid = laneid / 16;
2643
2644 const int seq_idx = blockIdx.x;
2645 // NOTE queries with sequence len > 1 are prefills and taken care by another
2646 // kernel.
2647 if (query_start_loc_ptr != nullptr &&
2648 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
2649 return;
2650 }
2651 const int partition_idx = blockIdx.y;
2652
2653 constexpr int T_PAR_SIZE = 256; // token partition size set to 256
2654
2655 const int max_num_partitions = gridDim.y;
2656
2657 const int seq_len = seq_lens[seq_idx]; // length of a seq
2658
2659 const int partition_start_token_idx = partition_idx * T_PAR_SIZE;
2660 // exit if partition is out of context for seq
2661 if (partition_start_token_idx >= seq_len) {
2662 return;
2663 }
2664
2665 constexpr int GQA_RATIO2 = DIVIDE_ROUND_UP(GQA_RATIO, 2);
2666
2667 __shared__ float shared_qk_max[NWARPS][16 + 1];
2668 __shared__ float shared_exp_sum[NWARPS][16 + 1];
2669 // shared_logits is used for multiple purposes
2670 __shared__ _B16x8 shared_logits[NWARPS][2][16][2];
2671
2672 // for QK wmma16x16_gfx12, layout is QHead/Tokenx16 across every 16 lanes,
2673 // 16 Bytes HeadElements in each lane, 2x16B HeadElements across 2 rows of
2674 // warp
2675 constexpr int ROWS_PER_WARP =
2676 WARP_SIZE / 16; // rows refers to 16 lanes; refer dpp terminology
2677 constexpr int CONTIGUOUS_KV_ELEMS_16B_LOAD =
2678 16 / sizeof(cache_t); // 8 for 16 bit cache type, 16 for 8 bit types
2679 constexpr int QKHE_PER_FETCH =
2680 CONTIGUOUS_KV_ELEMS_16B_LOAD *
2681 ROWS_PER_WARP; // each fetch across a warp fetches these many elements
2682 constexpr int QKHELOOP = HEAD_SIZE / QKHE_PER_FETCH; // 2xQKHE_16B across
2683 // warp
2684
2685 _B16x8 Qlocal[QKHELOOP]; // note that 16 contiguous elements of Q should
2686 // be fetched per lane for 16 bit cache types
2687
2688 constexpr int CONTIGUOUS_SCALAR_ELEMS_16B = 16 / sizeof(scalar_t);
2689
2690 constexpr int TOKENS_PER_WARP =
2691 T_PAR_SIZE /
2692 NWARPS; // sub partition of tokens per warp for qk calculation
2693 constexpr int TLOOP =
2694 TOKENS_PER_WARP /
2695 16; // each wmma16x16x16 instruction processes 16 tokens
2696
2697 _B16x8 Klocal[TLOOP]
2698 [QKHELOOP]; // can be interpreted as B8x16 for 8 bit types
2699
2700 const int wg_start_head_idx = blockIdx.z * GQA_RATIO;
2701 const int wg_start_kv_head_idx = blockIdx.z;
2702 const int total_num_heads = gridDim.z * GQA_RATIO;
2703
2704 // for QK wmma, tokens in multiples of TOKENS_PER_WARP are spread across warps
2705 // each wmma takes QH16xT16x16HE across warp
2706 // repeat wmma across QKHELOOP dimension
2707 // output layout from QKwmma : QH16xT8x2 16 qheads across 16 lanes, 16 tokens
2708 // across 2 rows x 8 tokens per lane
2709
2710 const int64_t query_start_off = static_cast<int64_t>(
2711 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
2712
2713 if (GQA_RATIO == 1) {
2714 const int local_qhead_idx = lane16id % GQA_RATIO;
2715 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
2716 const scalar_t* q_ptr = q + query_start_off * q_stride +
2717 global_qhead_idx * HEAD_SIZE +
2718 rowid * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2719 if (lane16id < GQA_RATIO) {
2720 #pragma unroll
2721 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
2722 const scalar_t* q_fetch_ptr = q_ptr + qkhe_depth * QKHE_PER_FETCH;
2723 const _B16x8* q_fetch_ptr_16B =
2724 reinterpret_cast<const _B16x8*>(q_fetch_ptr);
2725 Qlocal[qkhe_depth] = *q_fetch_ptr_16B;
2726 }
2727 }
2728 } else {
2729 // fetch Q in shared across warps and then write to registers
2730 const int local_qhead_idx = 2 * warpid + rowid;
2731 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
2732 const scalar_t* q_ptr =
2733 q + query_start_off * q_stride + global_qhead_idx * HEAD_SIZE;
2734
2735 const int qhead_element = lane16id * CONTIGUOUS_SCALAR_ELEMS_16B;
2736 if ((local_qhead_idx < GQA_RATIO) && (qhead_element < HEAD_SIZE)) {
2737 const scalar_t* q_fetch_ptr = q_ptr + qhead_element;
2738 const _B16x8* q_fetch_ptr_16B =
2739 reinterpret_cast<const _B16x8*>(q_fetch_ptr);
2740 _B16x8 tmp = *q_fetch_ptr_16B;
2741
2742 const int offset1 =
2743 lane16id /
2744 2; // 16 contiguous chunks of head elems are spread across 8x2lanes
2745 shared_logits[offset1][lane2id][local_qhead_idx][0] = tmp;
2746 }
2747
2748 __syncthreads();
2749
2750 #pragma unroll
2751 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
2752 Qlocal[qkhe_depth] =
2753 shared_logits[qkhe_depth][rowid][lane16id % GQA_RATIO][0];
2754 }
2755 }
2756
2757 const int num_seq_blocks = DIVIDE_ROUND_UP(seq_len, BLOCK_SIZE);
2758 const int last_seq_block = num_seq_blocks - 1;
2759
2760 const int* block_table_seq = block_tables + seq_idx * max_num_blocks_per_seq;
2761
2762 int kphysical_block_number[TLOOP];
2763
2764 // fetch k physical block numbers
2765 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2766 const int klocal_token_idx =
2767 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
2768 const int kglobal_token_idx = partition_start_token_idx + klocal_token_idx;
2769 const int kblock_idx = (kglobal_token_idx < seq_len)
2770 ? kglobal_token_idx / BLOCK_SIZE
2771 : last_seq_block;
2772 kphysical_block_number[token_depth] = block_table_seq[kblock_idx];
2773 }
2774
2775 constexpr int KX = 16 / sizeof(cache_t);
2776 const cache_t* k_ptr = k_cache + wg_start_kv_head_idx * kv_head_stride;
2777
2778 const int row_head_elem = rowid * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2779
2780 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2781 const int64_t kblock_number =
2782 static_cast<int64_t>(kphysical_block_number[token_depth]);
2783 const cache_t* k_ptr2 = k_ptr + kblock_number * kv_block_stride;
2784 const int klocal_token_idx =
2785 TOKENS_PER_WARP * warpid + token_depth * 16 + lane16id;
2786 const int kphysical_block_offset = klocal_token_idx % BLOCK_SIZE;
2787 const cache_t* k_ptr3 = k_ptr2 + kphysical_block_offset * KX;
2788
2789 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
2790 const int head_elem = row_head_elem + qkhe_depth * QKHE_PER_FETCH;
2791 const int offset1 = head_elem / KX;
2792 const int offset2 = head_elem % KX;
2793 const cache_t* k_fetch_ptr = k_ptr3 + offset1 * BLOCK_SIZE * KX + offset2;
2794 const _B16x8* k_fetch_ptr_16B =
2795 reinterpret_cast<const _B16x8*>(k_fetch_ptr);
2796 Klocal[token_depth][qkhe_depth] = *k_fetch_ptr_16B;
2797 }
2798 }
2799
2800 constexpr int VTOKENS_PER_LANE =
2801 TOKENS_PER_WARP / ROWS_PER_WARP; // 32/2 = 16 vtokens per lane
2802 constexpr int VBLOCKS_PER_LANE = 1; // assumes block size >=16
2803 constexpr int VTLOOP = NWARPS; // corresponds to tokens across warps
2804 constexpr int VTLANELOOP = DIVIDE_ROUND_UP(
2805 VTOKENS_PER_LANE,
2806 CONTIGUOUS_KV_ELEMS_16B_LOAD); // optimized for 16B fetches; assumes
2807 // minimum block size is 16
2808 constexpr int VHELOOP = DIVIDE_ROUND_UP(
2809 (HEAD_SIZE / 16), NWARPS); // head_size distributed across warps; each
2810 // wmma instr works on 16 head elements
2811
2812 int vphysical_block_number[VTLOOP][VBLOCKS_PER_LANE];
2813
2814 // fetch v physical block numbers
2815 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2816 for (int vblock_depth = 0; vblock_depth < VBLOCKS_PER_LANE;
2817 vblock_depth++) {
2818 const int vlocal_token_idx =
2819 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
2820 rowid * VTOKENS_PER_LANE + vblock_depth * BLOCK_SIZE;
2821 const int vglobal_token_idx =
2822 partition_start_token_idx + vlocal_token_idx;
2823 const int vblock_idx = (vglobal_token_idx < seq_len)
2824 ? vglobal_token_idx / BLOCK_SIZE
2825 : last_seq_block;
2826 vphysical_block_number[vtoken_depth][vblock_depth] =
2827 block_table_seq[vblock_idx];
2828 }
2829 }
2830
2831 _B16x8 Vlocal[VTLOOP][VHELOOP]
2832 [VTLANELOOP]; // this can be interpreted as B8x16 too
2833
2834 const cache_t* v_ptr = v_cache + wg_start_kv_head_idx * kv_head_stride +
2835 ((rowid * VTOKENS_PER_LANE) % BLOCK_SIZE);
2836
2837 // v fetches are 16head elems across lanes x 16 tokens per lane
2838 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2839 const int vhead_elem = vhe_depth * NWARPS * 16 + warpid * 16 + lane16id;
2840 const cache_t* v_ptr2 = v_ptr + vhead_elem * BLOCK_SIZE;
2841
2842 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2843 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
2844 const int vblock_depth = 0;
2845 const int64_t vblock_number = static_cast<int64_t>(
2846 vphysical_block_number[vtoken_depth][vblock_depth]);
2847 const cache_t* v_ptr3 = v_ptr2 + (vblock_number * kv_block_stride);
2848
2849 const cache_t* v_fetch_ptr =
2850 v_ptr3 + vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2851 const _B16x8* v_fetch_ptr_16B =
2852 reinterpret_cast<const _B16x8*>(v_fetch_ptr);
2853 Vlocal[vtoken_depth][vhe_depth][vfetch_depth] = *v_fetch_ptr_16B;
2854 }
2855 }
2856 }
2857
2858 if (partition_start_token_idx + T_PAR_SIZE > seq_len) {
2859 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2860 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2861 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
2862 const int vglobal_fetch_start =
2863 partition_start_token_idx +
2864 vtoken_depth * VTOKENS_PER_LANE * ROWS_PER_WARP +
2865 rowid * VTOKENS_PER_LANE +
2866 vfetch_depth * CONTIGUOUS_KV_ELEMS_16B_LOAD;
2867 Vlocal[vtoken_depth][vhe_depth][vfetch_depth] =
2868 mask_v_cache_padding<cache_t>(
2869 Vlocal[vtoken_depth][vhe_depth][vfetch_depth],
2870 vglobal_fetch_start, seq_len);
2871 }
2872 }
2873 }
2874 }
2875
2876 floatx8 dout[TLOOP];
2877 // qk wmma
2878 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2879 dout[token_depth] = {0};
2880 for (int qkhe_depth = 0; qkhe_depth < QKHELOOP; qkhe_depth++) {
2881 dout[token_depth] = gcn_wmma16x16x16_instr<scalar_t, 0, 0, 0>(
2882 Klocal[token_depth][qkhe_depth].u16x8, Qlocal[qkhe_depth].u16x8,
2883 dout[token_depth]);
2884 }
2885 dout[token_depth] *= scale;
2886 }
2887
2888 // calculate qk_max and exp_sum per warp and write to shared memory
2889 float qk_max = -FLT_MAX;
2890 float exp_sum = 0.0f;
2891 const int qkout_token_idx =
2892 partition_start_token_idx + TOKENS_PER_WARP * warpid + rowid * 8;
2893 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2894 const int local_token_idx = qkout_token_idx + token_depth * 16;
2895 for (int i = 0; i < 8; i++) {
2896 const float tmp =
2897 (local_token_idx + i < seq_len) ? dout[token_depth][i] : -FLT_MAX;
2898 qk_max = fmaxf(qk_max, tmp);
2899 }
2900 }
2901
2902 qk_max = fmaxf(qk_max, __shfl_xor(qk_max, 16));
2903
2904 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2905 const int local_token_idx = qkout_token_idx + token_depth * 16;
2906 for (int i = 0; i < 8; i++) {
2907 const float tmp = (local_token_idx + i < seq_len)
2908 ? __expf(dout[token_depth][i] - qk_max)
2909 : 0.0f;
2910 dout[token_depth][i] = tmp;
2911 exp_sum += tmp;
2912 }
2913 }
2914
2915 exp_sum += __shfl_xor(exp_sum, 16);
2916
2917 __syncthreads();
2918
2919 if (laneid < 16) {
2920 shared_qk_max[warpid][lane16id] = qk_max;
2921 shared_exp_sum[warpid][lane16id] = exp_sum;
2922 }
2923
2924 __syncthreads();
2925
2926 // calculate partition qk_max and exp_sum
2927 float partition_qk_max = -FLT_MAX;
2928 float warp_qk_max_exp[NWARPS];
2929 float partition_exp_sum = 0.0f;
2930
2931 #pragma unroll
2932 for (int w = 0; w < NWARPS; w++) {
2933 warp_qk_max_exp[w] = shared_qk_max[w][lane16id];
2934 partition_qk_max = fmaxf(partition_qk_max, warp_qk_max_exp[w]);
2935 }
2936
2937 for (int w = 0; w < NWARPS; w++) {
2938 warp_qk_max_exp[w] = __expf(warp_qk_max_exp[w] - partition_qk_max);
2939 partition_exp_sum += shared_exp_sum[w][lane16id] * warp_qk_max_exp[w];
2940 }
2941
2942 const float inv_sum_scale =
2943 __fdividef(1.f, partition_exp_sum + 1e-6f) * warp_qk_max_exp[warpid];
2944
2945 __syncthreads();
2946
2947 // write logits to shared mem
2948 #pragma unroll
2949 for (int token_depth = 0; token_depth < TLOOP; token_depth++) {
2950 dout[token_depth] *= inv_sum_scale;
2951 shared_logits[warpid][token_depth][lane16id][rowid] =
2952 from_floatx8<scalar_t>(dout[token_depth]);
2953 }
2954
2955 // write out partition max_logits and exp_sum
2956 if (threadIdx.x < GQA_RATIO) {
2957 const int qhead_idx = lane16id;
2958 const int offset = seq_idx * total_num_heads * max_num_partitions +
2959 (wg_start_head_idx + qhead_idx) * max_num_partitions +
2960 partition_idx;
2961 max_logits[offset] = partition_qk_max;
2962 exp_sums[offset] = partition_exp_sum;
2963 }
2964
2965 __syncthreads();
2966
2967 _B16x8 outelems[VHELOOP];
2968 // Softmax V wmma
2969 // v layout: 16he across lanes x 16 tokens per lane
2970 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2971 floatx8 tmp_out = {0};
2972
2973 for (int vtoken_depth = 0; vtoken_depth < VTLOOP; vtoken_depth++) {
2974 for (int vfetch_depth = 0; vfetch_depth < VTLANELOOP; vfetch_depth++) {
2975 const int offset = rowid * VTLANELOOP + vfetch_depth;
2976 const int offset1 = offset % ROWS_PER_WARP;
2977 const int offset2 = offset / ROWS_PER_WARP;
2978 // if output format is 16 qheads across 16 lanes, 16 head elems spread
2979 // across rows
2980 tmp_out = gcn_wmma16x16x16_instr<scalar_t, 0, 0, 0>(
2981 Vlocal[vtoken_depth][vhe_depth][vfetch_depth].u16x8,
2982 shared_logits[vtoken_depth][offset2][lane16id][offset1].u16x8,
2983 tmp_out);
2984 }
2985 }
2986 outelems[vhe_depth] = from_floatx8<scalar_t>(tmp_out);
2987 }
2988
2989 __syncthreads();
2990
2991 #pragma unroll
2992 for (int vhe_depth = 0; vhe_depth < VHELOOP; vhe_depth++) {
2993 shared_logits[warpid][vhe_depth][lane16id][rowid] =
2994 outelems[vhe_depth]; // lane16 id head dimension; rowid head element
2995 // dimension
2996 }
2997
2998 __syncthreads();
2999
3000 // write to tmp_out with coalesced writes after reading from shared mem
3001 if (warpid == 0) {
3002 _B16x8 vout[GQA_RATIO2];
3003 // each lane writes out 16Bytes of tmp_out along head elem dimension
3004 const int head_elem_idx = lane16id * 8;
3005 if (head_elem_idx < HEAD_SIZE) {
3006 for (int h = 0; h < GQA_RATIO2; h++) {
3007 const int local_head_idx = 2 * h + rowid;
3008 const int offset1 = (head_elem_idx / 16) % NWARPS;
3009 const int offset2 = head_elem_idx / 16 / NWARPS;
3010 const int offset3 = (head_elem_idx / 8) % 2; // num_he % num_row
3011 vout[h] = shared_logits[offset1][offset2][local_head_idx][offset3];
3012 }
3013
3014 const int hsz_maxp_mult = HEAD_SIZE * max_num_partitions;
3015 scalar_t* out_ptr = out + seq_idx * total_num_heads * hsz_maxp_mult +
3016 partition_idx * HEAD_SIZE;
3017 for (int h = 0; h < GQA_RATIO2; h++) {
3018 const int local_head_idx = 2 * h + rowid;
3019 if (local_head_idx < GQA_RATIO) {
3020 const int out_head_idx = wg_start_head_idx + local_head_idx;
3021 scalar_t* out_ptr2 = out_ptr + out_head_idx * hsz_maxp_mult;
3022 scalar_t* out_ptr3 = out_ptr2 + head_elem_idx;
3023 _B16x8* out_ptr_B16x8 = reinterpret_cast<_B16x8*>(out_ptr3);
3024 *out_ptr_B16x8 = vout[h];
3025 }
3026 }
3027 }
3028 }
3029 }
3030
3031 template <typename scalar_t, typename cache_t,
3032 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
3033 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED,
3034 int GQA_RATIO>
3035 __global__
3036 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
3037 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
3038 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads,
3039 // head_size/x, block_size, x]
3040 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads,
3041 // head_size, block_size]
3042 const int num_kv_heads, const float scale,
3043 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
3044 const int* __restrict__ seq_lens, // [num_seqs]
3045 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
3046 const int max_num_blocks_per_seq,
3047 const float* __restrict__ alibi_slopes, // [num_heads]
3048 const int q_stride, const int kv_block_stride, const int kv_head_stride,
3049 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
3050 float* __restrict__ max_logits, // [num_seqs, num_heads,
3051 // max_num_partitions]
3052 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions,
3053 // head_size]
3054 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
3055 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
3056 UNREACHABLE_CODE
3057 }
3058
3059 // Grid: (num_heads, num_seqs).
3060 template <typename scalar_t, typename OUTT, int HEAD_SIZE, int NUM_THREADS,
3061 int PARTITION_SIZE, int NPAR_LOOPS>
3062 __global__
3063 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
3064 OUTT* __restrict__ out, // [num_seqs, num_heads, head_size]
3065 const float* __restrict__ exp_sums, // [num_seqs, num_heads,
3066 // max_num_partitions]
3067 const float* __restrict__ max_logits, // [num_seqs, num_heads,
3068 // max_num_partitions]
3069 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
3070 // max_num_partitions, head_size]
3071 const int* __restrict__ seq_lens, // [num_seqs]
3072 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
3073 const int max_num_partitions, const float* __restrict__ fp8_out_scale_ptr) {
3074 const auto num_heads = gridDim.x;
3075 const auto head_idx = blockIdx.x;
3076 const auto seq_idx = blockIdx.y;
3077
3078 // NOTE queries with sequence len > 1 are prefills and taken care by another
3079 // kernel.
3080 if (query_start_loc_ptr != nullptr &&
3081 (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
3082 return;
3083 }
3084
3085 const int seq_len = seq_lens[seq_idx];
3086 const int num_partitions = DIVIDE_ROUND_UP(seq_len, PARTITION_SIZE);
3087 const int warpid = threadIdx.x / WARP_SIZE;
3088
3089 __shared__ float shared_global_exp_sum;
3090 // max num partitions supported is warp_size * NPAR_LOOPS
3091 __shared__ float shared_exp_sums[NPAR_LOOPS * WARP_SIZE];
3092
3093 if (warpid == 0) {
3094 const float* max_logits_ptr = max_logits +
3095 seq_idx * num_heads * max_num_partitions +
3096 head_idx * max_num_partitions;
3097
3098 // valid partition is the last valid partition in case threadid > num
3099 // partitions
3100 int valid_partition[NPAR_LOOPS];
3101 float reg_max_logit[NPAR_LOOPS];
3102 const int last_valid_partition = num_partitions - 1;
3103
3104 #pragma unroll
3105 for (int i = 0; i < NPAR_LOOPS; i++) {
3106 const int partition_no = i * WARP_SIZE + threadIdx.x;
3107 valid_partition[i] =
3108 (partition_no < num_partitions) ? partition_no : last_valid_partition;
3109 }
3110 #pragma unroll
3111 for (int i = 0; i < NPAR_LOOPS; i++) {
3112 reg_max_logit[i] = max_logits_ptr[valid_partition[i]];
3113 }
3114 float max_logit = reg_max_logit[0];
3115 #pragma unroll
3116 for (int i = 1; i < NPAR_LOOPS; i++) {
3117 max_logit = fmaxf(max_logit, reg_max_logit[i]);
3118 }
3119
3120 #pragma unroll
3121 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
3122 max_logit = fmaxf(max_logit, __shfl_xor(max_logit, mask));
3123 }
3124
3125 const float* exp_sums_ptr = exp_sums +
3126 seq_idx * num_heads * max_num_partitions +
3127 head_idx * max_num_partitions;
3128
3129 float rescaled_exp_sum[NPAR_LOOPS];
3130 #pragma unroll
3131 for (int i = 0; i < NPAR_LOOPS; i++) {
3132 rescaled_exp_sum[i] = exp_sums_ptr[valid_partition[i]];
3133 }
3134 #pragma unroll
3135 for (int i = 0; i < NPAR_LOOPS; i++) {
3136 const int partition_no = i * WARP_SIZE + threadIdx.x;
3137 rescaled_exp_sum[i] *= (partition_no < num_partitions)
3138 ? expf(reg_max_logit[i] - max_logit)
3139 : 0.0f;
3140 }
3141 float global_exp_sum = rescaled_exp_sum[0];
3142 #pragma unroll
3143 for (int i = 1; i < NPAR_LOOPS; i++) {
3144 global_exp_sum += rescaled_exp_sum[i];
3145 }
3146 #pragma unroll
3147 for (int i = 0; i < NPAR_LOOPS; i++) {
3148 const int partition_no = i * WARP_SIZE + threadIdx.x;
3149 shared_exp_sums[partition_no] = rescaled_exp_sum[i];
3150 }
3151
3152 #pragma unroll
3153 for (int mask = WARP_SIZE / 2; mask >= 1; mask /= 2) {
3154 global_exp_sum += __shfl_xor(global_exp_sum, mask);
3155 }
3156 if (threadIdx.x == 0) {
3157 shared_global_exp_sum = global_exp_sum;
3158 }
3159 } // warpid == 0
3160 const scalar_t* tmp_out_ptr =
3161 tmp_out + seq_idx * num_heads * max_num_partitions * HEAD_SIZE +
3162 head_idx * max_num_partitions * HEAD_SIZE + threadIdx.x;
3163 constexpr int MAX_NPAR = 32;
3164 scalar_t tmps[MAX_NPAR];
3165 const float dzero = 0.0f;
3166 #pragma unroll
3167 for (int j = 0; j < MAX_NPAR; j++) {
3168 tmps[j] = from_float<scalar_t>(dzero);
3169 }
3170 const int last_partition_offset = (num_partitions - 1) * HEAD_SIZE;
3171 const int num_partition_offset = (num_partitions)*HEAD_SIZE;
3172 int idx = 0;
3173
3174 constexpr int JCHUNK = 16;
3175
3176 #pragma unroll
3177 for (int j = 0; j < JCHUNK * HEAD_SIZE; j += HEAD_SIZE) {
3178 // lastj is last valid partition
3179 const int lastj_offset =
3180 (j < num_partition_offset) ? j : last_partition_offset;
3181 tmps[idx] = tmp_out_ptr[lastj_offset];
3182 idx++;
3183 }
3184 __syncthreads();
3185
3186 if (num_partitions > JCHUNK) {
3187 #pragma unroll
3188 for (int j = JCHUNK * HEAD_SIZE; j < 2 * JCHUNK * HEAD_SIZE;
3189 j += HEAD_SIZE) {
3190 const int lastj_offset =
3191 (j < num_partition_offset) ? j : last_partition_offset;
3192 tmps[idx] = tmp_out_ptr[lastj_offset];
3193 idx++;
3194 }
3195
3196 if (num_partitions > 2 * JCHUNK) {
3197 #pragma unroll
3198 for (int j = 2 * JCHUNK * HEAD_SIZE; j < MAX_NPAR * HEAD_SIZE;
3199 j += HEAD_SIZE) {
3200 const int lastj_offset =
3201 (j < num_partition_offset) ? j : last_partition_offset;
3202 tmps[idx] = tmp_out_ptr[lastj_offset];
3203 idx++;
3204 }
3205 }
3206 } // num_partitions > JCHUNK
3207
3208 // Aggregate tmp_out to out.
3209 float acc = 0.0f;
3210 #pragma unroll
3211 for (int j = 0; j < JCHUNK; j++) {
3212 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
3213 }
3214 if (num_partitions > JCHUNK) {
3215 #pragma unroll
3216 for (int j = JCHUNK; j < 2 * JCHUNK; j++) {
3217 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
3218 }
3219 if (num_partitions > 2 * JCHUNK) {
3220 #pragma unroll
3221 for (int j = 2 * JCHUNK; j < MAX_NPAR; j++) {
3222 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j];
3223 }
3224 }
3225 }
3226
3227 for (int p = 1; p < NPAR_LOOPS; p++) {
3228 if (num_partitions > p * MAX_NPAR) {
3229 idx = 0;
3230 #pragma unroll
3231 for (int j = p * MAX_NPAR * HEAD_SIZE; j < (p + 1) * MAX_NPAR * HEAD_SIZE;
3232 j += HEAD_SIZE) {
3233 // lastj is last valid partition
3234 const int lastj_offset =
3235 (j < num_partition_offset) ? j : last_partition_offset;
3236 tmps[idx] = tmp_out_ptr[lastj_offset];
3237 idx++;
3238 }
3239
3240 #pragma unroll
3241 for (int j = 0; j < MAX_NPAR; j++) {
3242 acc += to_float<scalar_t>(tmps[j]) * shared_exp_sums[j + p * MAX_NPAR];
3243 }
3244 }
3245 }
3246
3247 const float inv_global_exp_sum =
3248 __fdividef(1.0f, shared_global_exp_sum + 1e-6f);
3249 acc *= inv_global_exp_sum;
3250
3251 const int64_t query_start_off = static_cast<int64_t>(
3252 query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
3253 OUTT* out_ptr = out + query_start_off * num_heads * HEAD_SIZE +
3254 static_cast<int64_t>(head_idx) * HEAD_SIZE;
3255 out_ptr[threadIdx.x] = from_float<scalar_t>(acc);
3256 }
3257
3258 #else
3259
3260 // clang-format off
3261 template <typename scalar_t, typename cache_t,
3262 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
3263 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED,
3264 int GQA_RATIO, MFMAType MFMA_TYPE>
3265 __global__
3266 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma16_kernel(
3267 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
3268 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
3269 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
3270 const int num_kv_heads,
3271 const float scale,
3272 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
3273 const int* __restrict__ seq_lens, // [num_seqs]
3274 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
3275 const int max_num_blocks_per_seq,
3276 const float* __restrict__ alibi_slopes, // [num_heads]
3277 const int q_stride,
3278 const int kv_block_stride,
3279 const int kv_head_stride,
3280 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
3281 float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
3282 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions, head_size]
3283 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
3284 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
3285 UNREACHABLE_CODE
3286 }
3287
3288 template <typename scalar_t, typename cache_t,
3289 vllm::Fp8KVCacheDataType KV_DTYPE, typename OUTT, int BLOCK_SIZE,
3290 int HEAD_SIZE, int NUM_THREADS, bool ALIBI_ENABLED,
3291 int GQA_RATIO>
3292 __global__
3293 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
3294 const scalar_t* __restrict__ q, // [num_seqs, num_heads, head_size]
3295 const cache_t* __restrict__ k_cache, // [num_blocks, num_kv_heads, head_size/x, block_size, x]
3296 const cache_t* __restrict__ v_cache, // [num_blocks, num_kv_heads, head_size, block_size]
3297 const int num_kv_heads,
3298 const float scale,
3299 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
3300 const int* __restrict__ seq_lens, // [num_seqs]
3301 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
3302 const int max_num_blocks_per_seq,
3303 const float* __restrict__ alibi_slopes, // [num_heads]
3304 const int q_stride,
3305 const int kv_block_stride,
3306 const int kv_head_stride,
3307 float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
3308 float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
3309 scalar_t* __restrict__ out, // [num_seqs, num_heads, max_num_partitions, head_size]
3310 OUTT* __restrict__ final_out, // [num_seqs, num_heads, head_size]
3311 int max_ctx_blocks, const float* k_scale, const float* v_scale) {
3312 UNREACHABLE_CODE
3313 }
3314
3315 // Grid: (num_heads, num_seqs).
3316 template <typename scalar_t, typename OUTT, int HEAD_SIZE, int NUM_THREADS,
3317 int PARTITION_SIZE, int NPAR_LOOPS>
3318 __global__
3319 __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
3320 OUTT* __restrict__ out, // [num_seqs, num_heads, head_size]
3321 const float* __restrict__ exp_sums, // [num_seqs, num_heads, max_num_partitions]
3322 const float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
3323 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads, max_num_partitions, head_size]
3324 const int* __restrict__ seq_lens, // [num_seqs]
3325 const int* __restrict__ query_start_loc_ptr, // [num_seqs]
3326 const int max_num_partitions, const float* __restrict__ fp8_out_scale_ptr) {
3327 UNREACHABLE_CODE
3328 }
3329 // clang-format on
3330
3331 #endif
3332
3333 #define LAUNCH_CUSTOM_ATTENTION_MFMA16(GQA_RATIO) \
3334 paged_attention_ll4mi_QKV_mfma16_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
3335 HEAD_SIZE, NTHR, ALIBI_ENABLED, \
3336 GQA_RATIO, MFMA_TYPE> \
3337 <<<grid, block, 0, stream>>>( \
3338 query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
3339 block_tables_ptr, seq_lens_ptr, query_start_loc_ptr, \
3340 max_num_blocks_per_seq, alibi_slopes_ptr, q_stride, kv_block_stride, \
3341 kv_head_stride, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, \
3342 max_ctx_blocks, k_scale_ptr, v_scale_ptr);
3343
3344 #define LAUNCH_CUSTOM_ATTENTION_MFMA4(GQA_RATIO) \
3345 paged_attention_ll4mi_QKV_mfma4_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
3346 HEAD_SIZE, NTHR, ALIBI_ENABLED, \
3347 GQA_RATIO> \
3348 <<<grid, block, 0, stream>>>( \
3349 query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
3350 block_tables_ptr, seq_lens_ptr, query_start_loc_ptr, \
3351 max_num_blocks_per_seq, alibi_slopes_ptr, q_stride, kv_block_stride, \
3352 kv_head_stride, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, \
3353 max_ctx_blocks, k_scale_ptr, v_scale_ptr);
3354
3355 #define LAUNCH_CUSTOM_REDUCTION(NPAR_LOOPS) \
3356 paged_attention_ll4mi_reduce_kernel<T, OUTT, HEAD_SIZE, HEAD_SIZE, \
3357 PARTITION_SIZE, NPAR_LOOPS> \
3358 <<<reduce_grid, reduce_block, 0, stream>>>( \
3359 out_ptr, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, seq_lens_ptr, \
3360 query_start_loc_ptr, max_num_partitions, fp8_out_scale_ptr);
3361
3362 template <typename T, typename KVT, vllm::Fp8KVCacheDataType KV_DTYPE,
3363 int BLOCK_SIZE, int HEAD_SIZE, typename OUTT, int PARTITION_SIZE_OLD,
3364 bool ALIBI_ENABLED, MFMAType MFMA_TYPE>
3365 void paged_attention_custom_launcher(
3366 torch::Tensor& out, torch::Tensor& exp_sums, torch::Tensor& max_logits,
3367 torch::Tensor& tmp_out, torch::Tensor& query, torch::Tensor& key_cache,
3368 torch::Tensor& value_cache, const int num_kv_heads, float scale,
3369 torch::Tensor& block_tables, torch::Tensor& seq_lens,
3370 const std::optional<torch::Tensor>& query_start_loc, int max_seq_len,
3371 const std::optional<torch::Tensor>& alibi_slopes, torch::Tensor& k_scale,
3372 torch::Tensor& v_scale, const std::optional<torch::Tensor>& fp8_out_scale) {
3373 int num_seqs = block_tables.size(0);
3374 int num_heads = query.size(1);
3375 int head_size = query.size(2);
3376 int max_num_blocks_per_seq = block_tables.size(1);
3377 int q_stride = query.stride(0);
3378 int kv_block_stride = key_cache.stride(0);
3379 int kv_head_stride = key_cache.stride(1);
3380
3381 // NOTE: query start location is optional for V0 decode should not be used.
3382 // If batch contains mix of prefills and decode, prefills should be skipped.
3383 const int* query_start_loc_ptr =
3384 query_start_loc
3385 ? reinterpret_cast<const int*>(query_start_loc.value().data_ptr())
3386 : nullptr;
3387
3388 // NOTE: alibi_slopes is optional.
3389 const float* alibi_slopes_ptr =
3390 alibi_slopes
3391 ? reinterpret_cast<const float*>(alibi_slopes.value().data_ptr())
3392 : nullptr;
3393
3394 float* exp_sums_ptr = reinterpret_cast<float*>(exp_sums.data_ptr());
3395 float* max_logits_ptr = reinterpret_cast<float*>(max_logits.data_ptr());
3396 T* tmp_out_ptr = reinterpret_cast<T*>(tmp_out.data_ptr());
3397 T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
3398 KVT* key_cache_ptr = reinterpret_cast<KVT*>(key_cache.data_ptr());
3399 KVT* value_cache_ptr = reinterpret_cast<KVT*>(value_cache.data_ptr());
3400 int* block_tables_ptr = block_tables.data_ptr<int>();
3401 int* seq_lens_ptr = seq_lens.data_ptr<int>();
3402 const float* k_scale_ptr = reinterpret_cast<const float*>(k_scale.data_ptr());
3403 const float* v_scale_ptr = reinterpret_cast<const float*>(v_scale.data_ptr());
3404 // NOTE: fp8_out_scale is optional.
3405 const auto fp8_out_scale_ptr =
3406 fp8_out_scale
3407 ? static_cast<const float*>(fp8_out_scale.value().data_ptr())
3408 : nullptr;
3409 OUTT* out_ptr = reinterpret_cast<OUTT*>(out.data_ptr());
3410
3411 const int max_ctx_blocks = DIVIDE_ROUND_UP(max_seq_len, BLOCK_SIZE);
3412
3413 // partition size is fixed at 256 since both mfma4 and mfma16 kernels support
3414 // it mfma4 kernel also supports partition size 512
3415 constexpr int PARTITION_SIZE = 256;
3416 const int max_num_partitions = DIVIDE_ROUND_UP(max_seq_len, PARTITION_SIZE);
3417 const int gqa_ratio = num_heads / num_kv_heads;
3418 assert(num_heads % num_kv_heads == 0);
3419 assert(head_size == HEAD_SIZE);
3420
3421 constexpr int NTHR = 256;
3422 dim3 grid(num_seqs, max_num_partitions, num_kv_heads);
3423 dim3 block(NTHR);
3424 const at::cuda::OptionalCUDAGuard device_guard(device_of(query));
3425 const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
3426
3427 // mfma4 kernel is faster than mfma16 for gqa_ratio <= 4
3428 switch (gqa_ratio) {
3429 case 1:
3430 LAUNCH_CUSTOM_ATTENTION_MFMA4(1);
3431 break;
3432 case 2:
3433 LAUNCH_CUSTOM_ATTENTION_MFMA4(2);
3434 break;
3435 case 3:
3436 LAUNCH_CUSTOM_ATTENTION_MFMA4(3);
3437 break;
3438 case 4:
3439 LAUNCH_CUSTOM_ATTENTION_MFMA4(4);
3440 break;
3441 case 5:
3442 LAUNCH_CUSTOM_ATTENTION_MFMA16(5);
3443 break;
3444 case 6:
3445 LAUNCH_CUSTOM_ATTENTION_MFMA16(6);
3446 break;
3447 case 7:
3448 LAUNCH_CUSTOM_ATTENTION_MFMA16(7);
3449 break;
3450 case 8:
3451 LAUNCH_CUSTOM_ATTENTION_MFMA16(8);
3452 break;
3453 case 9:
3454 LAUNCH_CUSTOM_ATTENTION_MFMA16(9);
3455 break;
3456 case 10:
3457 LAUNCH_CUSTOM_ATTENTION_MFMA16(10);
3458 break;
3459 case 11:
3460 LAUNCH_CUSTOM_ATTENTION_MFMA16(11);
3461 break;
3462 case 12:
3463 LAUNCH_CUSTOM_ATTENTION_MFMA16(12);
3464 break;
3465 case 13:
3466 LAUNCH_CUSTOM_ATTENTION_MFMA16(13);
3467 break;
3468 case 14:
3469 LAUNCH_CUSTOM_ATTENTION_MFMA16(14);
3470 break;
3471 case 15:
3472 LAUNCH_CUSTOM_ATTENTION_MFMA16(15);
3473 break;
3474 case 16:
3475 LAUNCH_CUSTOM_ATTENTION_MFMA16(16);
3476 break;
3477 default:
3478 TORCH_CHECK(false, "Unsupported gqa ratio: ", gqa_ratio);
3479 break;
3480 }
3481
3482 dim3 reduce_grid(num_heads, num_seqs);
3483 dim3 reduce_block(head_size);
3484 const int npar_loops = DIVIDE_ROUND_UP(max_num_partitions, WARP_SIZE);
3485 // reduction kernel supports upto 8 NPAR_loops * 64 (warp_size) * 256
3486 // (partition size) = 128K context length
3487 switch (npar_loops) {
3488 case 1:
3489 LAUNCH_CUSTOM_REDUCTION(1);
3490 break;
3491 case 2:
3492 LAUNCH_CUSTOM_REDUCTION(2);
3493 break;
3494 case 3:
3495 LAUNCH_CUSTOM_REDUCTION(3);
3496 break;
3497 case 4:
3498 LAUNCH_CUSTOM_REDUCTION(4);
3499 break;
3500 case 5:
3501 LAUNCH_CUSTOM_REDUCTION(5);
3502 break;
3503 case 6:
3504 LAUNCH_CUSTOM_REDUCTION(6);
3505 break;
3506 case 7:
3507 LAUNCH_CUSTOM_REDUCTION(7);
3508 break;
3509 case 8:
3510 LAUNCH_CUSTOM_REDUCTION(8);
3511 break;
3512 default:
3513 TORCH_CHECK(false, "Unsupported npar_loops: ", npar_loops);
3514 break;
3515 }
3516 }
3517
3518 template <typename T, typename KVT, vllm::Fp8KVCacheDataType KV_DTYPE,
3519 int BLOCK_SIZE, int HEAD_SIZE, typename OUTT, int PARTITION_SIZE_OLD,
3520 bool ALIBI_ENABLED, MFMAType MFMA_TYPE>
3521 void paged_attention_custom_launcher_navi(
3522 torch::Tensor& out, torch::Tensor& exp_sums, torch::Tensor& max_logits,
3523 torch::Tensor& tmp_out, torch::Tensor& query, torch::Tensor& key_cache,
3524 torch::Tensor& value_cache, const int num_kv_heads, float scale,
3525 torch::Tensor& block_tables, torch::Tensor& seq_lens,
3526 const std::optional<torch::Tensor>& query_start_loc, int max_seq_len,
3527 const std::optional<torch::Tensor>& alibi_slopes, torch::Tensor& k_scale,
3528 torch::Tensor& v_scale) {
3529 int num_seqs = block_tables.size(0);
3530 int num_heads = query.size(1);
3531 int head_size = query.size(2);
3532 int max_num_blocks_per_seq = block_tables.size(1);
3533 int q_stride = query.stride(0);
3534 int kv_block_stride = key_cache.stride(0);
3535 int kv_head_stride = key_cache.stride(1);
3536
3537 // NOTE: query start location is optional for V0 decode should not be used.
3538 // If batch contains mix of prefills and decode, prefills should be skipped.
3539 const int* query_start_loc_ptr =
3540 query_start_loc
3541 ? reinterpret_cast<const int*>(query_start_loc.value().data_ptr())
3542 : nullptr;
3543
3544 // NOTE: Navi does not support alibi_slopes.
3545 const float* alibi_slopes_ptr = nullptr;
3546
3547 float* exp_sums_ptr = reinterpret_cast<float*>(exp_sums.data_ptr());
3548 float* max_logits_ptr = reinterpret_cast<float*>(max_logits.data_ptr());
3549 T* tmp_out_ptr = reinterpret_cast<T*>(tmp_out.data_ptr());
3550 T* query_ptr = reinterpret_cast<T*>(query.data_ptr());
3551 KVT* key_cache_ptr = reinterpret_cast<KVT*>(key_cache.data_ptr());
3552 KVT* value_cache_ptr = reinterpret_cast<KVT*>(value_cache.data_ptr());
3553 int* block_tables_ptr = block_tables.data_ptr<int>();
3554 int* seq_lens_ptr = seq_lens.data_ptr<int>();
3555
3556 const float* k_scale_ptr = reinterpret_cast<const float*>(k_scale.data_ptr());
3557 const float* v_scale_ptr = reinterpret_cast<const float*>(v_scale.data_ptr());
3558 // NOTE: Navi does not support fp8.
3559 const auto fp8_out_scale_ptr = nullptr;
3560 OUTT* out_ptr = reinterpret_cast<OUTT*>(out.data_ptr());
3561
3562 const int max_ctx_blocks = DIVIDE_ROUND_UP(max_seq_len, BLOCK_SIZE);
3563
3564 constexpr int PARTITION_SIZE = 256;
3565 const int max_num_partitions = DIVIDE_ROUND_UP(max_seq_len, PARTITION_SIZE);
3566 const int gqa_ratio = num_heads / num_kv_heads;
3567 assert(num_heads % num_kv_heads == 0);
3568 assert(head_size == HEAD_SIZE);
3569
3570 constexpr int NTHR = 256;
3571 dim3 grid(num_seqs, max_num_partitions, num_kv_heads);
3572 dim3 block(NTHR);
3573 const at::cuda::OptionalCUDAGuard device_guard(device_of(query));
3574 const cudaStream_t stream = at::cuda::getCurrentCUDAStream();
3575
3576 switch (gqa_ratio) {
3577 case 1:
3578 LAUNCH_CUSTOM_ATTENTION_MFMA16(1);
3579 break;
3580 case 2:
3581 LAUNCH_CUSTOM_ATTENTION_MFMA16(2);
3582 break;
3583 case 3:
3584 LAUNCH_CUSTOM_ATTENTION_MFMA16(3);
3585 break;
3586 case 4:
3587 LAUNCH_CUSTOM_ATTENTION_MFMA16(4);
3588 break;
3589 case 5:
3590 LAUNCH_CUSTOM_ATTENTION_MFMA16(5);
3591 break;
3592 case 6:
3593 LAUNCH_CUSTOM_ATTENTION_MFMA16(6);
3594 break;
3595 case 7:
3596 LAUNCH_CUSTOM_ATTENTION_MFMA16(7);
3597 break;
3598 case 8:
3599 LAUNCH_CUSTOM_ATTENTION_MFMA16(8);
3600 break;
3601 case 9:
3602 LAUNCH_CUSTOM_ATTENTION_MFMA16(9);
3603 break;
3604 case 10:
3605 LAUNCH_CUSTOM_ATTENTION_MFMA16(10);
3606 break;
3607 case 11:
3608 LAUNCH_CUSTOM_ATTENTION_MFMA16(11);
3609 break;
3610 case 12:
3611 LAUNCH_CUSTOM_ATTENTION_MFMA16(12);
3612 break;
3613 case 13:
3614 LAUNCH_CUSTOM_ATTENTION_MFMA16(13);
3615 break;
3616 case 14:
3617 LAUNCH_CUSTOM_ATTENTION_MFMA16(14);
3618 break;
3619 case 15:
3620 LAUNCH_CUSTOM_ATTENTION_MFMA16(15);
3621 break;
3622 case 16:
3623 LAUNCH_CUSTOM_ATTENTION_MFMA16(16);
3624 break;
3625 default:
3626 TORCH_CHECK(false, "Unsupported gqa ratio: ", gqa_ratio);
3627 break;
3628 }
3629
3630 dim3 reduce_grid(num_heads, num_seqs);
3631 dim3 reduce_block(head_size);
3632 const int warp_size = 32;
3633 const int npar_loops = DIVIDE_ROUND_UP(max_num_partitions, warp_size);
3634 // reduction kernel supports upto 16 NPAR_loops * 32 (warp_size) * 256
3635 // (partition size) = 128K context length
3636 switch (npar_loops) {
3637 case 1:
3638 LAUNCH_CUSTOM_REDUCTION(1);
3639 break;
3640 case 2:
3641 LAUNCH_CUSTOM_REDUCTION(2);
3642 break;
3643 case 3:
3644 LAUNCH_CUSTOM_REDUCTION(3);
3645 break;
3646 case 4:
3647 LAUNCH_CUSTOM_REDUCTION(4);
3648 break;
3649 case 5:
3650 LAUNCH_CUSTOM_REDUCTION(5);
3651 break;
3652 case 6:
3653 LAUNCH_CUSTOM_REDUCTION(6);
3654 break;
3655 case 7:
3656 LAUNCH_CUSTOM_REDUCTION(7);
3657 break;
3658 case 8:
3659 LAUNCH_CUSTOM_REDUCTION(8);
3660 break;
3661 case 9:
3662 LAUNCH_CUSTOM_REDUCTION(9);
3663 break;
3664 case 10:
3665 LAUNCH_CUSTOM_REDUCTION(10);
3666 break;
3667 case 11:
3668 LAUNCH_CUSTOM_REDUCTION(11);
3669 break;
3670 case 12:
3671 LAUNCH_CUSTOM_REDUCTION(12);
3672 break;
3673 case 13:
3674 LAUNCH_CUSTOM_REDUCTION(13);
3675 break;
3676 case 14:
3677 LAUNCH_CUSTOM_REDUCTION(14);
3678 break;
3679 case 15:
3680 LAUNCH_CUSTOM_REDUCTION(15);
3681 break;
3682 case 16:
3683 LAUNCH_CUSTOM_REDUCTION(16);
3684 break;
3685 default:
3686 TORCH_CHECK(false, "Unsupported npar_loops: ", npar_loops);
3687 break;
3688 }
3689 }
3690
3691 #define CALL_CUSTOM_LAUNCHER(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, OUTT, \
3692 PSIZE, ALIBI_ENABLED, MFMA_TYPE) \
3693 if (!is_navi) { \
3694 paged_attention_custom_launcher<T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
3695 OUTT, PSIZE, ALIBI_ENABLED, MFMA_TYPE>( \
3696 out, exp_sums, max_logits, tmp_out, query, key_cache, value_cache, \
3697 num_kv_heads, scale, block_tables, seq_lens, query_start_loc, \
3698 max_seq_len, alibi_slopes, k_scale, v_scale, fp8_out_scale); \
3699 } else { \
3700 paged_attention_custom_launcher_navi<T, KVT, KV_DTYPE, BLK_SIZE, \
3701 HEAD_SIZE, OUTT, PSIZE, \
3702 ALIBI_ENABLED, MFMA_TYPE>( \
3703 out, exp_sums, max_logits, tmp_out, query, key_cache, value_cache, \
3704 num_kv_heads, scale, block_tables, seq_lens, query_start_loc, \
3705 max_seq_len, alibi_slopes, k_scale, v_scale); \
3706 }
3707
3708 #define CALL_CUSTOM_LAUNCHER_ALIBI(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
3709 OUTT, PSIZE, MFMA_TYPE) \
3710 if (alibi_slopes) { \
3711 CALL_CUSTOM_LAUNCHER(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, OUTT, PSIZE, \
3712 true, MFMA_TYPE); \
3713 } else { \
3714 CALL_CUSTOM_LAUNCHER(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, OUTT, PSIZE, \
3715 false, MFMA_TYPE); \
3716 }
3717
3718 #if defined(__HIPCC__) && defined(__gfx90a__)
3719 #define CALL_CUSTOM_LAUNCHER_OUT(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
3720 MFMA_TYPE) \
3721 if (fp8_out_scale) { \
3722 TORCH_CHECK(false, "fp8 out scale unsupported for gfx90a"); \
3723 } else { \
3724 CALL_CUSTOM_LAUNCHER_ALIBI(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, T, \
3725 256, MFMA_TYPE); \
3726 }
3727 #else
3728 #define CALL_CUSTOM_LAUNCHER_OUT(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
3729 MFMA_TYPE) \
3730 if (fp8_out_scale) { \
3731 CALL_CUSTOM_LAUNCHER_ALIBI(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
3732 uint8_t, 256, MFMA_TYPE); \
3733 } else { \
3734 CALL_CUSTOM_LAUNCHER_ALIBI(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, T, \
3735 256, MFMA_TYPE); \
3736 }
3737 #endif
3738
3739 #define CALL_CUSTOM_LAUNCHER_BLK(T, KVT, KV_DTYPE, HEAD_SIZE, MFMA_TYPE) \
3740 switch (block_size) { \
3741 case 16: \
3742 CALL_CUSTOM_LAUNCHER_OUT(T, KVT, KV_DTYPE, 16, HEAD_SIZE, MFMA_TYPE); \
3743 break; \
3744 case 32: \
3745 CALL_CUSTOM_LAUNCHER_OUT(T, KVT, KV_DTYPE, 32, HEAD_SIZE, MFMA_TYPE); \
3746 break; \
3747 default: \
3748 TORCH_CHECK(false, "Unsupported block size: ", block_size); \
3749 break; \
3750 }
3751
3752 #define CALL_CUSTOM_LAUNCHER_BLK_HEAD(T, KVT, KV_DTYPE, MFMA_TYPE) \
3753 switch (head_size) { \
3754 case 64: \
3755 CALL_CUSTOM_LAUNCHER_BLK(T, KVT, KV_DTYPE, 64, MFMA_TYPE); \
3756 break; \
3757 case 128: \
3758 CALL_CUSTOM_LAUNCHER_BLK(T, KVT, KV_DTYPE, 128, MFMA_TYPE); \
3759 break; \
3760 default: \
3761 TORCH_CHECK(false, "Unsupported head size: ", head_size); \
3762 break; \
3763 }
3764
3765 bool is_navi_gpu() {
3766 static bool is_cached = false;
3767 static bool result;
3768
3769 if (!is_cached) {
3770 int device_id;
3771 hipDeviceProp_t deviceProp;
3772 hipGetDevice(&device_id);
3773 hipGetDeviceProperties(&deviceProp, device_id);
3774
3775 std::string arch = deviceProp.gcnArchName;
3776 result = arch.find("gfx11") == 0 || arch.find("gfx12") == 0;
3777 is_cached = true;
3778 }
3779
3780 return result;
3781 }
3782
3783 // clang-format off
3784 void paged_attention(
3785 torch::Tensor& out, // [num_seqs, num_heads, head_size]
3786 torch::Tensor& exp_sums, // [num_seqs, num_heads, max_num_partitions]
3787 torch::Tensor& max_logits, // [num_seqs, num_heads, max_num_partitions]
3788 torch::Tensor& tmp_out, // [num_seqs, num_heads, max_num_partitions, head_size]
3789 torch::Tensor& query, // [num_seqs, num_heads, head_size]
3790 torch::Tensor& key_cache, // [num_blocks, num_heads, head_size/x, block_size, x]
3791 torch::Tensor& value_cache, // [num_blocks, num_heads, head_size, block_size]
3792 int64_t num_kv_heads,
3793 double scale,
3794 torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
3795 torch::Tensor& seq_lens, // [num_seqs]
3796 const std::optional<torch::Tensor>& query_start_loc, // [num_seqs]
3797 int64_t block_size, int64_t max_seq_len,
3798 const std::optional<torch::Tensor>& alibi_slopes,
3799 const std::string& kv_cache_dtype, torch::Tensor& k_scale,
3800 torch::Tensor& v_scale,
3801 const std::optional<torch::Tensor>& fp8_out_scale,
3802 const std::string& mfma_type) {
3803 // clang-format on
3804 bool is_navi = is_navi_gpu();
3805 const int head_size = query.size(2);
3806 if (kv_cache_dtype == "auto") {
3807 if (query.dtype() == at::ScalarType::Half) {
3808 CALL_CUSTOM_LAUNCHER_BLK_HEAD(
3809 _Float16, _Float16, vllm::Fp8KVCacheDataType::kAuto, MFMAType::F16);
3810 } else if (query.dtype() == at::ScalarType::BFloat16) {
3811 CALL_CUSTOM_LAUNCHER_BLK_HEAD(__hip_bfloat16, __hip_bfloat16,
3812 vllm::Fp8KVCacheDataType::kAuto,
3813 MFMAType::F16);
3814 } else {
3815 TORCH_CHECK(false, "Unsupported data type: ", query.dtype());
3816 }
3817 } else if (kv_cache_dtype == "fp8" || kv_cache_dtype == "fp8_e4m3") {
3818 if (query.dtype() == at::ScalarType::Half) {
3819 if (mfma_type == "fp8") {
3820 CALL_CUSTOM_LAUNCHER_BLK_HEAD(_Float16, uint8_t,
3821 vllm::Fp8KVCacheDataType::kFp8E4M3,
3822 MFMAType::Fp8);
3823 } else {
3824 CALL_CUSTOM_LAUNCHER_BLK_HEAD(_Float16, uint8_t,
3825 vllm::Fp8KVCacheDataType::kFp8E4M3,
3826 MFMAType::F16);
3827 }
3828 } else if (query.dtype() == at::ScalarType::BFloat16) {
3829 if (mfma_type == "fp8") {
3830 CALL_CUSTOM_LAUNCHER_BLK_HEAD(__hip_bfloat16, uint8_t,
3831 vllm::Fp8KVCacheDataType::kFp8E4M3,
3832 MFMAType::Fp8);
3833 } else {
3834 CALL_CUSTOM_LAUNCHER_BLK_HEAD(__hip_bfloat16, uint8_t,
3835 vllm::Fp8KVCacheDataType::kFp8E4M3,
3836 MFMAType::F16);
3837 }
3838 } else {
3839 TORCH_CHECK(false, "Unsupported data type: ", query.dtype());
3840 }
3841 } else {
3842 TORCH_CHECK(false, "Unsupported KV cache dtype: ", kv_cache_dtype);
3843 }
3844 }
3845
3846 #undef WARP_SIZE
3847 #undef MAX
3848 #undef MIN
3849 #undef DIVIDE_ROUND_UP