main
py 225 lines 7.29 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """TRT-LLM Ragged backend for MLA prefill."""
4
5 from typing import TYPE_CHECKING, ClassVar
6
7 import torch
8
9 import vllm.envs as envs
10 from vllm.v1.attention.backends.mla.prefill.base import (
11 MLADimensions,
12 MLAPrefillBackend,
13 )
14 from vllm.v1.attention.backends.utils import log2_lse_to_ln
15 from vllm.v1.worker.workspace import current_workspace_manager
16
17 if TYPE_CHECKING:
18 from vllm.config import VllmConfig
19 from vllm.model_executor.layers.attention.mla_attention import (
20 MLACommonPrefillMetadata,
21 )
22 from vllm.platforms.interface import DeviceCapability
23
24
25 class TrtllmRaggedPrefillBackend(MLAPrefillBackend):
26 """TRT-LLM Ragged backend for MLA prefill."""
27
28 supported_mla_dimensions: ClassVar[list[MLADimensions]] = [
29 MLADimensions(
30 qk_nope_head_dim=128,
31 qk_rope_head_dim=64,
32 v_head_dim=128,
33 ),
34 MLADimensions(
35 qk_nope_head_dim=192,
36 qk_rope_head_dim=64,
37 v_head_dim=256,
38 ),
39 ]
40
41 @staticmethod
42 def get_name() -> str:
43 return "TRTLLM_RAGGED"
44
45 @classmethod
46 def supports_compute_capability(cls, device_capability: "DeviceCapability") -> bool:
47 return device_capability.major == 10
48
49 @classmethod
50 def is_available(cls) -> bool:
51 try:
52 from flashinfer.prefill import (
53 trtllm_ragged_attention_deepseek, # noqa: F401
54 )
55
56 return True
57 except ImportError:
58 return False
59
60 def __init__(
61 self,
62 num_heads: int,
63 scale: float,
64 kv_lora_rank: int,
65 qk_nope_head_dim: int,
66 qk_rope_head_dim: int,
67 v_head_dim: int,
68 vllm_config: "VllmConfig",
69 ) -> None:
70 super().__init__(
71 num_heads=num_heads,
72 scale=scale,
73 kv_lora_rank=kv_lora_rank,
74 qk_nope_head_dim=qk_nope_head_dim,
75 qk_rope_head_dim=qk_rope_head_dim,
76 v_head_dim=v_head_dim,
77 vllm_config=vllm_config,
78 )
79 (self._workspace_buffer,) = current_workspace_manager().get_simultaneous(
80 (
81 (envs.VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE,),
82 torch.uint8,
83 ),
84 )
85
86 def prepare_metadata(
87 self,
88 prefill_metadata: "MLACommonPrefillMetadata",
89 ) -> None:
90 super().prepare_metadata(prefill_metadata)
91 self._query_seq_lens = (
92 prefill_metadata.query_start_loc[1:] - prefill_metadata.query_start_loc[:-1]
93 )
94 query_lens_cpu = prefill_metadata.query_lens_cpu
95 if query_lens_cpu is None:
96 raise ValueError("TRTLLM ragged prefill requires CPU query lengths")
97 if query_lens_cpu.device.type != "cpu":
98 raise ValueError("TRTLLM ragged prefill query lengths must be on CPU")
99 if query_lens_cpu.ndim != 1 or query_lens_cpu.numel() == 0:
100 raise ValueError(
101 "TRTLLM ragged prefill requires a non-empty 1D query-length tensor"
102 )
103 min_query_len = int(torch.min(query_lens_cpu).item())
104 if min_query_len < 0:
105 raise ValueError("TRTLLM ragged prefill query lengths must be non-negative")
106 self._has_active_rows = min_query_len > 0
107 if not self._has_active_rows:
108 has_mixed_rows = bool(torch.any(query_lens_cpu > 0).item())
109 if has_mixed_rows:
110 raise ValueError(
111 "TRTLLM ragged prefill contains mixed active and empty query "
112 f"rows: query_lens={query_lens_cpu.tolist()}"
113 )
114
115 def supports_out(self) -> bool:
116 # Output head dim is v.shape[-1] == v_head_dim, so `out` is unpadded.
117 return True
118
119 def run_prefill_new_tokens(
120 self,
121 q: torch.Tensor,
122 k: torch.Tensor,
123 v: torch.Tensor,
124 return_softmax_lse: bool,
125 out: torch.Tensor | None = None,
126 output_scale: torch.Tensor | None = None,
127 ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:
128 from flashinfer.prefill import trtllm_ragged_attention_deepseek
129
130 if out is None:
131 out = torch.empty(
132 q.shape[0],
133 q.shape[1],
134 v.shape[2],
135 device=q.device,
136 dtype=self._prefill_metadata.output_dtype,
137 )
138
139 if not self._has_active_rows:
140 out.zero_()
141 if return_softmax_lse:
142 lse = torch.empty(
143 self.num_heads,
144 q.shape[0],
145 dtype=torch.float32,
146 device=q.device,
147 )
148 return out, lse.fill_(-float("inf"))
149 return out
150
151 ret = trtllm_ragged_attention_deepseek(
152 query=q,
153 key=k,
154 value=v,
155 workspace_buffer=self._workspace_buffer,
156 seq_lens=self._query_seq_lens,
157 max_q_len=self._prefill_metadata.max_query_len,
158 max_kv_len=self._prefill_metadata.max_query_len,
159 bmm1_scale=self.scale,
160 bmm2_scale=1.0,
161 o_sf_scale=1.0,
162 batch_size=self._query_seq_lens.shape[0],
163 window_left=-1,
164 cum_seq_lens_q=self._prefill_metadata.query_start_loc,
165 cum_seq_lens_kv=self._prefill_metadata.query_start_loc,
166 enable_pdl=False,
167 is_causal=True,
168 return_lse=return_softmax_lse,
169 out=out,
170 skip_all_rows_active_check=True,
171 )
172
173 if isinstance(ret, tuple):
174 # Convert from (q_len, num_heads) to (num_heads, q_len)
175 return ret[0], log2_lse_to_ln(ret[1].transpose(0, 1))
176 return ret
177
178 def run_prefill_context_chunk(
179 self,
180 chunk: "MLACommonPrefillMetadata.ContextChunk",
181 q: torch.Tensor,
182 k: torch.Tensor,
183 v: torch.Tensor,
184 out: torch.Tensor | None = None,
185 ) -> tuple[torch.Tensor, torch.Tensor]:
186 from flashinfer.prefill import trtllm_ragged_attention_deepseek
187
188 if not chunk.all_rows_active:
189 raise ValueError(
190 "TRTLLM ragged context prefill contains an empty query or KV row"
191 )
192
193 if out is None:
194 out = torch.empty(
195 q.shape[0],
196 q.shape[1],
197 v.shape[2],
198 device=q.device,
199 dtype=self._prefill_metadata.output_dtype,
200 )
201
202 attn_out, lse = trtllm_ragged_attention_deepseek(
203 query=q,
204 key=k,
205 value=v,
206 workspace_buffer=self._workspace_buffer,
207 seq_lens=chunk.seq_lens,
208 max_q_len=chunk.max_query_len,
209 max_kv_len=chunk.max_seq_len,
210 bmm1_scale=self.scale,
211 bmm2_scale=1.0,
212 o_sf_scale=1.0,
213 batch_size=chunk.num_requests,
214 window_left=-1,
215 cum_seq_lens_q=chunk.query_start_loc,
216 cum_seq_lens_kv=chunk.cu_seq_lens,
217 enable_pdl=False,
218 is_causal=False,
219 return_lse=True,
220 out=out,
221 skip_all_rows_active_check=True,
222 )
223
224 # Convert from (q_len, num_heads) to (num_heads, q_len)
225 return attn_out, log2_lse_to_ln(lse.transpose(0, 1))