main
py 286 lines 10.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import pytest
4 import torch
5 from torch import nn
6
7 from vllm.compilation.counter import compilation_counter
8 from vllm.compilation.decorators import ignore_torch_compile, support_torch_compile
9 from vllm.config import (
10 CacheConfig,
11 CompilationConfig,
12 CompilationMode,
13 CUDAGraphMode,
14 VllmConfig,
15 set_current_vllm_config,
16 )
17 from vllm.forward_context import BatchDescriptor, set_forward_context
18 from vllm.utils.torch_utils import is_torch_equal_or_newer
19
20 # This import automatically registers `torch.ops.silly.attention`
21 from . import silly_attention # noqa: F401
22
23 BATCH_SIZE = 32
24 MLP_SIZE = 128
25
26
27 @torch.inference_mode
28 def run_model(
29 vllm_config: VllmConfig, model: nn.Module, cudagraph_runtime_mode: CUDAGraphMode
30 ):
31 with set_forward_context({}, vllm_config=vllm_config):
32 # warmup for the model with cudagraph_mode NONE
33 model(torch.randn(BATCH_SIZE, MLP_SIZE).cuda())
34
35 # simulate cudagraphs capturing
36 with set_forward_context(
37 {},
38 vllm_config=vllm_config,
39 cudagraph_runtime_mode=cudagraph_runtime_mode,
40 batch_descriptor=BatchDescriptor(
41 num_tokens=2,
42 ),
43 ):
44 model(torch.randn(2, MLP_SIZE).cuda())
45 with set_forward_context(
46 {},
47 vllm_config=vllm_config,
48 cudagraph_runtime_mode=cudagraph_runtime_mode,
49 batch_descriptor=BatchDescriptor(
50 num_tokens=1,
51 ),
52 ):
53 model(torch.randn(1, MLP_SIZE).cuda())
54
55 # simulate cudagraphs replay
56 with set_forward_context(
57 {},
58 vllm_config=vllm_config,
59 cudagraph_runtime_mode=cudagraph_runtime_mode,
60 batch_descriptor=BatchDescriptor(
61 num_tokens=2,
62 ),
63 ):
64 output = model(torch.randn(2, MLP_SIZE).cuda())
65
66 output = output.cpu()
67 return output.cpu()
68
69
70 @pytest.mark.parametrize("use_inductor_graph_partition", [True, False])
71 def test_ignore_torch_compile_decorator(use_inductor_graph_partition, monkeypatch):
72 # disable compile cache so that we can count the number of compilations
73 # appropriately
74 monkeypatch.setenv("VLLM_DISABLE_COMPILE_CACHE", "1")
75
76 if use_inductor_graph_partition and not is_torch_equal_or_newer("2.9.0.dev"):
77 pytest.skip("inductor graph partition is only available in PyTorch 2.9+")
78
79 # piecewise
80 vllm_config = VllmConfig(
81 compilation_config=CompilationConfig(
82 mode=CompilationMode.VLLM_COMPILE,
83 splitting_ops=["silly::attention"],
84 cudagraph_capture_sizes=[1, 2],
85 use_inductor_graph_partition=use_inductor_graph_partition,
86 )
87 )
88 cudagraph_runtime_mode = CUDAGraphMode.PIECEWISE
89
90 expected_num_graphs_seen = 1
91 expected_num_cudagraph_captured = (
92 4 # num_cudagraph_sizes * num cudagraphs to capture
93 )
94 if use_inductor_graph_partition:
95 expected_num_piecewise_graphs_seen = 1
96 expected_num_piecewise_capturable_graphs_seen = 1
97 expected_num_backend_compilations = 1
98 else:
99 expected_num_piecewise_graphs_seen = 3
100 expected_num_piecewise_capturable_graphs_seen = 2
101 expected_num_backend_compilations = 2
102
103 @support_torch_compile
104 class A(nn.Module):
105 def __init__(
106 self, *, vllm_config: VllmConfig, prefix: str = "", **kwargs
107 ) -> None:
108 super().__init__()
109
110 def forward(self, x: torch.Tensor) -> torch.Tensor:
111 x = x + x
112 attn_output = torch.empty_like(x)
113 torch.ops.silly.attention(x, x, x, attn_output)
114 x = attn_output
115 x = x * 3
116 return x
117
118 @ignore_torch_compile
119 class B(A): ...
120
121 @support_torch_compile
122 class C(B): ...
123
124 with set_current_vllm_config(vllm_config):
125 mod_A = A(vllm_config=vllm_config, prefix="").eval().cuda()
126
127 # A has support_torch_compile
128 with compilation_counter.expect(
129 num_graphs_seen=expected_num_graphs_seen,
130 num_piecewise_graphs_seen=expected_num_piecewise_graphs_seen,
131 num_piecewise_capturable_graphs_seen=expected_num_piecewise_capturable_graphs_seen,
132 num_backend_compilations=expected_num_backend_compilations,
133 num_cudagraph_captured=expected_num_cudagraph_captured,
134 ):
135 run_model(vllm_config, mod_A, cudagraph_runtime_mode)
136
137 with set_current_vllm_config(vllm_config):
138 mod_B = B(vllm_config=vllm_config, prefix="").eval().cuda()
139
140 # B's ignore_torch_compile should override A's support_torch_compile
141 with compilation_counter.expect(
142 num_graphs_seen=0,
143 num_piecewise_graphs_seen=0,
144 num_piecewise_capturable_graphs_seen=0,
145 num_backend_compilations=0,
146 num_cudagraph_captured=0,
147 ):
148 run_model(vllm_config, mod_B, cudagraph_runtime_mode)
149
150 with set_current_vllm_config(vllm_config):
151 mod_C = C(vllm_config=vllm_config, prefix="").eval().cuda()
152
153 # C's support_torch_compile should override B's ignore_torch_compile
154 with compilation_counter.expect(
155 num_graphs_seen=expected_num_graphs_seen,
156 num_piecewise_graphs_seen=expected_num_piecewise_graphs_seen,
157 num_piecewise_capturable_graphs_seen=expected_num_piecewise_capturable_graphs_seen,
158 num_backend_compilations=expected_num_backend_compilations,
159 num_cudagraph_captured=expected_num_cudagraph_captured,
160 ):
161 run_model(vllm_config, mod_C, cudagraph_runtime_mode)
162
163
164 # Only enable torch.compile if
165 # vllm_config.cache_config.kv_sharing_fast_prefill=True
166 @support_torch_compile(
167 enable_if=lambda vllm_config: vllm_config.cache_config.kv_sharing_fast_prefill
168 )
169 class B(nn.Module):
170 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "", **kwargs) -> None:
171 super().__init__()
172
173 def forward(self, x: torch.Tensor) -> torch.Tensor:
174 x = x + x
175 attn_output = torch.empty_like(x)
176 torch.ops.silly.attention(x, x, x, attn_output)
177 x = attn_output
178 x = x + x
179 return x
180
181
182 # Only enable torch.compile if
183 # vllm_config.cache_config.kv_sharing_fast_prefill=False
184 @support_torch_compile(
185 enable_if=lambda vllm_config: not vllm_config.cache_config.kv_sharing_fast_prefill
186 )
187 class A(nn.Module):
188 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "", **kwargs) -> None:
189 super().__init__()
190 self.mod1 = B(vllm_config=vllm_config, prefix=prefix, **kwargs)
191 self.mod2 = B(vllm_config=vllm_config, prefix=prefix, **kwargs)
192
193 def forward(self, x: torch.Tensor) -> torch.Tensor:
194 x = self.mod1(x)
195 attn_output = torch.empty_like(x)
196 torch.ops.silly.attention(x, x, x, attn_output)
197 x = attn_output
198 x = self.mod2(x)
199 return x
200
201
202 @pytest.mark.parametrize("use_inductor_graph_partition", [True, False])
203 def test_conditional_compile_enable_if(use_inductor_graph_partition, monkeypatch):
204 # disable compile cache so that we can count the number of compilations
205 # appropriately
206 monkeypatch.setenv("VLLM_DISABLE_COMPILE_CACHE", "1")
207
208 if use_inductor_graph_partition and not is_torch_equal_or_newer("2.9.0.dev"):
209 pytest.skip("inductor graph partition is only available in PyTorch 2.9+")
210
211 vllm_config = VllmConfig(
212 cache_config=CacheConfig(
213 kv_sharing_fast_prefill=True,
214 ),
215 compilation_config=CompilationConfig(
216 mode=CompilationMode.VLLM_COMPILE,
217 splitting_ops=["silly::attention"],
218 cudagraph_capture_sizes=[1, 2],
219 use_inductor_graph_partition=use_inductor_graph_partition,
220 ),
221 )
222 cudagraph_runtime_mode = CUDAGraphMode.PIECEWISE
223
224 with set_current_vllm_config(vllm_config):
225 mod_A = A(vllm_config=vllm_config, prefix="").eval().cuda()
226
227 if use_inductor_graph_partition:
228 expected_num_piecewise_graphs_seen = 2
229 expected_num_piecewise_capturable_graphs_seen = 2
230 expected_num_backend_compilations = 2
231 else:
232 expected_num_piecewise_graphs_seen = 6
233 expected_num_piecewise_capturable_graphs_seen = 4
234 expected_num_backend_compilations = 4
235
236 # A has support_torch_compile but enable_if fn returns False
237 # enable_if will be True for B, so we expect mod1 and mod2
238 # to be compiled
239 with compilation_counter.expect(
240 num_graphs_seen=2,
241 num_piecewise_graphs_seen=expected_num_piecewise_graphs_seen,
242 # 3 piecewise graphs per instance of B()
243 num_piecewise_capturable_graphs_seen=expected_num_piecewise_capturable_graphs_seen,
244 num_backend_compilations=expected_num_backend_compilations,
245 num_cudagraph_captured=8,
246 # num_cudagraph_sizes * num cudagraphable graphs to capture
247 ):
248 run_model(vllm_config, mod_A, cudagraph_runtime_mode)
249
250 # Set kv_sharing_fast_prefill=False
251 # which will cause A to be compiled and B to not be compiled
252 vllm_config = VllmConfig(
253 cache_config=CacheConfig(
254 kv_sharing_fast_prefill=False,
255 ),
256 compilation_config=CompilationConfig(
257 mode=CompilationMode.VLLM_COMPILE,
258 splitting_ops=["silly::attention"],
259 cudagraph_capture_sizes=[1, 2],
260 use_inductor_graph_partition=use_inductor_graph_partition,
261 ),
262 )
263
264 with set_current_vllm_config(vllm_config):
265 mod_A = A(vllm_config=vllm_config, prefix="").eval().cuda()
266
267 if use_inductor_graph_partition:
268 expected_num_piecewise_graphs_seen = 1
269 expected_num_piecewise_capturable_graphs_seen = 1
270 expected_num_backend_compilations = 1
271 else:
272 # 3 attn ops and 4 non-attn ops
273 expected_num_piecewise_graphs_seen = 7
274 expected_num_piecewise_capturable_graphs_seen = 4
275 expected_num_backend_compilations = 4
276
277 with compilation_counter.expect(
278 num_graphs_seen=1,
279 num_piecewise_graphs_seen=expected_num_piecewise_graphs_seen,
280 # 3 attn ops and 4 non-attn ops
281 num_piecewise_capturable_graphs_seen=expected_num_piecewise_capturable_graphs_seen,
282 num_backend_compilations=expected_num_backend_compilations,
283 num_cudagraph_captured=8,
284 # num_cudagraph_sizes * num cudagraphable graphs to capture
285 ):
286 run_model(vllm_config, mod_A, cudagraph_runtime_mode)