| 1 | # SPDX-License-Identifier: Apache-2.0 |
| 2 | # SPDX-FileCopyrightText: Copyright contributors to the vLLM project |
| 3 | |
| 4 | from dataclasses import dataclass |
| 5 | |
| 6 | import torch |
| 7 | |
| 8 | from vllm.model_executor.layers.mamba.checkpoint import ( |
| 9 | MambaPrefillCheckpointExporter, |
| 10 | MambaPrefillCheckpointMetadata, |
| 11 | ) |
| 12 | from vllm.triton_utils import tl, triton |
| 13 | from vllm.v1.attention.backends.utils import NULL_BLOCK_ID |
| 14 | |
| 15 | |
| 16 | def kda_prefill_checkpoint_alignment(backend: str) -> int | None: |
| 17 | return 16 if backend == "flashkda" else None |
| 18 | |
| 19 | |
| 20 | @dataclass(frozen=True) |
| 21 | class FlashKDAPrefillCheckpointExporter(MambaPrefillCheckpointExporter): |
| 22 | """Store FlashKDA recurrent and convolution checkpoint states.""" |
| 23 | |
| 24 | state_len: int | None = None |
| 25 | |
| 26 | def export( |
| 27 | self, |
| 28 | checkpoint: MambaPrefillCheckpointMetadata, |
| 29 | *, |
| 30 | raw_qkv: torch.Tensor, |
| 31 | conv_state: torch.Tensor, |
| 32 | recurrent_checkpoint: torch.Tensor, |
| 33 | recurrent_state: torch.Tensor, |
| 34 | cu_seqlens: torch.Tensor, |
| 35 | ) -> None: |
| 36 | state_len = ( |
| 37 | self.state_len if self.state_len is not None else conv_state.shape[-1] |
| 38 | ) |
| 39 | width = raw_qkv.shape[-1] |
| 40 | recurrent_row_size = recurrent_checkpoint[0].numel() |
| 41 | block_size = 256 |
| 42 | store_cache_checkpoints_kernel[ |
| 43 | ( |
| 44 | checkpoint.checkpoint_offsets.numel(), |
| 45 | triton.cdiv(max(width * state_len, recurrent_row_size), block_size), |
| 46 | ) |
| 47 | ]( |
| 48 | raw_qkv, |
| 49 | conv_state, |
| 50 | recurrent_checkpoint, |
| 51 | recurrent_state, |
| 52 | cu_seqlens, |
| 53 | checkpoint.checkpoint_offsets, |
| 54 | checkpoint.state_indices, |
| 55 | raw_qkv.stride(0), |
| 56 | raw_qkv.stride(1), |
| 57 | conv_state.stride(0), |
| 58 | conv_state.stride(1), |
| 59 | conv_state.stride(2), |
| 60 | recurrent_checkpoint.stride(0), |
| 61 | recurrent_state.stride(0), |
| 62 | checkpoint.checkpoint_offsets.stride(0), |
| 63 | state_len, |
| 64 | width, |
| 65 | recurrent_row_size, |
| 66 | NULL_BLOCK_ID, |
| 67 | block_size, |
| 68 | ) |
| 69 | |
| 70 | |
| 71 | @triton.jit |
| 72 | def store_cache_checkpoints_kernel( |
| 73 | x_ptr, |
| 74 | conv_state_ptr, |
| 75 | recurrent_checkpoint_ptr, |
| 76 | recurrent_state_ptr, |
| 77 | query_start_loc_ptr, |
| 78 | checkpoint_offsets_ptr, |
| 79 | checkpoint_state_indices_ptr, |
| 80 | x_stride_0: tl.constexpr, |
| 81 | x_stride_1: tl.constexpr, |
| 82 | state_stride_0: tl.constexpr, |
| 83 | state_stride_1: tl.constexpr, |
| 84 | state_stride_2: tl.constexpr, |
| 85 | checkpoint_stride_0: tl.constexpr, |
| 86 | recurrent_state_stride_0: tl.constexpr, |
| 87 | checkpoint_offset_stride: tl.constexpr, |
| 88 | STATE_LEN: tl.constexpr, |
| 89 | WIDTH: tl.constexpr, |
| 90 | RECURRENT_ROW_SIZE: tl.constexpr, |
| 91 | NULL_STATE_IDX: tl.constexpr, |
| 92 | BLOCK_SIZE: tl.constexpr, |
| 93 | ): |
| 94 | seq_idx = tl.program_id(0) |
| 95 | cols = tl.program_id(1) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) |
| 96 | state_idx = tl.load(checkpoint_state_indices_ptr + seq_idx).to(tl.int64) |
| 97 | checkpoint_offset = tl.load( |
| 98 | checkpoint_offsets_ptr + seq_idx * checkpoint_offset_stride |
| 99 | ) |
| 100 | valid_checkpoint = (state_idx != NULL_STATE_IDX) & (checkpoint_offset > 0) |
| 101 | valid_conv = ( |
| 102 | (cols < WIDTH * STATE_LEN) & valid_checkpoint & (checkpoint_offset >= STATE_LEN) |
| 103 | ) |
| 104 | width_idx = cols // STATE_LEN |
| 105 | history_idx = cols % STATE_LEN |
| 106 | checkpoint_end = tl.load(query_start_loc_ptr + seq_idx) + checkpoint_offset |
| 107 | token_idx = checkpoint_end - STATE_LEN + history_idx |
| 108 | values = tl.load( |
| 109 | x_ptr + token_idx * x_stride_0 + width_idx * x_stride_1, |
| 110 | mask=valid_conv, |
| 111 | ) |
| 112 | tl.store( |
| 113 | conv_state_ptr |
| 114 | + state_idx * state_stride_0 |
| 115 | + width_idx * state_stride_1 |
| 116 | + history_idx * state_stride_2, |
| 117 | values, |
| 118 | mask=valid_conv, |
| 119 | ) |
| 120 | |
| 121 | valid_recurrent = (cols < RECURRENT_ROW_SIZE) & valid_checkpoint |
| 122 | recurrent = tl.load( |
| 123 | recurrent_checkpoint_ptr + seq_idx * checkpoint_stride_0 + cols, |
| 124 | mask=valid_recurrent, |
| 125 | ) |
| 126 | tl.store( |
| 127 | recurrent_state_ptr + state_idx * recurrent_state_stride_0 + cols, |
| 128 | recurrent, |
| 129 | mask=valid_recurrent, |
| 130 | ) |