main
py 177 lines 5.26 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4
5 import os
6
7 import pytest
8 import torch
9
10 from vllm.compilation.counter import compilation_counter
11 from vllm.compilation.wrapper import (
12 TorchCompileWithNoGuardsWrapper,
13 compile_model_with_stock_torch,
14 )
15 from vllm.config import (
16 CompilationConfig,
17 CompilationMode,
18 VllmConfig,
19 set_current_vllm_config,
20 )
21
22
23 class MyMod(torch.nn.Module):
24 def forward(self, x: torch.Tensor, cache: torch.Tensor | None = None):
25 if x.size()[0] >= 4:
26 return x * 2
27 else:
28 return x * 100
29
30
31 class MyWrapper(TorchCompileWithNoGuardsWrapper):
32 def __init__(self, model):
33 self.model = model
34 super().__init__()
35
36 def forward(self, x: torch.Tensor): # type: ignore[override]
37 # this is the function to be compiled
38 return self.model(x)
39
40
41 @pytest.mark.parametrize("use_bytecode_hook", [True, False])
42 def test_torch_compile_wrapper(use_bytecode_hook, monkeypatch):
43 """Test basic functionality of TorchCompileWithNoGuardsWrapper."""
44 # Set the environment variable for this test
45 monkeypatch.setenv("VLLM_USE_BYTECODE_HOOK", "1" if use_bytecode_hook else "0")
46
47 # Create a proper vLLM config instead of mocking
48 vllm_config = VllmConfig()
49 vllm_config.compilation_config = CompilationConfig()
50 vllm_config.compilation_config.mode = CompilationMode.DYNAMO_TRACE_ONCE
51 vllm_config.compilation_config.backend = "inductor"
52
53 # Test DYNAMO_TRACE_ONCE
54 with set_current_vllm_config(vllm_config):
55 torch._dynamo.reset()
56 mod = MyMod()
57 wrapper = MyWrapper(mod)
58
59 # First call should trigger compilation
60 x = torch.tensor([1, 2, 3, 4])
61 torch._dynamo.mark_dynamic(x, 0)
62
63 result1 = wrapper(x)
64 expected1 = torch.tensor([2, 4, 6, 8])
65 assert torch.allclose(result1, expected1), (
66 f"Expected {expected1}, got {result1}"
67 )
68
69 # Second call should use compiled code
70 x2 = torch.tensor([1, 2, 3])
71 result2 = wrapper(x2)
72 expected2 = torch.tensor([2, 4, 6])
73 assert torch.allclose(result2, expected2), (
74 f"Expected {expected2}, got {result2}"
75 )
76
77 # without the wrapper result would be different.
78 result3 = mod(x2)
79 expected3 = torch.tensor([100, 200, 300])
80
81 assert torch.allclose(result3, expected3), (
82 f"Expected {result3}, got {expected3}"
83 )
84
85 # with STOCK_TORCH_COMPILE we do not remove guards.
86 vllm_config.compilation_config.mode = CompilationMode.STOCK_TORCH_COMPILE
87 torch._dynamo.reset()
88 with set_current_vllm_config(vllm_config):
89 mod = MyMod()
90 wrapper = MyWrapper(mod)
91
92 # First call should trigger compilation
93 x = torch.tensor([1, 2, 3, 4])
94 torch._dynamo.mark_dynamic(x, 0)
95
96 result1 = wrapper(x)
97 expected1 = torch.tensor([2, 4, 6, 8])
98 assert torch.allclose(result1, expected1), (
99 f"Expected {expected1}, got {result1}"
100 )
101
102 # Second call should trigger another compilation
103 x2 = torch.tensor([1, 2, 3])
104 result2 = wrapper(x2)
105 expected2 = torch.tensor([100, 200, 300])
106 assert torch.allclose(result2, expected2), (
107 f"Expected {expected2}, got {result2}"
108 )
109
110 # NO_COMPILATION level not supported.
111 vllm_config.compilation_config.mode = None
112 torch._dynamo.reset()
113 with set_current_vllm_config(vllm_config):
114 torch._dynamo.reset()
115 mod = MyMod()
116
117 try:
118 wrapper = MyWrapper(mod)
119 except Exception:
120 return
121 raise AssertionError("expected an exception to be raised")
122
123
124 def test_compile_model_with_stock_torch(monkeypatch):
125 """The whole model is compiled in place with the configured backend."""
126 graphs: list[torch.fx.GraphModule] = []
127
128 def recording_backend(gm: torch.fx.GraphModule, example_inputs):
129 graphs.append(gm)
130 return gm.forward
131
132 monkeypatch.setattr(
133 CompilationConfig,
134 "init_backend",
135 lambda self, vllm_config, *args, **kwargs: recording_backend,
136 )
137
138 class Model(torch.nn.Module):
139 def __init__(self):
140 super().__init__()
141 self.linear = torch.nn.Linear(4, 2)
142
143 def forward(self, x: torch.Tensor):
144 return self.linear(x).relu()
145
146 vllm_config = VllmConfig()
147 vllm_config.compilation_config = CompilationConfig(
148 mode=CompilationMode.STOCK_TORCH_COMPILE
149 )
150 model = Model()
151 x = torch.randn(3, 4)
152 expected = model(x)
153
154 torch._dynamo.reset()
155 with compilation_counter.expect(stock_torch_compile_count=1):
156 compile_model_with_stock_torch(model, vllm_config)
157 assert not graphs # compilation is deferred to the first call
158 torch.testing.assert_close(model(x), expected)
159 assert len(graphs) == 1
160
161
162 if __name__ == "__main__":
163 # Run with both parameter values
164
165 class MockMonkeypatch:
166 def setenv(self, name, value):
167 os.environ[name] = value
168
169 mp = MockMonkeypatch()
170
171 print("Testing with VLLM_USE_BYTECODE_HOOK=False")
172 test_torch_compile_wrapper(False, mp)
173
174 print("Testing with VLLM_USE_BYTECODE_HOOK=True")
175 test_torch_compile_wrapper(True, mp)
176
177 print("All tests passed!")