|
7 | 7 | from vllm import _custom_ops as ops |
8 | 8 | from vllm.attention.backends.abstract import AttentionType |
9 | 9 | from vllm.logger import init_logger |
| 10 | +from vllm.platforms import current_platform |
10 | 11 | from vllm.model_executor.layers.quantization.utils.quant_utils import ( |
11 | 12 | QuantKey, |
12 | 13 | kFp8StaticTensorSym, |
|
20 | 21 |
|
21 | 22 | logger = init_logger(__name__) |
22 | 23 |
|
| 24 | +if current_platform.is_rocm(): |
| 25 | + from vllm._aiter_ops import rocm_aiter_ops |
| 26 | + if rocm_aiter_ops.is_enabled(): |
| 27 | + from aiter.ops.triton.fused_kv_cache import fused_qk_rope_reshape_and_cache |
23 | 28 |
|
24 | 29 | class RocmAiterUnifiedAttentionBackend(RocmAttentionBackend): |
25 | 30 | accept_output_buffer: bool = True |
@@ -102,6 +107,7 @@ def forward( |
102 | 107 | output: torch.Tensor | None = None, |
103 | 108 | output_scale: torch.Tensor | None = None, |
104 | 109 | output_block_scale: torch.Tensor | None = None, |
| 110 | + positions: torch.Tensor | None = None, |
105 | 111 | ) -> torch.Tensor: |
106 | 112 | """Forward pass with FlashAttention. |
107 | 113 |
|
@@ -145,23 +151,62 @@ def forward( |
145 | 151 | # key and value may be None in the case of cross attention. They are |
146 | 152 | # calculated once based on the output from the encoder and then cached |
147 | 153 | # in KV cache. |
148 | | - if ( |
149 | | - self.kv_sharing_target_layer_name is None |
150 | | - and key is not None |
151 | | - and value is not None |
152 | | - ): |
153 | | - # Reshape the input keys and values and store them in the cache. |
154 | | - # Skip this if sharing KV cache with an earlier attention layer. |
155 | | - ops.reshape_and_cache_flash( |
156 | | - key, |
157 | | - value, |
158 | | - key_cache, |
159 | | - value_cache, |
160 | | - attn_metadata.slot_mapping, |
161 | | - self.kv_cache_dtype, |
162 | | - layer._k_scale, |
163 | | - layer._v_scale, |
| 154 | + |
| 155 | + if positions is not None and query.shape[0] <= 256 and rocm_aiter_ops.is_enabled(): |
| 156 | + assert self.kv_sharing_target_layer_name is None |
| 157 | + cos_sin_cache = self.rotary_emb.cos_sin_cache |
| 158 | + is_neox = self.rotary_emb.is_neox_style |
| 159 | + cos, sin = cos_sin_cache.chunk(2, dim=-1) |
| 160 | + is_fp8_kv_cache = self.kv_cache_dtype.startswith("fp8") |
| 161 | + if is_fp8_kv_cache: |
| 162 | + key_cache = key_cache.view(current_platform.fp8_dtype()) |
| 163 | + value_cache = value_cache.view(current_platform.fp8_dtype()) |
| 164 | + query, key, key_cache, value_cache, output = ( |
| 165 | + fused_qk_rope_reshape_and_cache( |
| 166 | + query, |
| 167 | + key, |
| 168 | + value, |
| 169 | + key_cache, |
| 170 | + value_cache, |
| 171 | + attn_metadata.slot_mapping, |
| 172 | + positions, |
| 173 | + cos, |
| 174 | + sin, |
| 175 | + layer._k_scale, |
| 176 | + layer._v_scale, |
| 177 | + is_neox, |
| 178 | + flash_layout=True, |
| 179 | + apply_scale=is_fp8_kv_cache, |
| 180 | + offs=None, |
| 181 | + q_out=query, |
| 182 | + k_out=key, |
| 183 | + output_zeros=True, |
| 184 | + zeros_out=output, |
| 185 | + ) |
164 | 186 | ) |
| 187 | + else: |
| 188 | + if positions is not None: |
| 189 | + if current_platform.is_rocm(): |
| 190 | + query, key = self.rotary_emb.forward_cuda(positions, query, key) |
| 191 | + else: |
| 192 | + query, key = self.rotary_emb(positions, query, key) |
| 193 | + if ( |
| 194 | + self.kv_sharing_target_layer_name is None |
| 195 | + and key is not None |
| 196 | + and value is not None |
| 197 | + ): |
| 198 | + # Reshape the input keys and values and store them in the cache. |
| 199 | + # Skip this if sharing KV cache with an earlier attention layer. |
| 200 | + ops.reshape_and_cache_flash( |
| 201 | + key, |
| 202 | + value, |
| 203 | + key_cache, |
| 204 | + value_cache, |
| 205 | + attn_metadata.slot_mapping, |
| 206 | + self.kv_cache_dtype, |
| 207 | + layer._k_scale, |
| 208 | + layer._v_scale, |
| 209 | + ) |
165 | 210 |
|
166 | 211 | if self.kv_cache_dtype.startswith("fp8"): |
167 | 212 | key_cache = key_cache.view(self.fp8_dtype) |
|
0 commit comments