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 14, 05:30 UTC
Feature/vit attention unification# 23880 (#23978)
Signed-off-by: Isotr0py <mozf@mail2.sysu.edu.cn> Co-authored-by: Isotr0py <mozf@mail2.sysu.edu.cn>
baonudesifeizhai committed
Sep 10, 2025 at 09:10 UTC
6cbd41909edaee82786b5a896ccc2fbccd934aa8
9 files changed
+70
-58
tests/kernels/attention/test_mha_attn.py
+17
-5
@@ -23,6 +23,9 @@ def clear_cache():
23
"""Clear lru cache to ensure each test case runs without caching.
24
"""
25
_cached_get_attn_backend.cache_clear()
26
+ # Clear xformers availability cache
27
+ import vllm.attention.layer as layer_module
28
+ layer_module.USE_XFORMERS_OPS = None
29
30
31
@pytest.mark.parametrize("device", ["cpu", "hip", "cuda"])
@@ -33,19 +36,28 @@ def test_mha_attn_platform(device: str):
36
torch.set_default_dtype(torch.float16)
37
38
if device == "cpu":
36
- with patch("vllm.attention.selector.current_platform", CpuPlatform()):
39
+ with patch("vllm.attention.selector.current_platform",
40
+ CpuPlatform()), \
41
+ patch("vllm.platforms.current_platform", CpuPlatform()):
42
attn = MultiHeadAttention(16, 64, scale=1)
38
- assert attn.attn_backend == _Backend.TORCH_SDPA
43
+ assert attn.attn_backend == _Backend.TORCH_SDPA_VLLM_V1
44
elif device == "hip":
40
- with patch("vllm.attention.selector.current_platform", RocmPlatform()):
45
+ with patch("vllm.attention.selector.current_platform",
46
+ RocmPlatform()), \
47
+ patch("vllm.platforms.current_platform", RocmPlatform()), \
48
+ patch("vllm.attention.layer.current_platform", RocmPlatform()):
49
attn = MultiHeadAttention(16, 64, scale=1)
50
assert attn.attn_backend == _Backend.TORCH_SDPA
51
else:
44
- with patch("vllm.attention.selector.current_platform", CudaPlatform()):
52
+ with patch("vllm.attention.selector.current_platform",
53
+ CudaPlatform()), \
54
+ patch("vllm.platforms.current_platform", CudaPlatform()):
55
attn = MultiHeadAttention(16, 64, scale=1)
56
assert attn.attn_backend == _Backend.XFORMERS
57
48
- with patch("vllm.attention.selector.current_platform", CudaPlatform()):
58
+ with patch("vllm.attention.selector.current_platform",
59
+ CudaPlatform()), \
60
+ patch("vllm.platforms.current_platform", CudaPlatform()):
61
attn = MultiHeadAttention(16, 72, scale=1)
62
assert attn.attn_backend == _Backend.XFORMERS
63
vllm/attention/layer.py
+19
-6
@@ -360,13 +360,13 @@ class MultiHeadAttention(nn.Module):
360
# currently, only torch_sdpa is supported on rocm
361
self.attn_backend = _Backend.TORCH_SDPA
362
else:
363
- if backend in (_Backend.FLASH_ATTN, _Backend.FLASH_ATTN_VLLM_V1,
364
- _Backend.FLEX_ATTENTION):
365
- backend = _Backend.XFORMERS
366
-
363
self.attn_backend = backend if backend in {
368
- _Backend.TORCH_SDPA, _Backend.XFORMERS, _Backend.PALLAS_VLLM_V1
369
- } else _Backend.TORCH_SDPA
364
+ _Backend.TORCH_SDPA,
365
+ _Backend.TORCH_SDPA_VLLM_V1,
366
+ _Backend.XFORMERS,
367
+ _Backend.PALLAS_VLLM_V1,
368
+ _Backend.ROCM_AITER_FA,
369
+ } else current_platform.get_vit_attn_backend()
370
371
if (self.attn_backend == _Backend.XFORMERS
372
and not check_xformers_availability()):
@@ -413,6 +413,19 @@ class MultiHeadAttention(nn.Module):
413
from torch_xla.experimental.custom_kernel import flash_attention
414
out = flash_attention(query, key, value, sm_scale=self.scale)
415
out = out.transpose(1, 2)
416
+ elif self.attn_backend == _Backend.ROCM_AITER_FA:
417
+ from aiter import flash_attn_varlen_func
418
+
419
+ # ROCm Flash Attention expects (batch, seq, heads, head_dim)
420
+ out = flash_attn_varlen_func(query,
421
+ key,
422
+ value,
423
+ softmax_scale=self.scale)
424
+ else:
425
+ # ViT attention hasn't supported this backend yet
426
+ raise NotImplementedError(
427
+ f"ViT attention hasn't supported {self.attn_backend} "
428
+ f"backend yet.")
429
430
return out.reshape(bsz, q_len, -1)
431
vllm/model_executor/models/idefics2_vision_model.py
+3
@@ -170,6 +170,7 @@ class Idefics2VisionAttention(nn.Module):
170
quant_config=quant_config,
171
prefix=f"{prefix}.out_proj",
172
)
173
+ # Use unified MultiHeadAttention with Flash Attention support
174
self.attn = MultiHeadAttention(self.num_heads_per_partition,
175
self.head_dim, self.scale)
176
@@ -181,6 +182,8 @@ class Idefics2VisionAttention(nn.Module):
182
hidden_states
183
) # batch_size, q_len, 3 * num_heads_per_partition * head_dim
184
query_states, key_states, value_states = qkv.chunk(3, dim=-1)
185
+
186
+ # Use unified MultiHeadAttention implementation
187
out = self.attn(query_states, key_states, value_states)
188
attn_output, _ = self.out_proj(out)
189
return attn_output
vllm/model_executor/models/intern_vit.py
+6
-5
@@ -255,6 +255,10 @@ class InternSdpaAttention(nn.Module):
255
256
self.proj = nn.Linear(self.dummy_dim, self.embed_dim)
257
258
+ # Use unified MultiHeadAttention with automatic backend selection
259
+ self.attn = MultiHeadAttention(self.num_heads, self.head_dim,
260
+ self.scale)
261
+
262
def forward(self, x: torch.Tensor) -> torch.Tensor:
263
B, N, C = x.shape
264
qkv = self.qkv(x)
@@ -268,12 +272,9 @@ class InternSdpaAttention(nn.Module):
272
B_, N_, H_, D_ = q.shape
273
q = self.q_norm(q.flatten(-2, -1)).view(B_, N_, H_, D_)
274
k = self.k_norm(k.flatten(-2, -1)).view(B_, N_, H_, D_)
271
- q = q.transpose(1, 2)
272
- k = k.transpose(1, 2)
273
- v = v.transpose(1, 2)
275
275
- x = F.scaled_dot_product_attention(q, k, v, scale=self.scale)
276
- x = x.transpose(1, 2).reshape(B, N, -1)
276
+ # Use unified MultiHeadAttention with automatic backend selection
277
+ x = self.attn(q, k, v)
278
279
x = self.proj(x)
280
return x
vllm/model_executor/models/interns1_vit.py
+7
-10
@@ -12,10 +12,10 @@ from typing import Optional
12
13
import torch
14
import torch.nn as nn
15
-import torch.nn.functional as F
15
from transformers import PretrainedConfig
16
from transformers.utils import torch_int
17
18
+from vllm.attention.layer import MultiHeadAttention
19
from vllm.model_executor.layers.activation import get_act_fn
20
from vllm.model_executor.layers.layernorm import RMSNorm
21
from vllm.model_executor.layers.linear import (ColumnParallelLinear,
@@ -206,6 +206,10 @@ class InternSdpaAttention(nn.Module):
206
207
self.projection_layer = nn.Linear(self.dummy_dim, self.embed_dim)
208
209
+ # Use unified MultiHeadAttention with automatic backend selection
210
+ self.attn = MultiHeadAttention(self.num_heads, self.head_dim,
211
+ self.scale)
212
+
213
def forward(self, x: torch.Tensor) -> torch.Tensor:
214
B, N, C = x.shape
215
@@ -213,20 +217,13 @@ class InternSdpaAttention(nn.Module):
217
k = self.k_proj(x)
218
v = self.v_proj(x)
219
216
- q = q.view(B, N, self.num_heads, self.head_dim)
217
- k = k.view(B, N, self.num_heads, self.head_dim)
218
- v = v.view(B, N, self.num_heads, self.head_dim)
219
-
220
if self.qk_normalization:
221
B_, N_, H_, D_ = q.shape
222
q = self.q_norm(q.flatten(-2, -1)).view(B_, N_, H_, D_)
223
k = self.k_norm(k.flatten(-2, -1)).view(B_, N_, H_, D_)
224
- q = q.transpose(1, 2)
225
- k = k.transpose(1, 2)
226
- v = v.transpose(1, 2)
224
228
- x = F.scaled_dot_product_attention(q, k, v, scale=self.scale)
229
- x = x.transpose(1, 2).reshape(B, N, -1)
225
+ # Use unified MultiHeadAttention with automatic backend selection
226
+ x = self.attn(q, k, v)
227
228
x = self.projection_layer(x)
229
return x
vllm/model_executor/models/mllama.py
+9
-15
@@ -35,6 +35,7 @@ from transformers.models.mllama.processing_mllama import (
35
36
import vllm.distributed.parallel_state as ps
37
from vllm.attention import Attention, AttentionMetadata, AttentionType
38
+from vllm.attention.layer import MultiHeadAttention
39
from vllm.attention.ops.paged_attn import PagedAttention
40
from vllm.attention.selector import _Backend
41
from vllm.config import VllmConfig
@@ -517,6 +518,10 @@ class MllamaVisionSdpaAttention(nn.Module):
518
prefix=f"{prefix}.o_proj",
519
)
520
521
+ # Use unified MultiHeadAttention with automatic backend selection
522
+ self.attn = MultiHeadAttention(self.num_local_heads, self.head_dim,
523
+ 1.0 / math.sqrt(self.head_dim))
524
+
525
def forward(
526
self,
527
hidden_state: torch.Tensor,
@@ -524,21 +529,10 @@ class MllamaVisionSdpaAttention(nn.Module):
529
) -> torch.Tensor:
530
qkv, _ = self.qkv_proj(hidden_state)
531
q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
527
- q = q.view(q.shape[0], q.shape[1], self.num_local_heads,
528
- self.head_dim).transpose(1, 2)
529
- k = k.view(k.shape[0], k.shape[1], self.num_local_heads,
530
- self.head_dim).transpose(1, 2)
531
- v = v.view(v.shape[0], v.shape[1], self.num_local_heads,
532
- self.head_dim).transpose(1, 2)
533
-
534
- # TODO: remove padding in image encoder
535
- attn_output = F.scaled_dot_product_attention(q,
536
- k,
537
- v,
538
- attn_mask=attention_mask,
539
- dropout_p=0.0)
540
-
541
- attn_output = attn_output.transpose(1, 2).contiguous()
532
+
533
+ # Use unified MultiHeadAttention with automatic backend selection
534
+ attn_output = self.attn(q, k, v)
535
+
536
attn_output = attn_output.reshape(attn_output.shape[0],
537
attn_output.shape[1], -1)
538
output, _ = self.o_proj(attn_output)
vllm/model_executor/models/step3_vl.py
+7
-16
@@ -16,6 +16,7 @@ from torchvision import transforms
16
from torchvision.transforms.functional import InterpolationMode
17
from transformers import BatchFeature, PretrainedConfig, TensorType
18
19
+from vllm.attention.layer import MultiHeadAttention
20
from vllm.config import VllmConfig
21
from vllm.distributed import get_tensor_model_parallel_world_size
22
from vllm.model_executor.layers.activation import get_act_fn
@@ -682,9 +683,9 @@ class Step3VisionAttention(nn.Module):
683
prefix=f"{prefix}.out_proj",
684
disable_tp=use_data_parallel)
685
685
- def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
686
- return tensor.view(bsz, seq_len, self.num_heads,
687
- self.head_dim).transpose(1, 2).contiguous()
686
+ # Use unified MultiHeadAttention with automatic backend selection
687
+ self.attn = MultiHeadAttention(self.num_heads, self.head_dim,
688
+ self.scale)
689
690
def forward(
691
self,
@@ -696,19 +697,9 @@ class Step3VisionAttention(nn.Module):
697
# get query proj
698
qkv, _ = self.qkv_proj(hidden_states)
699
q, k, v = qkv.chunk(chunks=3, dim=-1)
699
- q = q.view(bsz, tgt_len, self.num_heads, self.head_dim)
700
- k = k.view(bsz, tgt_len, self.num_heads, self.head_dim)
701
- v = v.view(bsz, tgt_len, self.num_heads, self.head_dim)
702
- q = q.transpose(1, 2)
703
- k = k.transpose(1, 2)
704
- v = v.transpose(1, 2)
705
- attn_output = F.scaled_dot_product_attention(q,
706
- k,
707
- v,
708
- scale=self.scale,
709
- is_causal=False)
710
- attn_output = attn_output.transpose(1, 2).reshape(
711
- bsz, tgt_len, self.num_heads * self.head_dim)
700
+
701
+ # Use unified MultiHeadAttention with automatic backend selection
702
+ attn_output = self.attn(q, k, v)
703
704
attn_output, _ = self.out_proj(attn_output)
705
vllm/model_executor/models/vision.py
+1
-1
@@ -122,4 +122,4 @@ def resolve_visual_encoder_outputs(
122
uses_last_layer = feature_sample_layers[-1] in (len(hs_pool) - 1, -1)
123
if post_layer_norm is not None and uses_last_layer:
124
hs_pool[-1] = post_layer_norm(encoder_outputs)
125
- return torch.cat(hs_pool, dim=-1)
125
+ return torch.cat(hs_pool, dim=-1)
\ No newline at end of file
vllm/platforms/interface.py
+1
@@ -48,6 +48,7 @@ class _Backend(enum.Enum):
48
ROCM_AITER_MLA_VLLM_V1 = enum.auto()
49
ROCM_AITER_FA = enum.auto() # used for ViT attn backend
50
TORCH_SDPA = enum.auto()
51
+ TORCH_SDPA_VLLM_V1 = enum.auto()
52
FLASHINFER = enum.auto()
53
FLASHINFER_VLLM_V1 = enum.auto()
54
TRITON_MLA = enum.auto() # Supported by V1