main
py 110 lines 3.57 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import pytest
5 import torch
6 import torch.nn as nn
7
8 import vllm
9 from tests.compile.backend import TestBackend
10 from vllm.compilation.passes.utility.scatter_split_replace import (
11 ScatterSplitReplacementPass,
12 )
13 from vllm.compilation.passes.utility.split_coalescing import SplitCoalescingPass
14 from vllm.config import CompilationConfig, CompilationMode, VllmConfig
15 from vllm.model_executor.layers.rotary_embedding import RotaryEmbedding
16 from vllm.platforms import current_platform
17
18 DEVICE_TYPE = current_platform.device_type
19
20
21 class ScatterSplitReplacementModel(nn.Module):
22 """Model with a rope+getitem+slice_scatter+split_with_sizes sequence."""
23
24 def __init__(
25 self,
26 num_heads: int,
27 num_kv_heads: int,
28 head_size: int,
29 dtype: torch.dtype,
30 ):
31 super().__init__()
32 self.q_size = num_heads * head_size
33 self.kv_size = num_kv_heads * head_size
34
35 self.rotary_emb = RotaryEmbedding(
36 head_size,
37 rotary_dim=head_size,
38 max_position_embeddings=4096,
39 base=10000,
40 is_neox_style=True,
41 dtype=dtype,
42 )
43
44 def forward(self, qkv: torch.Tensor, positions: torch.Tensor):
45 # Create copy so inplace ops do not modify the original tensors
46 qkv = qkv.clone()
47 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
48 q, k = self.rotary_emb(positions, q, k)
49 q = q + 1
50 k = k + 2
51 v = v + 3
52 return q, k, v
53
54 def ops_in_model_before(self) -> list[torch._ops.OpOverload]:
55 return [
56 torch.ops.aten.slice_scatter.default,
57 torch.ops.aten.split_with_sizes.default,
58 torch.ops.aten.getitem.default,
59 ]
60
61 def ops_in_model_after(self) -> list[torch._ops.OpOverload]:
62 return [torch.ops.aten.getitem.default]
63
64
65 @pytest.mark.parametrize("dtype", [torch.float16, torch.bfloat16])
66 def test_scatter_split_replace(dtype):
67 torch.set_default_device(DEVICE_TYPE)
68 torch.set_default_dtype(dtype)
69 torch.manual_seed(0)
70
71 num_heads = 8
72 num_kv_heads = 4
73 head_size = 64
74
75 vllm_config = VllmConfig(
76 compilation_config=CompilationConfig(
77 mode=CompilationMode.VLLM_COMPILE,
78 custom_ops=["+rotary_embedding"],
79 ),
80 )
81 with vllm.config.set_current_vllm_config(vllm_config):
82 # ScatterSplitReplacementPass requires SplitCoalescingPass to be run before it
83 coalesce_pass = SplitCoalescingPass(vllm_config)
84 replace_pass = ScatterSplitReplacementPass(vllm_config)
85 passes = [coalesce_pass, replace_pass]
86 backend = TestBackend(*passes)
87
88 model = ScatterSplitReplacementModel(num_heads, num_kv_heads, head_size, dtype)
89
90 T = 5
91 qkv = torch.randn(
92 T, num_heads * head_size + 2 * num_kv_heads * head_size, dtype=dtype
93 )
94 pos = torch.arange(T, dtype=torch.long)
95
96 qkv_eager = qkv.clone()
97 pos_eager = pos.clone()
98 result_eager = model(qkv_eager, pos_eager)
99
100 torch._dynamo.mark_dynamic(qkv, 0)
101 torch._dynamo.mark_dynamic(pos, 0)
102
103 model_compiled = torch.compile(model, backend=backend)
104 result_compiled = model_compiled(qkv, pos)
105
106 for eager, compiled in zip(result_eager, result_compiled):
107 torch.testing.assert_close(eager, compiled)
108
109 assert backend.op_count(torch.ops.aten.slice_scatter.default) == 0
110 assert backend.op_count(torch.ops.aten.split_with_sizes.default) == 1