Skip to content

Commit bf9a5dd

Browse files
[MLA] Optimize mla indexer prepare uniform decode for MTP > 1 (vllm-project#39458)
Signed-off-by: Giancarlo Delfin <gdelfin@inferact.ai>
1 parent 79e799e commit bf9a5dd

1 file changed

Lines changed: 100 additions & 43 deletions

File tree

vllm/v1/attention/backends/mla/indexer.py

Lines changed: 100 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from vllm.config import VllmConfig
99
from vllm.logger import init_logger
1010
from vllm.platforms import current_platform
11+
from vllm.triton_utils import tl, triton
1112
from vllm.utils.deep_gemm import (
1213
get_paged_mqa_logits_metadata,
1314
has_deep_gemm,
@@ -30,6 +31,40 @@
3031
logger = init_logger(__name__)
3132

3233

34+
@triton.jit
35+
def _prepare_uniform_decode_kernel(
36+
seq_lens_ptr,
37+
decode_seq_lens_ptr,
38+
block_table_ptr,
39+
block_table_stride,
40+
expanded_block_table_ptr,
41+
expanded_bt_stride,
42+
decode_lens_ptr,
43+
max_decode_len,
44+
BLOCK_SIZE: tl.constexpr,
45+
):
46+
idx = tl.program_id(0)
47+
req_id = idx // max_decode_len
48+
local_idx = idx % max_decode_len
49+
50+
# Compute number of KVs attended to by this token.
51+
seq_len = tl.load(seq_lens_ptr + req_id)
52+
per_token_seq_len = seq_len - max_decode_len + local_idx + 1
53+
tl.store(decode_seq_lens_ptr + idx, per_token_seq_len)
54+
55+
# Copy block table row.
56+
src = block_table_ptr + req_id * block_table_stride
57+
dst = expanded_block_table_ptr + idx * expanded_bt_stride
58+
for i in tl.range(0, expanded_bt_stride, BLOCK_SIZE):
59+
off = i + tl.arange(0, BLOCK_SIZE)
60+
mask = off < expanded_bt_stride
61+
src_block = tl.load(src + off, mask=mask)
62+
tl.store(dst + off, src_block, mask=mask)
63+
64+
# All reqs now have decode_len = 1.
65+
tl.store(decode_lens_ptr + idx, 1)
66+
67+
3368
def split_indexer_prefill_chunks(
3469
seq_lens_cpu: torch.Tensor,
3570
query_lens_cpu: torch.Tensor,
@@ -405,52 +440,75 @@ def _prepare_decode_tensors(
405440
Returns (seq_lens, block_table, decode_lens, batch_size, requires_padding).
406441
seq_lens is 1D (batch_size,) for flatten/plain, 2D (B, next_n) for native MTP.
407442
"""
443+
min_decode_len = int(decode_lens_cpu.min().item())
408444
if not use_native and max_decode_len > 1:
409445
assert self.decode_seq_lens_buffer.dim() == 1
410-
# Assume 4 requests with seq_lens [10, 7, 12, 0] (the final req is
411-
# padding) and decode_lens [3, 1, 4, 0] in the below example comments.
412-
# The context lengths are therefore
413-
# [10-3, 7-1, 12-4, 0-0] = [7, 6, 8, 0].
414-
415-
# 3 + 1 + 4 + 0 = 8
416-
actual_expanded = int(decode_lens_cpu.sum().item())
417-
418-
# Fuse expanded_base and expanded_starts into a single repeat_interleave:
419-
# seq_len_i = (context_start[b] - query_start_loc[b]) + arange[i] + 1
420-
# where context_start[b] = seq_lens[b] - decode_lens[b].
421-
# Example: offsets = [7-0, 6-3, 8-4, 0-8] = [7, 3, 4, -8]
422-
# expanded_offsets = [7, 7, 7, 3, 4, 4, 4, 4]
423-
# result = [8, 9, 10, 7, 9, 10, 11, 12]
424-
expanded_offsets = torch.repeat_interleave(
425-
seq_lens - decode_lens - query_start_loc,
426-
decode_lens,
427-
output_size=actual_expanded,
428-
)
446+
if min_decode_len == max_decode_len:
447+
# Uniform decode lengths.
448+
num_decode_tokens = num_decodes * max_decode_len
449+
_prepare_uniform_decode_kernel[(num_decode_tokens,)](
450+
seq_lens,
451+
self.decode_seq_lens_buffer,
452+
block_table,
453+
block_table.stride(0),
454+
self.expanded_block_table_buffer,
455+
self.expanded_block_table_buffer.stride(0),
456+
self.decode_lens_buffer,
457+
max_decode_len,
458+
BLOCK_SIZE=1024,
459+
)
460+
self.decode_seq_lens_buffer[num_decode_tokens:] = 0
461+
seq_lens = self.decode_seq_lens_buffer[:num_decode_tokens]
462+
block_table = self.expanded_block_table_buffer[:num_decode_tokens]
463+
decode_lens = self.decode_lens_buffer[:num_decode_tokens]
464+
return seq_lens, block_table, decode_lens, num_decode_tokens, False
465+
else:
466+
# Variable decode lengths.
467+
# Assume 4 requests with seq_lens [10, 7, 12, 0] (the final req is
468+
# padding) and decode_lens [3, 1, 4, 0] in the below example comments.
469+
# The context lengths are therefore
470+
# [10-3, 7-1, 12-4, 0-0] = [7, 6, 8, 0].
471+
472+
# 3 + 1 + 4 + 0 = 8
473+
actual_expanded = int(decode_lens_cpu.sum().item())
474+
475+
# Fuse expanded_base and expanded_starts into a single
476+
# repeat_interleave:
477+
# seq_len_i = (context_start[b] - query_start_loc[b]) + arange[i] + 1
478+
# where context_start[b] = seq_lens[b] - decode_lens[b].
479+
# Example: offsets = [7-0, 6-3, 8-4, 0-8] = [7, 3, 4, -8]
480+
# expanded_offsets = [7, 7, 7, 3, 4, 4, 4, 4]
481+
# result = [8, 9, 10, 7, 9, 10, 11, 12]
482+
expanded_offsets = torch.repeat_interleave(
483+
seq_lens - decode_lens - query_start_loc,
484+
decode_lens,
485+
output_size=actual_expanded,
486+
)
429487

430-
# [8, 9, 10, 7, 9, 10, 11, 12, ...] where ... is unused buffer space
431-
self.decode_seq_lens_buffer[:actual_expanded] = (
432-
expanded_offsets + self.arange_buffer[:actual_expanded] + 1
433-
)
434-
self.decode_seq_lens_buffer[actual_expanded:] = 0
435-
seq_lens = self.decode_seq_lens_buffer[:num_decode_tokens]
436-
437-
# Give each of the flattened entries the same block table row as the
438-
# original request.
439-
self.expanded_block_table_buffer[:actual_expanded] = (
440-
torch.repeat_interleave(
441-
block_table, decode_lens, dim=0, output_size=actual_expanded
488+
# [8, 9, 10, 7, 9, 10, 11, 12, ...] where ... is unused buffer space
489+
self.decode_seq_lens_buffer[:actual_expanded] = (
490+
expanded_offsets + self.arange_buffer[:actual_expanded] + 1
442491
)
443-
)
444-
if actual_expanded < num_decode_tokens:
445-
self.expanded_block_table_buffer[
446-
actual_expanded:num_decode_tokens, 0
447-
] = 0
448-
block_table = self.expanded_block_table_buffer[:num_decode_tokens]
449-
450-
# All reqs now have decode_len=1
451-
self.decode_lens_buffer[:num_decode_tokens] = 1
452-
decode_lens = self.decode_lens_buffer[:num_decode_tokens]
453-
return seq_lens, block_table, decode_lens, num_decode_tokens, False
492+
self.decode_seq_lens_buffer[actual_expanded:] = 0
493+
seq_lens = self.decode_seq_lens_buffer[:num_decode_tokens]
494+
495+
# Give each of the flattened entries the same block table row as the
496+
# original request.
497+
self.expanded_block_table_buffer[:actual_expanded] = (
498+
torch.repeat_interleave(
499+
block_table, decode_lens, dim=0, output_size=actual_expanded
500+
)
501+
)
502+
if actual_expanded < num_decode_tokens:
503+
self.expanded_block_table_buffer[
504+
actual_expanded:num_decode_tokens, 0
505+
] = 0
506+
block_table = self.expanded_block_table_buffer[:num_decode_tokens]
507+
508+
# All reqs now have decode_len=1
509+
self.decode_lens_buffer[:num_decode_tokens] = 1
510+
decode_lens = self.decode_lens_buffer[:num_decode_tokens]
511+
return seq_lens, block_table, decode_lens, num_decode_tokens, False
454512
else:
455513
# Native path: plain decode (next_n==1) or spec decode
456514
# with 2D per-token context lengths (next_n > 1).
@@ -459,7 +517,6 @@ def _prepare_decode_tensors(
459517
# decode_len < next_n due to padding or short prefills), the simple
460518
# reshape in sparse_attn_indexer won't work. Use pack_seq_triton
461519
# (requires_padding) instead.
462-
min_decode_len = int(decode_lens_cpu.min().item())
463520
requires_padding = min_decode_len != max_decode_len
464521
if use_native and next_n > 1:
465522
assert self.decode_seq_lens_buffer.dim() == 2

0 commit comments

Comments
 (0)