@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / e73ff24e31
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 16:00 UTC

[ROCM][KERNEL] Paged attention for V1 (#15720)

Signed-off-by: Aleksandr Malyshev <maleksan@amd.com> Signed-off-by: root <root@banff-cyxtera-s65-4.amd.com> Co-authored-by: Aleksandr Malyshev <maleksan@amd.com> Co-authored-by: root <root@banff-cyxtera-s65-4.amd.com>

Aleksandr Malyshev committed Apr 2, 2025 at 19:48 UTC e73ff24e31d29b095e24e160d3b0651d2436d977
11 files changed +220 -110
csrc/rocm/attention.cu
+73 -32
@@ -272,6 +272,7 @@ __launch_bounds__(NUM_THREADS, 5) void paged_attention_ll4mi_QKV_mfma16_kernel(
272 const float scale,
273 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
274 const int* __restrict__ context_lens, // [num_seqs]
275 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
276 const int max_num_blocks_per_seq,
277 const float* __restrict__ alibi_slopes, // [num_heads]
278 const int q_stride,
@@ -291,6 +292,13 @@ __launch_bounds__(NUM_THREADS, 5) void paged_attention_ll4mi_QKV_mfma16_kernel(
292 const int rowid = laneid / 16;
293
294 const auto seq_idx = blockIdx.x;
295 + // NOTE queries with sequence len > 1 are prefills and taken care by another
296 + // kernel.
297 + if (query_start_loc_ptr != nullptr &&
298 + (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx]) != 1) {
299 + return;
300 + }
301 +
302 const auto partition_idx = blockIdx.y;
303
304 constexpr int T_PAR_SIZE = 256; // token partition size set to 256
@@ -377,9 +385,10 @@ __launch_bounds__(NUM_THREADS, 5) void paged_attention_ll4mi_QKV_mfma16_kernel(
385 // fetch Q in shared across warps and then write to registers
386 const int local_qhead_idx = 4 * warpid + rowid;
387 const int global_qhead_idx = wg_start_head_idx + local_qhead_idx;
380 - const int64_t seq_idx64 = static_cast<int64_t>(seq_idx);
388 + const int64_t query_start_off = static_cast<int64_t>(
389 + query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
390 const scalar_t* q_ptr =
382 - q + seq_idx64 * q_stride + global_qhead_idx * HEAD_SIZE;
391 + q + query_start_off * q_stride + global_qhead_idx * HEAD_SIZE;
392
393 const int qhead_element = lane16id * CONTIGUOUS_SCALAR_ELEMS_16B;
394 if ((local_qhead_idx < GQA_RATIO) && (qhead_element < HEAD_SIZE)) {
@@ -777,6 +786,7 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
786 const float scale,
787 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
788 const int* __restrict__ context_lens, // [num_seqs]
789 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
790 const int max_num_blocks_per_seq,
791 const float* __restrict__ alibi_slopes, // [num_heads]
792 const int q_stride,
@@ -794,6 +804,12 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
804 const int lane4id = laneid % 4;
805
806 const auto seq_idx = blockIdx.x;
807 + // NOTE queries with sequence len > 1 are prefills and taken care by another
808 + // kernel.
809 + if (query_start_loc_ptr != nullptr &&
810 + (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
811 + return;
812 + }
813 const auto partition_idx = blockIdx.y;
814 const auto partition_size = blockDim.x;
815 const auto max_num_partitions = gridDim.y;
@@ -882,9 +898,11 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
898 }
899
900 // fetch q elements
885 - // every 4 lanes fetch 8 elems, so warp fetches 8*16 = 128 elems
901 + // every 4 lanes fetch 8 elems, so warp fetches 8*16 = 128 elemsc
902 + const int64_t query_start_off = static_cast<int64_t>(
903 + query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
904 const scalar_t* q_ptr =
887 - q + seq_idx * q_stride + wg_start_head_idx * HEAD_SIZE;
905 + q + query_start_off * q_stride + wg_start_head_idx * HEAD_SIZE;
906 const _B16x8* q_ptrh8 = reinterpret_cast<const _B16x8*>(q_ptr);
907 const int qhead_elemh8 = laneid / 4;
908
@@ -1267,10 +1285,19 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
1285 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads,
1286 // max_num_partitions, head_size]
1287 const int* __restrict__ context_lens, // [num_seqs]
1288 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1289 const int max_num_partitions) {
1290 const auto num_heads = gridDim.x;
1291 const auto head_idx = blockIdx.x;
1292 const auto seq_idx = blockIdx.y;
1293 +
1294 + // NOTE queries with sequence len > 1 are prefills and taken care by another
1295 + // kernel.
1296 + if (query_start_loc_ptr != nullptr &&
1297 + (query_start_loc_ptr[seq_idx + 1] - query_start_loc_ptr[seq_idx] != 1)) {
1298 + return;
1299 + }
1300 +
1301 const int context_len = context_lens[seq_idx];
1302 const int num_partitions = DIVIDE_ROUND_UP(context_len, PARTITION_SIZE);
1303 [[maybe_unused]] constexpr int NUM_WARPS = NUM_THREADS / WARP_SIZE;
@@ -1439,7 +1466,9 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
1466 __fdividef(1.0f, shared_global_exp_sum + 1e-6f);
1467 acc *= inv_global_exp_sum;
1468
1442 - OUTT* out_ptr = out + static_cast<int64_t>(seq_idx) * num_heads * HEAD_SIZE +
1469 + const int64_t query_start_off = static_cast<int64_t>(
1470 + query_start_loc_ptr ? query_start_loc_ptr[seq_idx] : seq_idx);
1471 + OUTT* out_ptr = out + query_start_off * num_heads * HEAD_SIZE +
1472 static_cast<int64_t>(head_idx) * HEAD_SIZE;
1473 if constexpr (std::is_same<OUTT, bit8_t>::value) {
1474 out_ptr[threadIdx.x] =
@@ -1466,6 +1495,7 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma16_kernel(
1495 const float scale,
1496 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
1497 const int* __restrict__ context_lens, // [num_seqs]
1498 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1499 const int max_num_blocks_per_seq,
1500 const float* __restrict__ alibi_slopes, // [num_heads]
1501 const int q_stride,
@@ -1492,6 +1522,7 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_QKV_mfma4_kernel(
1522 const float scale,
1523 const int* __restrict__ block_tables, // [num_seqs, max_num_blocks_per_seq]
1524 const int* __restrict__ context_lens, // [num_seqs]
1525 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1526 const int max_num_blocks_per_seq,
1527 const float* __restrict__ alibi_slopes, // [num_heads]
1528 const int q_stride,
@@ -1515,6 +1546,7 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
1546 const float* __restrict__ max_logits, // [num_seqs, num_heads, max_num_partitions]
1547 const scalar_t* __restrict__ tmp_out, // [num_seqs, num_heads, max_num_partitions, head_size]
1548 const int* __restrict__ context_lens, // [num_seqs]
1549 + const int* __restrict__ query_start_loc_ptr, // [num_seqs]
1550 const int max_num_partitions) {
1551 UNREACHABLE_CODE
1552 }
@@ -1522,34 +1554,34 @@ __launch_bounds__(NUM_THREADS) void paged_attention_ll4mi_reduce_kernel(
1554
1555 #endif // defined(__HIP__MI300_MI250__) TODO: Add NAVI support
1556
1525 -#define LAUNCH_CUSTOM_ATTENTION_MFMA16(GQA_RATIO) \
1526 - paged_attention_ll4mi_QKV_mfma16_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
1527 - HEAD_SIZE, NTHR, ALIBI_ENABLED, \
1528 - GQA_RATIO> \
1529 - <<<grid, block, 0, stream>>>( \
1530 - query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
1531 - block_tables_ptr, context_lens_ptr, max_num_blocks_per_seq, \
1532 - alibi_slopes_ptr, q_stride, kv_block_stride, kv_head_stride, \
1533 - exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, max_ctx_blocks, \
1534 - k_scale_ptr, v_scale_ptr);
1535 -
1536 -#define LAUNCH_CUSTOM_ATTENTION_MFMA4(GQA_RATIO) \
1537 - paged_attention_ll4mi_QKV_mfma4_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
1538 - HEAD_SIZE, NTHR, ALIBI_ENABLED, \
1539 - GQA_RATIO> \
1540 - <<<grid, block, 0, stream>>>( \
1541 - query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
1542 - block_tables_ptr, context_lens_ptr, max_num_blocks_per_seq, \
1543 - alibi_slopes_ptr, q_stride, kv_block_stride, kv_head_stride, \
1544 - exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, max_ctx_blocks, \
1545 - k_scale_ptr, v_scale_ptr);
1557 +#define LAUNCH_CUSTOM_ATTENTION_MFMA16(GQA_RATIO) \
1558 + paged_attention_ll4mi_QKV_mfma16_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
1559 + HEAD_SIZE, NTHR, ALIBI_ENABLED, \
1560 + GQA_RATIO> \
1561 + <<<grid, block, 0, stream>>>( \
1562 + query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
1563 + block_tables_ptr, context_lens_ptr, query_start_loc_ptr, \
1564 + max_num_blocks_per_seq, alibi_slopes_ptr, q_stride, kv_block_stride, \
1565 + kv_head_stride, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, \
1566 + max_ctx_blocks, k_scale_ptr, v_scale_ptr);
1567 +
1568 +#define LAUNCH_CUSTOM_ATTENTION_MFMA4(GQA_RATIO) \
1569 + paged_attention_ll4mi_QKV_mfma4_kernel<T, KVT, KV_DTYPE, OUTT, BLOCK_SIZE, \
1570 + HEAD_SIZE, NTHR, ALIBI_ENABLED, \
1571 + GQA_RATIO> \
1572 + <<<grid, block, 0, stream>>>( \
1573 + query_ptr, key_cache_ptr, value_cache_ptr, num_kv_heads, scale, \
1574 + block_tables_ptr, context_lens_ptr, query_start_loc_ptr, \
1575 + max_num_blocks_per_seq, alibi_slopes_ptr, q_stride, kv_block_stride, \
1576 + kv_head_stride, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, out_ptr, \
1577 + max_ctx_blocks, k_scale_ptr, v_scale_ptr);
1578
1579 #define LAUNCH_CUSTOM_REDUCTION(NPAR_LOOPS) \
1580 paged_attention_ll4mi_reduce_kernel<T, OUTT, HEAD_SIZE, HEAD_SIZE, \
1581 PARTITION_SIZE, NPAR_LOOPS> \
1582 <<<reduce_grid, reduce_block, 0, stream>>>( \
1583 out_ptr, exp_sums_ptr, max_logits_ptr, tmp_out_ptr, \
1552 - context_lens_ptr, max_num_partitions);
1584 + context_lens_ptr, query_start_loc_ptr, max_num_partitions);
1585
1586 template <typename T, typename KVT, vllm::Fp8KVCacheDataType KV_DTYPE,
1587 int BLOCK_SIZE, int HEAD_SIZE, typename OUTT, int PARTITION_SIZE_OLD,
@@ -1559,9 +1591,10 @@ void paged_attention_custom_launcher(
1591 torch::Tensor& tmp_out, torch::Tensor& query, torch::Tensor& key_cache,
1592 torch::Tensor& value_cache, const int num_kv_heads, float scale,
1593 torch::Tensor& block_tables, torch::Tensor& context_lens,
1562 - int max_context_len, const std::optional<torch::Tensor>& alibi_slopes,
1563 - torch::Tensor& k_scale, torch::Tensor& v_scale) {
1564 - int num_seqs = query.size(0);
1594 + const std::optional<torch::Tensor>& query_start_loc, int max_context_len,
1595 + const std::optional<torch::Tensor>& alibi_slopes, torch::Tensor& k_scale,
1596 + torch::Tensor& v_scale) {
1597 + int num_seqs = block_tables.size(0);
1598 int num_heads = query.size(1);
1599 int head_size = query.size(2);
1600 int max_num_blocks_per_seq = block_tables.size(1);
@@ -1569,6 +1602,13 @@ void paged_attention_custom_launcher(
1602 int kv_block_stride = key_cache.stride(0);
1603 int kv_head_stride = key_cache.stride(1);
1604
1605 + // NOTE: query start location is optional for V0 decode should not be used.
1606 + // If batch contains mix of prefills and decode, prefills should be skipped.
1607 + const int* query_start_loc_ptr =
1608 + query_start_loc
1609 + ? reinterpret_cast<const int*>(query_start_loc.value().data_ptr())
1610 + : nullptr;
1611 +
1612 // NOTE: alibi_slopes is optional.
1613 const float* alibi_slopes_ptr =
1614 alibi_slopes
@@ -1700,8 +1740,8 @@ void paged_attention_custom_launcher(
1740 paged_attention_custom_launcher<T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, T, \
1741 PSIZE, ALIBI_ENABLED>( \
1742 out, exp_sums, max_logits, tmp_out, query, key_cache, value_cache, \
1703 - num_kv_heads, scale, block_tables, context_lens, max_context_len, \
1704 - alibi_slopes, k_scale, v_scale);
1743 + num_kv_heads, scale, block_tables, context_lens, query_start_loc, \
1744 + max_context_len, alibi_slopes, k_scale, v_scale);
1745
1746 #define CALL_CUSTOM_LAUNCHER_ALIBI(T, KVT, KV_DTYPE, BLK_SIZE, HEAD_SIZE, \
1747 PSIZE) \
@@ -1750,6 +1790,7 @@ void paged_attention(
1790 double scale,
1791 torch::Tensor& block_tables, // [num_seqs, max_num_blocks_per_seq]
1792 torch::Tensor& context_lens, // [num_seqs]
1793 + const std::optional<torch::Tensor>& query_start_loc, // [num_seqs]
1794 int64_t block_size, int64_t max_context_len,
1795 const std::optional<torch::Tensor>& alibi_slopes,
1796 const std::string& kv_cache_dtype, torch::Tensor& k_scale,
csrc/rocm/ops.h
+3 -2
@@ -7,8 +7,9 @@ void paged_attention(torch::Tensor& out, torch::Tensor& exp_sums,
7 torch::Tensor& query, torch::Tensor& key_cache,
8 torch::Tensor& value_cache, int64_t num_kv_heads,
9 double scale, torch::Tensor& block_tables,
10 - torch::Tensor& context_lens, int64_t block_size,
11 - int64_t max_context_len,
10 + torch::Tensor& context_lens,
11 + const std::optional<torch::Tensor>& query_start_loc,
12 + int64_t block_size, int64_t max_context_len,
13 const std::optional<torch::Tensor>& alibi_slopes,
14 const std::string& kv_cache_dtype, torch::Tensor& k_scale,
15 torch::Tensor& v_scale);
csrc/rocm/torch_bindings.cpp
+3 -1
@@ -23,7 +23,9 @@ TORCH_LIBRARY_EXPAND(TORCH_EXTENSION_NAME, rocm_ops) {
23 " Tensor query, Tensor key_cache,"
24 " Tensor value_cache, int num_kv_heads,"
25 " float scale, Tensor block_tables,"
26 - " Tensor context_lens, int block_size,"
26 + " Tensor context_lens,"
27 + " Tensor? query_start_loc,"
28 + " int block_size,"
29 " int max_context_len,"
30 " Tensor? alibi_slopes,"
31 " str kv_cache_dtype,"
tests/kernels/test_prefix_prefill.py
+4
@@ -164,6 +164,7 @@ def test_contexted_kv_attention(
164 block_table,
165 b_start_loc,
166 b_seq_len,
167 + MAX_CTX_LEN,
168 max_input_len,
169 k_scale,
170 v_scale,
@@ -180,6 +181,7 @@ def test_contexted_kv_attention(
181 block_table,
182 b_start_loc,
183 b_seq_len,
184 + MAX_CTX_LEN,
185 max_input_len,
186 k_scale,
187 v_scale,
@@ -397,6 +399,7 @@ def test_contexted_kv_attention_alibi(
399 block_table,
400 b_start_loc,
401 b_seq_len,
402 + MAX_CTX_LEN,
403 max_input_len,
404 k_scale,
405 v_scale,
@@ -413,6 +416,7 @@ def test_contexted_kv_attention_alibi(
416 block_table,
417 b_start_loc,
418 b_seq_len,
419 + MAX_CTX_LEN,
420 max_input_len,
421 k_scale,
422 v_scale,
vllm/_custom_ops.py
+4 -2
@@ -110,6 +110,7 @@ def paged_attention_rocm(
110 scale: float,
111 block_tables: torch.Tensor,
112 seq_lens: torch.Tensor,
113 + query_start_loc: Optional[torch.Tensor],
114 block_size: int,
115 max_seq_len: int,
116 alibi_slopes: Optional[torch.Tensor],
@@ -120,8 +121,9 @@ def paged_attention_rocm(
121 torch.ops._rocm_C.paged_attention(out, exp_sum, max_logits, tmp_out, query,
122 key_cache, value_cache, num_kv_heads,
123 scale, block_tables, seq_lens,
123 - block_size, max_seq_len, alibi_slopes,
124 - kv_cache_dtype, k_scale, v_scale)
124 + query_start_loc, block_size, max_seq_len,
125 + alibi_slopes, kv_cache_dtype, k_scale,
126 + v_scale)
127
128
129 def mla_decode_kvcache_cpu(
vllm/attention/backends/rocm_flash_attn.py
+6 -18
@@ -17,16 +17,13 @@ from vllm.attention.ops.paged_attn import (PagedAttention,
17 PagedAttentionMetadata)
18 from vllm.logger import init_logger
19 from vllm.platforms import current_platform
20 +from vllm.platforms.rocm import use_rocm_custom_paged_attention
21
22 if TYPE_CHECKING:
23 from vllm.worker.model_runner import ModelInputForGPUWithSamplingMetadata
24
25 logger = init_logger(__name__)
25 -
26 _PARTITION_SIZE_ROCM = 256
27 -_GPU_ARCH = torch.cuda.get_device_properties("cuda").gcnArchName
28 -_ON_NAVI = "gfx1" in _GPU_ARCH
29 -_ON_MI250_MI300 = any(arch in _GPU_ARCH for arch in ["gfx90a", "gfx942"])
27
28
29 class ROCmFlashAttentionBackend(AttentionBackend):
@@ -790,9 +787,9 @@ class ROCmFlashAttentionImpl(AttentionImpl):
787 num_seqs, num_heads, head_size = decode_query.shape
788 block_size = value_cache.shape[3]
789 gqa_ratio = num_heads // self.num_kv_heads
793 - use_custom = _use_rocm_custom_paged_attention(
790 + use_custom = use_rocm_custom_paged_attention(
791 decode_query.dtype, head_size, block_size, gqa_ratio,
795 - decode_meta.max_decode_seq_len)
792 + decode_meta.max_decode_seq_len, self.sliding_window)
793 if use_custom:
794 max_seq_len = (decode_meta.max_decode_seq_len if self.attn_type
795 != AttentionType.ENCODER_DECODER else
@@ -817,6 +814,8 @@ class ROCmFlashAttentionImpl(AttentionImpl):
814 out = output[num_prefill_tokens:]
815 else:
816 out = output
817 +
818 + query_start_loc = None
819 ops.paged_attention_rocm(
820 out,
821 exp_sums,
@@ -833,6 +832,7 @@ class ROCmFlashAttentionImpl(AttentionImpl):
832 decode_meta.seq_lens_tensor
833 if self.attn_type != AttentionType.ENCODER_DECODER else
834 decode_meta.encoder_seq_lens_tensor,
835 + query_start_loc,
836 block_size,
837 max_seq_len,
838 self.alibi_slopes,
@@ -898,15 +898,3 @@ def _sdpa_attention(
898 start = end
899
900 return output
901 -
902 -
903 -def _use_rocm_custom_paged_attention(qtype: torch.dtype, head_size: int,
904 - block_size: int, gqa_ratio: int,
905 - max_seq_len: int) -> bool:
906 - # rocm custom page attention not support on navi (gfx1*)
907 - return (_ON_MI250_MI300 and not _ON_NAVI
908 - and (qtype == torch.half or qtype == torch.bfloat16)
909 - and (head_size == 64 or head_size == 128)
910 - and (block_size == 16 or block_size == 32)
911 - and (gqa_ratio >= 1 and gqa_ratio <= 16) and max_seq_len <= 32768
912 - and envs.VLLM_ROCM_CUSTOM_PAGED_ATTN)
vllm/attention/ops/chunked_prefill_paged_decode.py
+103 -54
@@ -10,6 +10,9 @@ import torch
10 import triton
11 import triton.language as tl
12
13 +from vllm import _custom_ops as ops
14 +from vllm.platforms.rocm import use_rocm_custom_paged_attention
15 +
16 from .prefix_prefill import context_attention_fwd
17
18
@@ -33,26 +36,26 @@ def kernel_paged_attention_2d(
36 num_query_heads: tl.constexpr, # int
37 num_queries_per_kv: tl.constexpr, # int
38 num_queries_per_kv_padded: tl.constexpr, # int
36 - block_table_stride: tl.constexpr, # int
37 - query_stride_0: tl.constexpr, # int
38 - query_stride_1: tl.constexpr, # int, should be equal to head_size
39 - output_stride_0: tl.constexpr, # int
40 - output_stride_1: tl.constexpr, # int, should be equal to head_size
39 + block_table_stride: tl.int64, # int
40 + query_stride_0: tl.int64, # int
41 + query_stride_1: tl.int64, # int, should be equal to head_size
42 + output_stride_0: tl.int64, # int
43 + output_stride_1: tl.int64, # int, should be equal to head_size
44 BLOCK_SIZE: tl.constexpr, # int
45 HEAD_SIZE: tl.constexpr, # int
46 HEAD_SIZE_PADDED: tl.constexpr, # int, must be power of 2
47 USE_ALIBI_SLOPES: tl.constexpr, # bool
48 SLIDING_WINDOW: tl.constexpr, # int
49 x: tl.constexpr, # int
47 - stride_k_cache_0: tl.constexpr, # int
48 - stride_k_cache_1: tl.constexpr, # int
49 - stride_k_cache_2: tl.constexpr, # int
50 - stride_k_cache_3: tl.constexpr, # int
51 - stride_k_cache_4: tl.constexpr, # int
52 - stride_v_cache_0: tl.constexpr, # int
53 - stride_v_cache_1: tl.constexpr, # int
54 - stride_v_cache_2: tl.constexpr, # int
55 - stride_v_cache_3: tl.constexpr, # int
50 + stride_k_cache_0: tl.int64, # int
51 + stride_k_cache_1: tl.int64, # int
52 + stride_k_cache_2: tl.int64, # int
53 + stride_k_cache_3: tl.int64, # int
54 + stride_k_cache_4: tl.int64, # int
55 + stride_v_cache_0: tl.int64, # int
56 + stride_v_cache_1: tl.int64, # int
57 + stride_v_cache_2: tl.int64, # int
58 + stride_v_cache_3: tl.int64, # int
59 filter_by_query_len: tl.constexpr, # bool
60 query_start_len_ptr, # [num_seqs+1]
61 ):
@@ -212,6 +215,7 @@ def chunked_prefill_paged_decode(
215 block_table,
216 query_start_loc,
217 seq_lens,
218 + max_seq_len,
219 max_query_len,
220 k_scale,
221 v_scale,
@@ -240,6 +244,7 @@ def chunked_prefill_paged_decode(
244 b_loc=block_table,
245 b_start_loc=query_start_loc,
246 b_seq_len=seq_lens,
247 + max_seq_len=max_seq_len,
248 max_input_len=max_query_len,
249 k_scale=k_scale,
250 v_scale=v_scale,
@@ -275,43 +280,87 @@ def chunked_prefill_paged_decode(
280 num_queries_per_kv_padded = max(triton.next_power_of_2(num_queries_per_kv),
281 16)
282
278 - kernel_paged_attention_2d[(
279 - num_seqs,
280 - num_kv_heads,
281 - )](
282 - output_ptr=output,
283 - query_ptr=query,
284 - key_cache_ptr=key_cache,
285 - value_cache_ptr=value_cache,
286 - block_tables_ptr=block_table,
287 - seq_lens_ptr=seq_lens,
288 - alibi_slopes_ptr=alibi_slopes,
289 - scale=sm_scale,
290 - k_scale=k_scale,
291 - v_scale=v_scale,
292 - num_query_heads=num_query_heads,
293 - num_queries_per_kv=num_queries_per_kv,
294 - num_queries_per_kv_padded=num_queries_per_kv_padded,
295 - block_table_stride=block_table.stride(0),
296 - query_stride_0=query.stride(0),
297 - query_stride_1=query.stride(1),
298 - output_stride_0=output.stride(0),
299 - output_stride_1=output.stride(1),
300 - BLOCK_SIZE=block_size,
301 - HEAD_SIZE=head_size,
302 - HEAD_SIZE_PADDED=triton.next_power_of_2(head_size),
303 - USE_ALIBI_SLOPES=use_alibi_slopes,
304 - SLIDING_WINDOW=sliding_window,
305 - x=key_cache.shape[4],
306 - stride_k_cache_0=key_cache.stride(0),
307 - stride_k_cache_1=key_cache.stride(1),
308 - stride_k_cache_2=key_cache.stride(2),
309 - stride_k_cache_3=key_cache.stride(3),
310 - stride_k_cache_4=key_cache.stride(4),
311 - stride_v_cache_0=value_cache.stride(0),
312 - stride_v_cache_1=value_cache.stride(1),
313 - stride_v_cache_2=value_cache.stride(2),
314 - stride_v_cache_3=value_cache.stride(3),
315 - filter_by_query_len=True,
316 - query_start_len_ptr=query_start_loc,
317 - )
283 + use_custom = use_rocm_custom_paged_attention(query.dtype, head_size,
284 + block_size,
285 + num_queries_per_kv,
286 + max_seq_len, sliding_window)
287 + if use_custom:
288 + _PARTITION_SIZE_ROCM = 256
289 + max_num_partitions = ((max_seq_len + _PARTITION_SIZE_ROCM - 1) //
290 + _PARTITION_SIZE_ROCM)
291 + assert _PARTITION_SIZE_ROCM % block_size == 0
292 + total_num_seq = query.shape[0]
293 + tmp_output = torch.empty(
294 + size=(total_num_seq, num_query_heads, max_num_partitions,
295 + head_size),
296 + dtype=output.dtype,
297 + device=output.device,
298 + )
299 + exp_sums = torch.empty(
300 + size=(total_num_seq, num_query_heads, max_num_partitions),
301 + dtype=torch.float32,
302 + device=output.device,
303 + )
304 + max_logits = torch.empty_like(exp_sums)
305 +
306 + ops.paged_attention_rocm(
307 + output,
308 + exp_sums,
309 + max_logits,
310 + tmp_output,
311 + query,
312 + key_cache,
313 + value_cache,
314 + num_kv_heads,
315 + scale=sm_scale,
316 + block_tables=block_table,
317 + seq_lens=seq_lens,
318 + query_start_loc=query_start_loc,
319 + block_size=block_size,
320 + max_seq_len=max_seq_len,
321 + alibi_slopes=alibi_slopes,
322 + kv_cache_dtype=kv_cache_dtype,
323 + k_scale=k_scale,
324 + v_scale=v_scale,
325 + )
326 + else:
327 + kernel_paged_attention_2d[(
328 + num_seqs,
329 + num_kv_heads,
330 + )](
331 + output_ptr=output,
332 + query_ptr=query,
333 + key_cache_ptr=key_cache,
334 + value_cache_ptr=value_cache,
335 + block_tables_ptr=block_table,
336 + seq_lens_ptr=seq_lens,
337 + alibi_slopes_ptr=alibi_slopes,
338 + scale=sm_scale,
339 + k_scale=k_scale,
340 + v_scale=v_scale,
341 + num_query_heads=num_query_heads,
342 + num_queries_per_kv=num_queries_per_kv,
343 + num_queries_per_kv_padded=num_queries_per_kv_padded,
344 + block_table_stride=block_table.stride(0),
345 + query_stride_0=query.stride(0),
346 + query_stride_1=query.stride(1),
347 + output_stride_0=output.stride(0),
348 + output_stride_1=output.stride(1),
349 + BLOCK_SIZE=block_size,
350 + HEAD_SIZE=head_size,
351 + HEAD_SIZE_PADDED=triton.next_power_of_2(head_size),
352 + USE_ALIBI_SLOPES=use_alibi_slopes,
353 + SLIDING_WINDOW=sliding_window,
354 + x=key_cache.shape[4],
355 + stride_k_cache_0=key_cache.stride(0),
356 + stride_k_cache_1=key_cache.stride(1),
357 + stride_k_cache_2=key_cache.stride(2),
358 + stride_k_cache_3=key_cache.stride(3),
359 + stride_k_cache_4=key_cache.stride(4),
360 + stride_v_cache_0=value_cache.stride(0),
361 + stride_v_cache_1=value_cache.stride(1),
362 + stride_v_cache_2=value_cache.stride(2),
363 + stride_v_cache_3=value_cache.stride(3),
364 + filter_by_query_len=True,
365 + query_start_len_ptr=query_start_loc,
366 + )
vllm/attention/ops/paged_attn.py
+2
@@ -209,6 +209,7 @@ class PagedAttention:
209 v_scale: torch.Tensor,
210 ) -> torch.Tensor:
211 output = torch.empty_like(query)
212 + max_seq_len = None
213 context_attention_fwd(
214 query,
215 key,
@@ -221,6 +222,7 @@ class PagedAttention:
222 # query_start_loc is (batch_size + 1,)
223 query_start_loc,
224 seq_lens_tensor,
225 + max_seq_len,
226 max_query_len,
227 k_scale,
228 v_scale,
vllm/attention/ops/prefix_prefill.py
+1
@@ -725,6 +725,7 @@ if triton.__version__ >= "2.1.0":
725 b_loc,
726 b_start_loc,
727 b_seq_len,
728 + max_seq_len,
729 max_input_len,
730 k_scale: torch.Tensor,
731 v_scale: torch.Tensor,
vllm/platforms/rocm.py
+20 -1
@@ -1,7 +1,7 @@
1 # SPDX-License-Identifier: Apache-2.0
2
3 import os
4 -from functools import lru_cache, wraps
4 +from functools import cache, lru_cache, wraps
5 from typing import TYPE_CHECKING, Dict, List, Optional
6
7 import torch
@@ -98,6 +98,25 @@ def device_id_to_physical_device_id(device_id: int) -> int:
98 return device_id
99
100
101 +@cache
102 +def use_rocm_custom_paged_attention(qtype: torch.dtype, head_size: int,
103 + block_size: int, gqa_ratio: int,
104 + max_seq_len: int,
105 + sliding_window: int) -> bool:
106 +
107 + GPU_ARCH = torch.cuda.get_device_properties("cuda").gcnArchName
108 + ON_NAVI = "gfx1" in GPU_ARCH
109 + ON_MI250_MI300 = any(arch in GPU_ARCH for arch in ["gfx90a", "gfx942"])
110 +
111 + # rocm custom page attention not support on navi (gfx1*)
112 + return (ON_MI250_MI300 and not ON_NAVI and (sliding_window == 0)
113 + and (qtype == torch.half or qtype == torch.bfloat16)
114 + and (head_size == 64 or head_size == 128)
115 + and (block_size == 16 or block_size == 32)
116 + and (gqa_ratio >= 1 and gqa_ratio <= 16) and max_seq_len <= 32768
117 + and envs.VLLM_ROCM_CUSTOM_PAGED_ATTN)
118 +
119 +
120 class RocmPlatform(Platform):
121 _enum = PlatformEnum.ROCM
122 device_name: str = "rocm"
vllm/v1/attention/backends/triton_attn.py
+1
@@ -168,6 +168,7 @@ class TritonAttentionImpl(AttentionImpl):
168 block_table=attn_metadata.block_table,
169 query_start_loc=attn_metadata.query_start_loc,
170 seq_lens=attn_metadata.seq_lens,
171 + max_seq_len=attn_metadata.max_seq_len,
172 max_query_len=attn_metadata.max_query_len,
173 k_scale=layer._k_scale,
174 v_scale=layer._v_scale,