[Kernel][CI] `--jit-monitor-mode error` e2e tests for kernel warmup infra (#50109)

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai>

Nicolò Lucchesi committed Jul 31, 2026 at 14:53 UTC 864a87febb5b4f3fe15b50520a6857b241d5bf63
10 files changed +716 -539
.buildkite/intel_jobs/engine_intel.yaml
+2 -2
@@ -33,12 +33,12 @@ steps:
33 - tests/test_config
34 - tests/test_logger
35 - tests/test_vllm_port
36 - - tests/test_jit_monitor.py
36 + - tests/jit_monitor/test_hooks.py
37 commands:
38 - >-
39 bash .buildkite/scripts/hardware_ci/run-intel-test.sh
40 'cd tests &&
41 - pytest -v -s engine/test_arg_utils.py test_sequence.py test_logger.py test_vllm_port.py test_jit_monitor.py'
41 + pytest -v -s engine/test_arg_utils.py test_sequence.py test_logger.py test_vllm_port.py jit_monitor/test_hooks.py'
42
43 - label: Engine (1 GPU)
44 timeout_in_minutes: 30
.buildkite/test-amd.yaml
+1 -1
@@ -971,7 +971,7 @@ steps:
971 - tests/test_logger
972 - tests/test_vllm_port
973 commands:
974 - - pytest -v -s engine test_sequence.py test_config.py test_logger.py test_vllm_port.py test_jit_monitor.py
974 + - pytest -v -s engine test_sequence.py test_config.py test_logger.py test_vllm_port.py jit_monitor/test_hooks.py jit_monitor/test_hooks_gpu.py
975
976 #-------------------------------------------------------- mi300 · entrypoints --------------------------------------------------------#
977
.buildkite/test_areas/engine.yaml
+3 -2
@@ -23,9 +23,10 @@ steps:
23 - tests/test_config
24 - tests/test_logger
25 - tests/test_vllm_port
26 - - tests/test_jit_monitor.py
26 + - tests/jit_monitor/test_hooks.py
27 + - tests/jit_monitor/test_hooks_gpu.py
28 commands:
28 - - pytest -v -s engine test_sequence.py test_config.py test_logger.py test_vllm_port.py test_jit_monitor.py
29 + - pytest -v -s engine test_sequence.py test_config.py test_logger.py test_vllm_port.py jit_monitor/test_hooks.py jit_monitor/test_hooks_gpu.py
30 mirror:
31 amd:
32 dind: false
.buildkite/test_areas/jit_monitor.yaml new
+22
@@ -0,0 +1,22 @@
1 +group: JIT Monitor
2 +depends_on:
3 + - image-build
4 +steps:
5 +- label: No Runtime JITs e2e tests
6 + key: jit-monitor-no-runtime-jit
7 + device: h200_35gb
8 + timeout_in_minutes: 45
9 + source_file_dependencies:
10 + - vllm/utils/jit_monitor.py
11 + - vllm/v1/worker/gpu_worker.py
12 + - vllm/model_executor/warmup/
13 + - vllm/config/observability.py
14 + - tests/jit_monitor/test_no_runtime_jit.py
15 + - tests/models/registry.py
16 + commands:
17 + # Boot a curated JIT-heavy model set with the JIT monitor in "error" mode
18 + # and run generation; any post-warmup JIT compilation fails the test.
19 + # Per-test watchdog so a wedged engine/CUDA init fails with a traceback
20 + # instead of running until the build timeout.
21 + - export PYTHONFAULTHANDLER=1
22 + - pytest -v -s jit_monitor/test_no_runtime_jit.py --timeout=900 --timeout-method=thread
tests/jit_monitor/__init__.py
tests/jit_monitor/conftest.py new
+27
@@ -0,0 +1,27 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +
4 +import pytest
5 +
6 +from vllm.utils import jit_monitor
7 +
8 +
9 +@pytest.fixture(autouse=True)
10 +def _reset_monitor():
11 + """Reset global monitor state between tests.
12 +
13 + ``activate()`` installs process-global hooks and flips module globals, so
14 + without this every test would inherit the previous test's monitor state.
15 + """
16 +
17 + def reset():
18 + jit_monitor._active = False
19 + jit_monitor._mode = "warn"
20 + jit_monitor._verbose = False
21 + jit_monitor._cutedsl_hook_installed = False
22 + jit_monitor._tilelang_hook_installed = False
23 + jit_monitor._tilelang_jitimpl_compile_depth = 0
24 +
25 + reset()
26 + yield
27 + reset()
tests/jit_monitor/test_hooks.py new
+435
@@ -0,0 +1,435 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""Unit tests for the JIT monitor hooks. Backends are mocked, so no GPU."""
4 +
5 +import inspect
6 +import os
7 +import sys
8 +from contextlib import contextmanager
9 +from types import ModuleType, SimpleNamespace
10 +from typing import Any, cast
11 +from unittest import mock
12 +
13 +import pytest
14 +
15 +from vllm.utils import jit_monitor
16 +
17 +pytestmark = pytest.mark.cpu_test
18 +
19 +
20 +# ------------------------------------------------------------------
21 +# Helpers — lightweight stand-ins for the modules ``activate()`` patches
22 +# ------------------------------------------------------------------
23 +
24 +
25 +def _make_fake_knobs(*, autotuning_print=False, jit_hook=None):
26 + """Build a minimal fake ``triton.knobs`` namespace."""
27 + autotuning = SimpleNamespace(print=autotuning_print)
28 + runtime = SimpleNamespace(jit_post_compile_hook=jit_hook)
29 + return SimpleNamespace(autotuning=autotuning, runtime=runtime)
30 +
31 +
32 +def _fake_cute_import_modules(compile_fn):
33 + """Fake Python's parent package + submodule for ``import cutlass.cute``."""
34 + fake_cute = cast(Any, ModuleType("cutlass.cute"))
35 + fake_cute.compile = compile_fn
36 + fake_parent_package = cast(Any, ModuleType("cutlass"))
37 + fake_parent_package.__path__ = []
38 + fake_parent_package.cute = fake_cute
39 + return {
40 + "cutlass": fake_parent_package,
41 + "cutlass.cute": fake_cute,
42 + }
43 +
44 +
45 +def _fake_cute_compile(*args, **kwargs):
46 + return "compiled"
47 +
48 +
49 +def _fake_tilelang_import_modules():
50 + """Fake Python's TileLang modules touched by ``jit_monitor.activate``."""
51 +
52 + class FakeJITKernel:
53 + def __init__(self, *args, **kwargs):
54 + pass
55 +
56 + class FakeJITImpl:
57 + def __init__(self, func, signature):
58 + self.func = func
59 + self.signature = signature
60 + self.mode = "lazy"
61 + self._kernel_cache = {}
62 +
63 + def __call__(self, *args, **kwargs):
64 + key, _ = self.func.parse_args(*args, **kwargs)
65 + kernel = self._kernel_cache.get(key)
66 + if kernel is None:
67 + kernel = "compiled"
68 + self._kernel_cache[key] = kernel
69 + return kernel
70 +
71 + fake_kernel = cast(Any, ModuleType("tilelang.jit.kernel"))
72 + fake_kernel.JITKernel = FakeJITKernel
73 +
74 + fake_jit = cast(Any, ModuleType("tilelang.jit"))
75 + fake_jit.JITImpl = FakeJITImpl
76 + fake_jit.kernel = fake_kernel
77 +
78 + fake_tilelang = cast(Any, ModuleType("tilelang"))
79 + fake_tilelang.jit = fake_jit
80 +
81 + return {
82 + "tilelang": fake_tilelang,
83 + "tilelang.jit": fake_jit,
84 + "tilelang.jit.kernel": fake_kernel,
85 + }
86 +
87 +
88 +@contextmanager
89 +def _patch_jit_modules(fake_knobs, *, cute_compile=_fake_cute_compile):
90 + """Patch the Triton and CuTeDSL imports touched by ``jit_monitor.activate``."""
91 + fake_triton = cast(Any, ModuleType("triton"))
92 + fake_triton.knobs = fake_knobs
93 + with (
94 + mock.patch.dict(
95 + sys.modules,
96 + {
97 + "triton": fake_triton,
98 + **_fake_cute_import_modules(cute_compile),
99 + **_fake_tilelang_import_modules(),
100 + },
101 + ),
102 + mock.patch.object(jit_monitor, "HAS_TRITON", True),
103 + ):
104 + yield
105 +
106 +
107 +def _triton_hook_kwargs(name: str):
108 + return dict(
109 + key="k",
110 + repr="r",
111 + fn=SimpleNamespace(name=name),
112 + compile=lambda: None,
113 + is_manual_warmup=False,
114 + already_compiled=False,
115 + )
116 +
117 +
118 +# ------------------------------------------------------------------
119 +# activate()
120 +# ------------------------------------------------------------------
121 +
122 +
123 +def test_activate_sets_active():
124 + assert not jit_monitor.is_active()
125 + with _patch_jit_modules(_make_fake_knobs()):
126 + jit_monitor.activate()
127 + assert jit_monitor.is_active()
128 +
129 +
130 +def test_activate_is_idempotent():
131 + fake = _make_fake_knobs()
132 + with _patch_jit_modules(fake):
133 + jit_monitor.activate()
134 + first_hook = fake.runtime.jit_post_compile_hook
135 + jit_monitor.activate()
136 + assert fake.runtime.jit_post_compile_hook is first_hook
137 +
138 +
139 +def test_activate_logs_info():
140 + with (
141 + mock.patch.object(jit_monitor.logger, "info") as m,
142 + _patch_jit_modules(_make_fake_knobs()),
143 + ):
144 + jit_monitor.activate()
145 + m.assert_called_once()
146 + assert "Kernel JIT monitor activated" in m.call_args[0][0]
147 +
148 +
149 +def test_activate_rejects_unknown_mode():
150 + with pytest.raises(ValueError, match="Unsupported JIT monitor mode"):
151 + jit_monitor.activate(mode="panic") # type: ignore[arg-type]
152 +
153 +
154 +def test_activate_without_triton():
155 + with mock.patch.object(jit_monitor, "HAS_TRITON", False):
156 + jit_monitor.activate()
157 + assert jit_monitor.is_active()
158 +
159 +
160 +# ------------------------------------------------------------------
161 +# Triton autotuning print
162 +# ------------------------------------------------------------------
163 +
164 +
165 +def test_autotuning_print_is_enabled():
166 + fake = _make_fake_knobs(autotuning_print=False)
167 + with _patch_jit_modules(fake):
168 + jit_monitor.activate()
169 + assert fake.autotuning.print is True
170 +
171 +
172 +def test_autotuning_print_respects_user_opt_out():
173 + fake = _make_fake_knobs(autotuning_print=False)
174 + with (
175 + mock.patch.dict(os.environ, {"TRITON_PRINT_AUTOTUNING": "0"}),
176 + _patch_jit_modules(fake),
177 + ):
178 + jit_monitor.activate()
179 + assert fake.autotuning.print is False
180 +
181 +
182 +def test_autotuning_print_noop_when_user_already_enabled():
183 + fake = _make_fake_knobs(autotuning_print=True)
184 + with (
185 + mock.patch.dict(os.environ, {"TRITON_PRINT_AUTOTUNING": "1"}),
186 + _patch_jit_modules(fake),
187 + ):
188 + jit_monitor.activate()
189 + assert fake.autotuning.print is True
190 +
191 +
192 +# ------------------------------------------------------------------
193 +# Triton JIT hook
194 +# ------------------------------------------------------------------
195 +
196 +
197 +def test_triton_hook_is_registered():
198 + fake = _make_fake_knobs()
199 + assert fake.runtime.jit_post_compile_hook is None
200 + with _patch_jit_modules(fake):
201 + jit_monitor.activate()
202 + assert fake.runtime.jit_post_compile_hook is not None
203 +
204 +
205 +def test_triton_hook_logs_warning():
206 + fake = _make_fake_knobs()
207 + with _patch_jit_modules(fake):
208 + jit_monitor.activate()
209 +
210 + hook = fake.runtime.jit_post_compile_hook
211 +
212 + with (
213 + mock.patch.object(jit_monitor.logger, "warning_once") as m,
214 + mock.patch.object(jit_monitor.logger, "warning") as warning,
215 + ):
216 + hook(**_triton_hook_kwargs("test_kernel"))
217 +
218 + m.assert_called_once()
219 + warning.assert_not_called()
220 + msg = m.call_args[0][0] % m.call_args[0][1:]
221 + assert "Triton kernel JIT compilation during inference" in msg
222 + assert "test_kernel" in msg
223 +
224 +
225 +def test_triton_hook_chains_existing_hook():
226 + existing = mock.MagicMock(return_value="existing_result")
227 + fake = _make_fake_knobs(jit_hook=existing)
228 + with _patch_jit_modules(fake):
229 + jit_monitor.activate()
230 +
231 + hook = fake.runtime.jit_post_compile_hook
232 + result = hook(**_triton_hook_kwargs("chained_kernel"))
233 +
234 + existing.assert_called_once()
235 + assert result == "existing_result"
236 +
237 +
238 +def test_triton_hook_works_without_existing_hook():
239 + fake = _make_fake_knobs(jit_hook=None)
240 + with _patch_jit_modules(fake):
241 + jit_monitor.activate()
242 +
243 + hook = fake.runtime.jit_post_compile_hook
244 + assert hook(**_triton_hook_kwargs("solo_kernel")) is None
245 +
246 +
247 +def test_triton_hook_error_mode_raises():
248 + fake = _make_fake_knobs()
249 + with _patch_jit_modules(fake):
250 + jit_monitor.activate(mode="error")
251 +
252 + hook = fake.runtime.jit_post_compile_hook
253 + with pytest.raises(RuntimeError, match="Triton kernel JIT compilation"):
254 + hook(**_triton_hook_kwargs("error_kernel"))
255 +
256 +
257 +# ------------------------------------------------------------------
258 +# CuTeDSL hook
259 +# ------------------------------------------------------------------
260 +
261 +
262 +def test_cutedsl_compile_logs_warning():
263 + with _patch_jit_modules(_make_fake_knobs(), cute_compile=_fake_cute_compile):
264 + import cutlass.cute as cute
265 +
266 + jit_monitor.activate()
267 + with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
268 + result = cute.compile(lambda: None, "arg", option=True)
269 +
270 + assert result == "compiled"
271 + warning_once.assert_called_once()
272 + msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
273 + assert "CuTeDSL JIT compilation during inference" in msg
274 +
275 +
276 +def test_cutedsl_compile_logs_verbose_warning():
277 + with _patch_jit_modules(_make_fake_knobs(), cute_compile=_fake_cute_compile):
278 + import cutlass.cute as cute
279 +
280 + jit_monitor.activate(verbose=True)
281 + with mock.patch.object(jit_monitor.logger, "warning") as warning:
282 + result = cute.compile(lambda: None, "arg", option=True)
283 +
284 + assert result == "compiled"
285 + warning.assert_called_once()
286 + msg = warning.call_args[0][0] % warning.call_args[0][1:]
287 + assert "CuTeDSL JIT compilation during inference" in msg
288 +
289 +
290 +def test_cutedsl_error_mode_raises():
291 + with _patch_jit_modules(_make_fake_knobs(), cute_compile=_fake_cute_compile):
292 + import cutlass.cute as cute
293 +
294 + jit_monitor.activate(mode="error")
295 + with pytest.raises(RuntimeError, match="CuTeDSL JIT compilation"):
296 + cute.compile(lambda: None, "arg", option=True)
297 +
298 +
299 +def test_cutedsl_subscripted_compile_is_monitored():
300 + """``cute.compile[options](...)`` (flashinfer >= 0.6.14) must work."""
301 +
302 + class FakeCompileCallable:
303 + def __getitem__(self, options):
304 + return self
305 +
306 + def __call__(self, *args, **kwargs):
307 + return "compiled"
308 +
309 + with _patch_jit_modules(_make_fake_knobs(), cute_compile=FakeCompileCallable()):
310 + import cutlass.cute as cute
311 +
312 + jit_monitor.activate()
313 + with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
314 + result = cute.compile[("opt_level", 3)](lambda: None, "arg")
315 +
316 + assert result == "compiled"
317 + warning_once.assert_called_once()
318 +
319 +
320 +# ------------------------------------------------------------------
321 +# TileLang hook
322 +# ------------------------------------------------------------------
323 +
324 +
325 +def test_tilelang_jit_kernel_logs_warning():
326 + with _patch_jit_modules(_make_fake_knobs()):
327 + from tilelang.jit.kernel import JITKernel
328 +
329 + func = SimpleNamespace(attrs={"global_symbol": "tl_kernel"})
330 + jit_monitor.activate()
331 + with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
332 + JITKernel(func=func, out_idx=None, execution_backend="tvm_ffi")
333 +
334 + warning_once.assert_called_once()
335 + msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
336 + assert "TileLang JIT compilation during inference" in msg
337 + assert "tl_kernel" in msg
338 +
339 +
340 +def test_tilelang_jit_impl_logs_warning():
341 + with _patch_jit_modules(_make_fake_knobs()):
342 + from tilelang.jit import JITImpl
343 +
344 + def tilelang_fn(
345 + gemm_out_mul,
346 + hidden_size: int,
347 + n_splits: int = 1,
348 + hc_mult: int = 4,
349 + ):
350 + return None
351 +
352 + class FakeFunc:
353 + orig_func = tilelang_fn
354 +
355 + def parse_args(self, *args, **kwargs):
356 + return (
357 + (
358 + "tilelang_key",
359 + kwargs["hidden_size"],
360 + kwargs.get("n_splits", 1),
361 + ),
362 + {},
363 + )
364 +
365 + def set_mode(self, mode):
366 + self.mode = mode
367 +
368 + tensor = SimpleNamespace(
369 + shape=(2, 16, 24),
370 + dtype="float32",
371 + device="cuda:0",
372 + )
373 + impl = JITImpl(FakeFunc(), inspect.signature(tilelang_fn))
374 +
375 + jit_monitor.activate()
376 + with (
377 + mock.patch.object(jit_monitor.logger, "warning_once") as warning_once,
378 + mock.patch.object(jit_monitor.logger, "warning") as warning,
379 + ):
380 + impl(tensor, hidden_size=7168, n_splits=2)
381 +
382 + warning_once.assert_called_once()
383 + warning.assert_not_called()
384 + msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
385 + assert "TileLang JIT compilation during inference" in msg
386 + assert "tilelang_fn" in msg
387 +
388 +
389 +def test_tilelang_jit_impl_does_not_log_on_cache_hit():
390 + with _patch_jit_modules(_make_fake_knobs()):
391 + from tilelang.jit import JITImpl
392 +
393 + def tilelang_fn(gemm_out_mul, n_splits: int = 1):
394 + return None
395 +
396 + class FakeFunc:
397 + orig_func = tilelang_fn
398 +
399 + def parse_args(self, *args, **kwargs):
400 + return (("tilelang_key", kwargs.get("n_splits", 1)), {})
401 +
402 + def set_mode(self, mode):
403 + self.mode = mode
404 +
405 + tensor = SimpleNamespace(shape=(2, 16, 24), dtype="float32")
406 + impl = JITImpl(FakeFunc(), inspect.signature(tilelang_fn))
407 +
408 + jit_monitor.activate()
409 + with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
410 + impl(tensor, n_splits=2)
411 + impl(tensor, n_splits=2)
412 +
413 + warning_once.assert_called_once()
414 +
415 +
416 +def test_tilelang_from_database_does_not_log():
417 + with _patch_jit_modules(_make_fake_knobs()):
418 + from tilelang.jit.kernel import JITKernel
419 +
420 + func = SimpleNamespace(attrs={"global_symbol": "cached_tl_kernel"})
421 + jit_monitor.activate()
422 + with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
423 + JITKernel(func=func, from_database=True)
424 +
425 + warning_once.assert_not_called()
426 +
427 +
428 +def test_tilelang_error_mode_raises():
429 + with _patch_jit_modules(_make_fake_knobs()):
430 + from tilelang.jit.kernel import JITKernel
431 +
432 + func = SimpleNamespace(attrs={"global_symbol": "error_tl_kernel"})
433 + jit_monitor.activate(mode="error")
434 + with pytest.raises(RuntimeError, match="TileLang JIT compilation"):
435 + JITKernel(func=func)
tests/jit_monitor/test_hooks_gpu.py new
+85
@@ -0,0 +1,85 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""End-to-end JIT monitor tests: real Triton kernel, real GPU, real hook."""
4 +
5 +from unittest import mock
6 +
7 +import pytest
8 +
9 +from vllm.utils import jit_monitor
10 +
11 +try:
12 + import torch
13 +
14 + _HAS_CUDA = torch.cuda.is_available()
15 +except ImportError:
16 + _HAS_CUDA = False
17 +
18 +try:
19 + import triton
20 + import triton.language as tl
21 +
22 + _HAS_TRITON = True
23 +except ImportError:
24 + _HAS_TRITON = False
25 +
26 +pytestmark = pytest.mark.skipif(
27 + not (_HAS_CUDA and _HAS_TRITON),
28 + reason="Requires CUDA GPU and Triton",
29 +)
30 +
31 +
32 +if _HAS_TRITON:
33 +
34 + @triton.jit
35 + def _add_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
36 + pid = tl.program_id(0)
37 + offs = pid * BLOCK + tl.arange(0, BLOCK)
38 + mask = offs < n
39 + x = tl.load(x_ptr + offs, mask=mask)
40 + y = tl.load(y_ptr + offs, mask=mask)
41 + tl.store(out_ptr + offs, x + y, mask=mask)
42 +
43 +
44 +def _run_add_kernel(n: int, block: int = 256, offset: int = 0) -> None:
45 + """Launch ``_add_kernel`` with vectors of length *n*."""
46 + x = torch.randn(n + offset, device="cuda")[offset:] # affect alignment
47 + y = torch.randn(n, device="cuda")
48 + out = torch.empty(n, device="cuda")
49 + grid = ((n + block - 1) // block,)
50 + _add_kernel[grid](x, y, out, n, BLOCK=block)
51 + torch.accelerator.synchronize()
52 +
53 +
54 +def test_no_warning_on_cached_shape():
55 + _run_add_kernel(1024)
56 +
57 + jit_monitor.activate()
58 + with mock.patch.object(jit_monitor.logger, "warning_once") as w:
59 + _run_add_kernel(1024)
60 + w.assert_not_called()
61 +
62 +
63 +def test_warning_on_new_constexpr():
64 + _run_add_kernel(1024, block=256)
65 +
66 + jit_monitor.activate()
67 + with mock.patch.object(jit_monitor.logger, "warning_once") as w:
68 + # Different BLOCK (a tl.constexpr) forces recompilation.
69 + _run_add_kernel(1024, block=512)
70 + w.assert_called()
71 + msg = w.call_args[0][0] % w.call_args[0][1:]
72 + assert "_add_kernel" in msg
73 +
74 +
75 +def test_verbose_warning_on_each_new_pointer_alignment():
76 + _run_add_kernel(1024)
77 +
78 + jit_monitor.activate(verbose=True)
79 + with (
80 + mock.patch.object(jit_monitor.logger, "warning") as w,
81 + mock.patch.object(jit_monitor.logger, "warning_once") as w_once,
82 + ):
83 + _run_add_kernel(1024, offset=1)
84 + assert w.called
85 + w_once.assert_not_called()
tests/jit_monitor/test_no_runtime_jit.py new
+141
@@ -0,0 +1,141 @@
1 +# SPDX-License-Identifier: Apache-2.0
2 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 +"""Catch runtime (post-warmup) JIT compilations for JIT-heavy backends running
4 +e2e tests on popular models, for which we only load a few blocks for performance.
5 +
6 +NOTE(NickLucche) With cuda graphs on, kernels fully covered by graphs captured during
7 +warmup do not re-trigger the Python JIT hooks. The targeted paths (prefill
8 +MoE/MLA/SSM and the sampler) run mixed, so they are unaffected.
9 +"""
10 +
11 +from dataclasses import dataclass
12 +
13 +import pytest
14 +
15 +from vllm import LLM, SamplingParams
16 +from vllm.inputs import TokensPrompt
17 +
18 +from ..models.utils import dummy_hf_overrides
19 +from ..utils import create_new_process_for_each_test
20 +
21 +# Warmup coverage is still incomplete for these backends, so the monitor fires
22 +# during inference. Tracked in https://github.com/vllm-project/vllm/issues/49349;
23 +# drop this once the warmup contract migrations land.
24 +pytestmark = pytest.mark.skip(reason="Kernel warmup coverage is still incomplete")
25 +
26 +
27 +@dataclass(frozen=True)
28 +class JitModel:
29 + model: str
30 + draft: str | None = None
31 + trust_remote_code: bool = False
32 +
33 +
34 +JIT_MONITOR_MODELS = [
35 + JitModel("Qwen/Qwen3-0.6B"),
36 + JitModel("deepseek-ai/DeepSeek-V2-Lite-Chat", trust_remote_code=True),
37 + JitModel("deepseek-ai/DeepSeek-V3", trust_remote_code=True),
38 + JitModel("ibm-granite/granite-4.0-tiny-preview"),
39 + JitModel(
40 + "luccafong/deepseek_mtp_main_random",
41 + draft="luccafong/deepseek_mtp_draft_random",
42 + trust_remote_code=True,
43 + ),
44 + JitModel(
45 + "eagle618/deepseek-v3-random",
46 + draft="eagle618/eagle-deepseek-v3-random",
47 + trust_remote_code=True,
48 + ),
49 +]
50 +
51 +
52 +def _run_shape_battery(llm: LLM) -> None:
53 + """Exercise diverse compile keys so missing warmup keys surface.
54 +
55 + Token-id prompts keep shapes exact and avoid depending on a tokenizer.
56 + Outputs are meaningless under dummy weights; we assert only that no JIT
57 + fired.
58 + """
59 + short = TokensPrompt(prompt_token_ids=[1, 2, 3, 4])
60 + medium = TokensPrompt(prompt_token_ids=list(range(1, 33)))
61 + long = TokensPrompt(prompt_token_ids=list(range(1, 129)))
62 +
63 + # Greedy single-sequence multi-step decode: prefill + autoregressive decode
64 + # + greedy sampler.
65 + llm.generate(medium, SamplingParams(temperature=0.0, max_tokens=16))
66 +
67 + # Batched prefill with mixed lengths: varlen prefill + padded decode.
68 + llm.generate([short, medium, long], SamplingParams(temperature=0.0, max_tokens=8))
69 +
70 + # Triton sampler kernels: top_k / top_p / min_p each specialize.
71 + for sampling_params in (
72 + SamplingParams(temperature=0.8, top_k=20, max_tokens=8, seed=0),
73 + SamplingParams(temperature=0.8, top_p=0.9, max_tokens=8, seed=0),
74 + SamplingParams(temperature=0.8, min_p=0.1, max_tokens=8, seed=0),
75 + SamplingParams(
76 + temperature=0.8, top_k=20, top_p=0.9, min_p=0.1, max_tokens=8, seed=0
77 + ),
78 + ):
79 + llm.generate(medium, sampling_params)
80 +
81 + # Heterogeneous SamplingParams in one step, where missing sampler warmup
82 + # keys most often hide.
83 + llm.generate(
84 + [medium] * 4,
85 + [
86 + SamplingParams(temperature=0.0, max_tokens=8),
87 + SamplingParams(temperature=0.8, top_k=20, max_tokens=8, seed=0),
88 + SamplingParams(temperature=0.8, top_p=0.9, max_tokens=8, seed=0),
89 + SamplingParams(temperature=0.8, min_p=0.1, max_tokens=8, seed=0),
90 + ],
91 + )
92 +
93 +
94 +@create_new_process_for_each_test("spawn")
95 +def can_run_without_jit(spec: JitModel):
96 + """Boot ``spec`` with the monitor armed and run the shape battery.
97 +
98 + A subprocess per model is required: the monitor's hooks are process-global
99 + and, once armed in ``error`` mode, stay armed. It must be spawned rather
100 + than forked, since forking a pytest process that already initialized CUDA
101 + poisons the child.
102 + """
103 + llm = LLM(
104 + spec.model,
105 + trust_remote_code=spec.trust_remote_code,
106 + max_model_len=2048,
107 + max_num_seqs=8,
108 + gpu_memory_utilization=0.80,
109 + load_format="dummy",
110 + hf_overrides=dummy_hf_overrides,
111 + # cuda graphs cover captured decode shapes, run eager.
112 + enforce_eager=False,
113 + jit_monitor_mode="error",
114 + speculative_config={
115 + "model": spec.draft,
116 + "num_speculative_tokens": 2,
117 + }
118 + if spec.draft
119 + else None,
120 + )
121 +
122 + try:
123 + _run_shape_battery(llm)
124 + except Exception as e:
125 + # The monitor's message contains "during inference"; distinguish a real
126 + # JIT miss from an unrelated crash.
127 + if "during inference" in str(e):
128 + pytest.fail(
129 + f"{spec.model}: post-warmup JIT compilation detected - a warmup "
130 + f"key is missing for a shape in the battery.\n{e}"
131 + )
132 + raise
133 +
134 +
135 +@pytest.mark.parametrize("spec", JIT_MONITOR_MODELS, ids=lambda s: s.model)
136 +def test_no_runtime_jit(spec: JitModel, monkeypatch: pytest.MonkeyPatch):
137 + """Assert JIT-heavy backends do not JIT-compile during inference."""
138 + # Set here rather than in the child so the spawned process inherits it:
139 + # the engine core must not be forked once the test process has CUDA up.
140 + monkeypatch.setenv("VLLM_WORKER_MULTIPROC_METHOD", "spawn")
141 + can_run_without_jit(spec)
tests/test_jit_monitor.py deleted
-534
@@ -1,534 +0,0 @@
1 -# SPDX-License-Identifier: Apache-2.0
2 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 -import inspect
4 -import os
5 -import sys
6 -from contextlib import contextmanager
7 -from types import ModuleType, SimpleNamespace
8 -from typing import Any, cast
9 -from unittest import mock
10 -
11 -import pytest
12 -
13 -from vllm.utils import jit_monitor
14 -
15 -
16 -@pytest.fixture(autouse=True)
17 -def _reset_monitor():
18 - """Reset global monitor state between tests."""
19 - jit_monitor._active = False
20 - jit_monitor._mode = "warn"
21 - jit_monitor._verbose = False
22 - jit_monitor._cutedsl_hook_installed = False
23 - jit_monitor._tilelang_hook_installed = False
24 - jit_monitor._tilelang_jitimpl_compile_depth = 0
25 - yield
26 - jit_monitor._active = False
27 - jit_monitor._mode = "warn"
28 - jit_monitor._verbose = False
29 - jit_monitor._cutedsl_hook_installed = False
30 - jit_monitor._tilelang_hook_installed = False
31 - jit_monitor._tilelang_jitimpl_compile_depth = 0
32 -
33 -
34 -# ------------------------------------------------------------------
35 -# Helpers — lightweight stand-ins for the modules ``activate()`` patches
36 -# ------------------------------------------------------------------
37 -
38 -
39 -def _make_fake_knobs(*, autotuning_print=False, jit_hook=None):
40 - """Build a minimal fake ``triton.knobs`` namespace."""
41 - autotuning = SimpleNamespace(print=autotuning_print)
42 - runtime = SimpleNamespace(jit_post_compile_hook=jit_hook)
43 - return SimpleNamespace(autotuning=autotuning, runtime=runtime)
44 -
45 -
46 -def _fake_cute_import_modules(compile_fn):
47 - """Fake Python's parent package + submodule for ``import cutlass.cute``."""
48 - fake_cute = cast(Any, ModuleType("cutlass.cute"))
49 - fake_cute.compile = compile_fn
50 - fake_parent_package = cast(Any, ModuleType("cutlass"))
51 - fake_parent_package.__path__ = []
52 - fake_parent_package.cute = fake_cute
53 - return {
54 - "cutlass": fake_parent_package,
55 - "cutlass.cute": fake_cute,
56 - }
57 -
58 -
59 -def _fake_cute_compile(*args, **kwargs):
60 - return "compiled"
61 -
62 -
63 -def _fake_tilelang_import_modules():
64 - """Fake Python's TileLang modules touched by ``jit_monitor.activate``."""
65 -
66 - class FakeJITKernel:
67 - def __init__(self, *args, **kwargs):
68 - pass
69 -
70 - class FakeJITImpl:
71 - def __init__(self, func, signature):
72 - self.func = func
73 - self.signature = signature
74 - self.mode = "lazy"
75 - self._kernel_cache = {}
76 -
77 - def __call__(self, *args, **kwargs):
78 - key, _ = self.func.parse_args(*args, **kwargs)
79 - kernel = self._kernel_cache.get(key)
80 - if kernel is None:
81 - kernel = "compiled"
82 - self._kernel_cache[key] = kernel
83 - return kernel
84 -
85 - fake_kernel = cast(Any, ModuleType("tilelang.jit.kernel"))
86 - fake_kernel.JITKernel = FakeJITKernel
87 -
88 - fake_jit = cast(Any, ModuleType("tilelang.jit"))
89 - fake_jit.JITImpl = FakeJITImpl
90 - fake_jit.kernel = fake_kernel
91 -
92 - fake_tilelang = cast(Any, ModuleType("tilelang"))
93 - fake_tilelang.jit = fake_jit
94 -
95 - return {
96 - "tilelang": fake_tilelang,
97 - "tilelang.jit": fake_jit,
98 - "tilelang.jit.kernel": fake_kernel,
99 - }
100 -
101 -
102 -@contextmanager
103 -def _patch_jit_modules(fake_knobs, *, cute_compile=_fake_cute_compile):
104 - """Patch the Triton and CuTeDSL imports touched by ``jit_monitor.activate``."""
105 - fake_triton = cast(Any, ModuleType("triton"))
106 - fake_triton.knobs = fake_knobs
107 - with (
108 - mock.patch.dict(
109 - sys.modules,
110 - {
111 - "triton": fake_triton,
112 - **_fake_cute_import_modules(cute_compile),
113 - **_fake_tilelang_import_modules(),
114 - },
115 - ),
116 - mock.patch.object(jit_monitor, "HAS_TRITON", True),
117 - ):
118 - yield
119 -
120 -
121 -# ------------------------------------------------------------------
122 -# Unit tests (no GPU required, triton is mocked)
123 -# ------------------------------------------------------------------
124 -
125 -
126 -class TestActivateBasic:
127 - def test_sets_active(self):
128 - assert not jit_monitor.is_active()
129 - with _patch_jit_modules(_make_fake_knobs()):
130 - jit_monitor.activate()
131 - assert jit_monitor.is_active()
132 -
133 - def test_idempotent(self):
134 - fake = _make_fake_knobs()
135 - with _patch_jit_modules(fake):
136 - jit_monitor.activate()
137 - first_hook = fake.runtime.jit_post_compile_hook
138 - jit_monitor.activate()
139 - assert fake.runtime.jit_post_compile_hook is first_hook
140 -
141 - def test_logs_info_on_activation(self):
142 - with (
143 - mock.patch.object(jit_monitor.logger, "info") as m,
144 - _patch_jit_modules(_make_fake_knobs()),
145 - ):
146 - jit_monitor.activate()
147 - m.assert_called_once()
148 - assert "Kernel JIT monitor activated" in m.call_args[0][0]
149 -
150 - def test_rejects_unknown_mode(self):
151 - with pytest.raises(ValueError, match="Unsupported JIT monitor mode"):
152 - jit_monitor.activate(mode="panic") # type: ignore[arg-type]
153 -
154 -
155 -class TestAutotuningPrint:
156 - def test_enables_autotuning_print(self):
157 - fake = _make_fake_knobs(autotuning_print=False)
158 - with _patch_jit_modules(fake):
159 - jit_monitor.activate()
160 - assert fake.autotuning.print is True
161 -
162 - def test_respects_user_opt_out(self):
163 - fake = _make_fake_knobs(autotuning_print=False)
164 - with (
165 - mock.patch.dict(os.environ, {"TRITON_PRINT_AUTOTUNING": "0"}),
166 - _patch_jit_modules(fake),
167 - ):
168 - jit_monitor.activate()
169 - assert fake.autotuning.print is False
170 -
171 - def test_noop_when_user_already_enabled(self):
172 - fake = _make_fake_knobs(autotuning_print=True)
173 - with (
174 - mock.patch.dict(os.environ, {"TRITON_PRINT_AUTOTUNING": "1"}),
175 - _patch_jit_modules(fake),
176 - ):
177 - jit_monitor.activate()
178 - assert fake.autotuning.print is True
179 -
180 -
181 -class TestTritonJitHook:
182 - def test_hook_registered(self):
183 - fake = _make_fake_knobs()
184 - assert fake.runtime.jit_post_compile_hook is None
185 - with _patch_jit_modules(fake):
186 - jit_monitor.activate()
187 - assert fake.runtime.jit_post_compile_hook is not None
188 -
189 - def test_hook_logs_warning(self):
190 - fake = _make_fake_knobs()
191 - with _patch_jit_modules(fake):
192 - jit_monitor.activate()
193 -
194 - hook = fake.runtime.jit_post_compile_hook
195 - mock_fn = SimpleNamespace(name="test_kernel")
196 -
197 - with (
198 - mock.patch.object(jit_monitor.logger, "warning_once") as m,
199 - mock.patch.object(jit_monitor.logger, "warning") as warning,
200 - ):
201 - hook(
202 - key="some_key",
203 - repr="some_repr",
204 - fn=mock_fn,
205 - compile=lambda: None,
206 - is_manual_warmup=False,
207 - already_compiled=False,
208 - )
209 -
210 - m.assert_called_once()
211 - warning.assert_not_called()
212 - msg = m.call_args[0][0] % m.call_args[0][1:]
213 - assert "Triton kernel JIT compilation during inference" in msg
214 - assert "test_kernel" in msg
215 -
216 - def test_hook_chains_existing_hook(self):
217 - existing = mock.MagicMock(return_value="existing_result")
218 - fake = _make_fake_knobs(jit_hook=existing)
219 - with _patch_jit_modules(fake):
220 - jit_monitor.activate()
221 -
222 - hook = fake.runtime.jit_post_compile_hook
223 - mock_fn = SimpleNamespace(name="chained_kernel")
224 - kwargs = dict(
225 - key="k",
226 - repr="r",
227 - fn=mock_fn,
228 - compile=lambda: None,
229 - is_manual_warmup=False,
230 - already_compiled=False,
231 - )
232 - result = hook(**kwargs)
233 -
234 - existing.assert_called_once()
235 - assert result == "existing_result"
236 -
237 - def test_hook_works_without_existing_hook(self):
238 - fake = _make_fake_knobs(jit_hook=None)
239 - with _patch_jit_modules(fake):
240 - jit_monitor.activate()
241 -
242 - hook = fake.runtime.jit_post_compile_hook
243 - mock_fn = SimpleNamespace(name="solo_kernel")
244 - result = hook(
245 - key="k",
246 - repr="r",
247 - fn=mock_fn,
248 - compile=lambda: None,
249 - is_manual_warmup=False,
250 - already_compiled=False,
251 - )
252 - assert result is None
253 -
254 - def test_error_mode_raises(self):
255 - fake = _make_fake_knobs()
256 - with _patch_jit_modules(fake):
257 - jit_monitor.activate(mode="error")
258 -
259 - hook = fake.runtime.jit_post_compile_hook
260 - mock_fn = SimpleNamespace(name="error_kernel")
261 - with pytest.raises(RuntimeError, match="Triton kernel JIT compilation"):
262 - hook(
263 - key="k",
264 - repr="r",
265 - fn=mock_fn,
266 - compile=lambda: None,
267 - is_manual_warmup=False,
268 - already_compiled=False,
269 - )
270 -
271 -
272 -class TestNoTritonFallback:
273 - def test_activate_without_triton(self):
274 - with mock.patch.object(jit_monitor, "HAS_TRITON", False):
275 - jit_monitor.activate()
276 - assert jit_monitor.is_active()
277 -
278 -
279 -class TestCuTeDSLHook:
280 - def test_compile_logs_warning(self):
281 - def compile_fn(*args, **kwargs):
282 - return "compiled"
283 -
284 - with _patch_jit_modules(_make_fake_knobs(), cute_compile=compile_fn):
285 - import cutlass.cute as cute
286 -
287 - jit_monitor.activate()
288 - with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
289 - result = cute.compile(lambda: None, "arg", option=True)
290 -
291 - assert result == "compiled"
292 - warning_once.assert_called_once()
293 - msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
294 - assert "CuTeDSL JIT compilation during inference" in msg
295 -
296 - def test_compile_logs_verbose_warning(self):
297 - def compile_fn(*args, **kwargs):
298 - return "compiled"
299 -
300 - with _patch_jit_modules(_make_fake_knobs(), cute_compile=compile_fn):
301 - import cutlass.cute as cute
302 -
303 - jit_monitor.activate(verbose=True)
304 - with mock.patch.object(jit_monitor.logger, "warning") as warning:
305 - result = cute.compile(lambda: None, "arg", option=True)
306 -
307 - assert result == "compiled"
308 - warning.assert_called_once()
309 - msg = warning.call_args[0][0] % warning.call_args[0][1:]
310 - assert "CuTeDSL JIT compilation during inference" in msg
311 -
312 - def test_error_mode_raises(self):
313 - def compile_fn(*args, **kwargs):
314 - return "compiled"
315 -
316 - with _patch_jit_modules(_make_fake_knobs(), cute_compile=compile_fn):
317 - import cutlass.cute as cute
318 -
319 - jit_monitor.activate(mode="error")
320 - with pytest.raises(RuntimeError, match="CuTeDSL JIT compilation"):
321 - cute.compile(lambda: None, "arg", option=True)
322 -
323 - def test_subscripted_compile_is_monitored(self):
324 - """``cute.compile[options](...)`` (flashinfer >= 0.6.14) must work."""
325 -
326 - class FakeCompileCallable:
327 - def __getitem__(self, options):
328 - return self
329 -
330 - def __call__(self, *args, **kwargs):
331 - return "compiled"
332 -
333 - with _patch_jit_modules(_make_fake_knobs(), cute_compile=FakeCompileCallable()):
334 - import cutlass.cute as cute
335 -
336 - jit_monitor.activate()
337 - with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
338 - result = cute.compile[("opt_level", 3)](lambda: None, "arg")
339 -
340 - assert result == "compiled"
341 - warning_once.assert_called_once()
342 -
343 -
344 -class TestTileLangHook:
345 - def test_jit_kernel_logs_warning(self):
346 - with _patch_jit_modules(_make_fake_knobs()):
347 - from tilelang.jit.kernel import JITKernel
348 -
349 - func = SimpleNamespace(attrs={"global_symbol": "tl_kernel"})
350 - jit_monitor.activate()
351 - with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
352 - JITKernel(func=func, out_idx=None, execution_backend="tvm_ffi")
353 -
354 - warning_once.assert_called_once()
355 - msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
356 - assert "TileLang JIT compilation during inference" in msg
357 - assert "tl_kernel" in msg
358 -
359 - def test_jit_impl_logs_warning(self):
360 - with _patch_jit_modules(_make_fake_knobs()):
361 - from tilelang.jit import JITImpl
362 -
363 - def tilelang_fn(
364 - gemm_out_mul,
365 - hidden_size: int,
366 - n_splits: int = 1,
367 - hc_mult: int = 4,
368 - ):
369 - return None
370 -
371 - class FakeFunc:
372 - orig_func = tilelang_fn
373 -
374 - def parse_args(self, *args, **kwargs):
375 - return (
376 - (
377 - "tilelang_key",
378 - kwargs["hidden_size"],
379 - kwargs.get("n_splits", 1),
380 - ),
381 - {},
382 - )
383 -
384 - def set_mode(self, mode):
385 - self.mode = mode
386 -
387 - tensor = SimpleNamespace(
388 - shape=(2, 16, 24),
389 - dtype="float32",
390 - device="cuda:0",
391 - )
392 - impl = JITImpl(FakeFunc(), inspect.signature(tilelang_fn))
393 -
394 - jit_monitor.activate()
395 - with (
396 - mock.patch.object(jit_monitor.logger, "warning_once") as warning_once,
397 - mock.patch.object(jit_monitor.logger, "warning") as warning,
398 - ):
399 - impl(tensor, hidden_size=7168, n_splits=2)
400 -
401 - warning_once.assert_called_once()
402 - warning.assert_not_called()
403 - msg = warning_once.call_args[0][0] % warning_once.call_args[0][1:]
404 - assert "TileLang JIT compilation during inference" in msg
405 - assert "tilelang_fn" in msg
406 -
407 - def test_jit_impl_does_not_log_on_cache_hit(self):
408 - with _patch_jit_modules(_make_fake_knobs()):
409 - from tilelang.jit import JITImpl
410 -
411 - def tilelang_fn(gemm_out_mul, n_splits: int = 1):
412 - return None
413 -
414 - class FakeFunc:
415 - orig_func = tilelang_fn
416 -
417 - def parse_args(self, *args, **kwargs):
418 - return (("tilelang_key", kwargs.get("n_splits", 1)), {})
419 -
420 - def set_mode(self, mode):
421 - self.mode = mode
422 -
423 - tensor = SimpleNamespace(shape=(2, 16, 24), dtype="float32")
424 - impl = JITImpl(FakeFunc(), inspect.signature(tilelang_fn))
425 -
426 - jit_monitor.activate()
427 - with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
428 - impl(tensor, n_splits=2)
429 - impl(tensor, n_splits=2)
430 -
431 - warning_once.assert_called_once()
432 -
433 - def test_from_database_does_not_log(self):
434 - with _patch_jit_modules(_make_fake_knobs()):
435 - from tilelang.jit.kernel import JITKernel
436 -
437 - func = SimpleNamespace(attrs={"global_symbol": "cached_tl_kernel"})
438 - jit_monitor.activate()
439 - with mock.patch.object(jit_monitor.logger, "warning_once") as warning_once:
440 - JITKernel(func=func, from_database=True)
441 -
442 - warning_once.assert_not_called()
443 -
444 - def test_error_mode_raises(self):
445 - with _patch_jit_modules(_make_fake_knobs()):
446 - from tilelang.jit.kernel import JITKernel
447 -
448 - func = SimpleNamespace(attrs={"global_symbol": "error_tl_kernel"})
449 - jit_monitor.activate(mode="error")
450 - with pytest.raises(RuntimeError, match="TileLang JIT compilation"):
451 - JITKernel(func=func)
452 -
453 -
454 -# ------------------------------------------------------------------
455 -# Integration tests (real Triton + GPU)
456 -# ------------------------------------------------------------------
457 -
458 -try:
459 - import torch
460 -
461 - _HAS_CUDA = torch.cuda.is_available()
462 -except ImportError:
463 - _HAS_CUDA = False
464 -
465 -try:
466 - import triton
467 - import triton.language as tl
468 -
469 - _HAS_TRITON = True
470 -except ImportError:
471 - _HAS_TRITON = False
472 -
473 -_skip_no_gpu = pytest.mark.skipif(
474 - not (_HAS_CUDA and _HAS_TRITON),
475 - reason="Requires CUDA GPU and Triton",
476 -)
477 -
478 -
479 -if _HAS_TRITON:
480 -
481 - @triton.jit
482 - def _add_kernel(x_ptr, y_ptr, out_ptr, n, BLOCK: tl.constexpr):
483 - pid = tl.program_id(0)
484 - offs = pid * BLOCK + tl.arange(0, BLOCK)
485 - mask = offs < n
486 - x = tl.load(x_ptr + offs, mask=mask)
487 - y = tl.load(y_ptr + offs, mask=mask)
488 - tl.store(out_ptr + offs, x + y, mask=mask)
489 -
490 -
491 -def _run_add_kernel(n: int, block: int = 256, offset: int = 0) -> None:
492 - """Launch ``_add_kernel`` with vectors of length *n*."""
493 - x = torch.randn(n + offset, device="cuda")[offset:] # affect alignment
494 - y = torch.randn(n, device="cuda")
495 - out = torch.empty(n, device="cuda")
496 - grid = ((n + block - 1) // block,)
497 - _add_kernel[grid](x, y, out, n, BLOCK=block)
498 - torch.accelerator.synchronize()
499 -
500 -
501 -@_skip_no_gpu
502 -class TestTritonJitHookIntegration:
503 - """End-to-end: real Triton kernel, real GPU, real hook."""
504 -
505 - def test_no_warning_on_cached_shape(self):
506 - _run_add_kernel(1024)
507 -
508 - jit_monitor.activate()
509 - with mock.patch.object(jit_monitor.logger, "warning_once") as w:
510 - _run_add_kernel(1024)
511 - w.assert_not_called()
512 -
513 - def test_warning_on_new_constexpr(self):
514 - _run_add_kernel(1024, block=256)
515 -
516 - jit_monitor.activate()
517 - with mock.patch.object(jit_monitor.logger, "warning_once") as w:
518 - # Different BLOCK (a tl.constexpr) forces recompilation.
519 - _run_add_kernel(1024, block=512)
520 - w.assert_called()
521 - msg = w.call_args[0][0] % w.call_args[0][1:]
522 - assert "_add_kernel" in msg
523 -
524 - def test_verbose_warning_on_each_new_pointer_alignment(self):
525 - _run_add_kernel(1024)
526 -
527 - jit_monitor.activate(verbose=True)
528 - with (
529 - mock.patch.object(jit_monitor.logger, "warning") as w,
530 - mock.patch.object(jit_monitor.logger, "warning_once") as w_once,
531 - ):
532 - _run_add_kernel(1024, offset=1)
533 - assert w.called
534 - w_once.assert_not_called()