numba-cuda 0.19.1__py3-none-any.whl → 0.20.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of numba-cuda might be problematic. Click here for more details.

Files changed (172) hide show
  1. numba_cuda/VERSION +1 -1
  2. numba_cuda/numba/cuda/__init__.py +1 -1
  3. numba_cuda/numba/cuda/_internal/cuda_bf16.py +12706 -1470
  4. numba_cuda/numba/cuda/_internal/cuda_fp16.py +2653 -8769
  5. numba_cuda/numba/cuda/api.py +6 -1
  6. numba_cuda/numba/cuda/bf16.py +285 -2
  7. numba_cuda/numba/cuda/cgutils.py +2 -2
  8. numba_cuda/numba/cuda/cloudpickle/__init__.py +21 -0
  9. numba_cuda/numba/cuda/cloudpickle/cloudpickle.py +1598 -0
  10. numba_cuda/numba/cuda/cloudpickle/cloudpickle_fast.py +17 -0
  11. numba_cuda/numba/cuda/codegen.py +1 -1
  12. numba_cuda/numba/cuda/compiler.py +373 -30
  13. numba_cuda/numba/cuda/core/analysis.py +319 -0
  14. numba_cuda/numba/cuda/core/annotations/__init__.py +0 -0
  15. numba_cuda/numba/cuda/core/annotations/type_annotations.py +304 -0
  16. numba_cuda/numba/cuda/core/base.py +1289 -0
  17. numba_cuda/numba/cuda/core/bytecode.py +727 -0
  18. numba_cuda/numba/cuda/core/caching.py +2 -2
  19. numba_cuda/numba/cuda/core/compiler.py +6 -14
  20. numba_cuda/numba/cuda/core/compiler_machinery.py +497 -0
  21. numba_cuda/numba/cuda/core/config.py +747 -0
  22. numba_cuda/numba/cuda/core/consts.py +124 -0
  23. numba_cuda/numba/cuda/core/cpu.py +370 -0
  24. numba_cuda/numba/cuda/core/environment.py +68 -0
  25. numba_cuda/numba/cuda/core/event.py +511 -0
  26. numba_cuda/numba/cuda/core/funcdesc.py +330 -0
  27. numba_cuda/numba/cuda/core/inline_closurecall.py +1889 -0
  28. numba_cuda/numba/cuda/core/interpreter.py +48 -26
  29. numba_cuda/numba/cuda/core/ir_utils.py +15 -26
  30. numba_cuda/numba/cuda/core/options.py +262 -0
  31. numba_cuda/numba/cuda/core/postproc.py +249 -0
  32. numba_cuda/numba/cuda/core/pythonapi.py +1868 -0
  33. numba_cuda/numba/cuda/core/rewrites/__init__.py +26 -0
  34. numba_cuda/numba/cuda/core/rewrites/ir_print.py +90 -0
  35. numba_cuda/numba/cuda/core/rewrites/registry.py +104 -0
  36. numba_cuda/numba/cuda/core/rewrites/static_binop.py +40 -0
  37. numba_cuda/numba/cuda/core/rewrites/static_getitem.py +187 -0
  38. numba_cuda/numba/cuda/core/rewrites/static_raise.py +98 -0
  39. numba_cuda/numba/cuda/core/ssa.py +496 -0
  40. numba_cuda/numba/cuda/core/targetconfig.py +329 -0
  41. numba_cuda/numba/cuda/core/tracing.py +231 -0
  42. numba_cuda/numba/cuda/core/transforms.py +952 -0
  43. numba_cuda/numba/cuda/core/typed_passes.py +738 -7
  44. numba_cuda/numba/cuda/core/typeinfer.py +1948 -0
  45. numba_cuda/numba/cuda/core/unsafe/__init__.py +0 -0
  46. numba_cuda/numba/cuda/core/unsafe/bytes.py +67 -0
  47. numba_cuda/numba/cuda/core/unsafe/eh.py +66 -0
  48. numba_cuda/numba/cuda/core/unsafe/refcount.py +98 -0
  49. numba_cuda/numba/cuda/core/untyped_passes.py +1983 -0
  50. numba_cuda/numba/cuda/cpython/cmathimpl.py +560 -0
  51. numba_cuda/numba/cuda/cpython/mathimpl.py +499 -0
  52. numba_cuda/numba/cuda/cpython/numbers.py +1474 -0
  53. numba_cuda/numba/cuda/cuda_paths.py +422 -246
  54. numba_cuda/numba/cuda/cudadecl.py +1 -1
  55. numba_cuda/numba/cuda/cudadrv/__init__.py +1 -1
  56. numba_cuda/numba/cuda/cudadrv/devicearray.py +2 -1
  57. numba_cuda/numba/cuda/cudadrv/driver.py +11 -140
  58. numba_cuda/numba/cuda/cudadrv/dummyarray.py +111 -24
  59. numba_cuda/numba/cuda/cudadrv/libs.py +5 -5
  60. numba_cuda/numba/cuda/cudadrv/mappings.py +1 -1
  61. numba_cuda/numba/cuda/cudadrv/nvrtc.py +19 -8
  62. numba_cuda/numba/cuda/cudadrv/nvvm.py +1 -4
  63. numba_cuda/numba/cuda/cudadrv/runtime.py +1 -1
  64. numba_cuda/numba/cuda/cudaimpl.py +5 -1
  65. numba_cuda/numba/cuda/debuginfo.py +85 -2
  66. numba_cuda/numba/cuda/decorators.py +3 -3
  67. numba_cuda/numba/cuda/descriptor.py +3 -4
  68. numba_cuda/numba/cuda/deviceufunc.py +66 -2
  69. numba_cuda/numba/cuda/dispatcher.py +18 -39
  70. numba_cuda/numba/cuda/flags.py +141 -1
  71. numba_cuda/numba/cuda/fp16.py +0 -2
  72. numba_cuda/numba/cuda/include/13/cuda_bf16.h +5118 -0
  73. numba_cuda/numba/cuda/include/13/cuda_bf16.hpp +3865 -0
  74. numba_cuda/numba/cuda/include/13/cuda_fp16.h +5363 -0
  75. numba_cuda/numba/cuda/include/13/cuda_fp16.hpp +3483 -0
  76. numba_cuda/numba/cuda/lowering.py +7 -144
  77. numba_cuda/numba/cuda/mathimpl.py +2 -1
  78. numba_cuda/numba/cuda/memory_management/nrt.py +43 -17
  79. numba_cuda/numba/cuda/misc/findlib.py +75 -0
  80. numba_cuda/numba/cuda/models.py +9 -1
  81. numba_cuda/numba/cuda/np/npdatetime_helpers.py +217 -0
  82. numba_cuda/numba/cuda/np/npyfuncs.py +1807 -0
  83. numba_cuda/numba/cuda/np/numpy_support.py +553 -0
  84. numba_cuda/numba/cuda/np/ufunc/ufuncbuilder.py +59 -0
  85. numba_cuda/numba/cuda/nvvmutils.py +1 -1
  86. numba_cuda/numba/cuda/printimpl.py +12 -1
  87. numba_cuda/numba/cuda/random.py +1 -1
  88. numba_cuda/numba/cuda/serialize.py +1 -1
  89. numba_cuda/numba/cuda/simulator/__init__.py +1 -1
  90. numba_cuda/numba/cuda/simulator/api.py +1 -1
  91. numba_cuda/numba/cuda/simulator/compiler.py +4 -0
  92. numba_cuda/numba/cuda/simulator/cudadrv/devicearray.py +1 -1
  93. numba_cuda/numba/cuda/simulator/kernelapi.py +1 -1
  94. numba_cuda/numba/cuda/simulator/memory_management/nrt.py +14 -2
  95. numba_cuda/numba/cuda/target.py +35 -17
  96. numba_cuda/numba/cuda/testing.py +7 -19
  97. numba_cuda/numba/cuda/tests/__init__.py +1 -1
  98. numba_cuda/numba/cuda/tests/cloudpickle_main_class.py +9 -0
  99. numba_cuda/numba/cuda/tests/core/test_serialize.py +4 -4
  100. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_devicerecord.py +1 -1
  101. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_libraries.py +1 -1
  102. numba_cuda/numba/cuda/tests/cudadrv/test_deallocations.py +1 -1
  103. numba_cuda/numba/cuda/tests/cudadrv/test_detect.py +6 -3
  104. numba_cuda/numba/cuda/tests/cudadrv/test_emm_plugins.py +1 -1
  105. numba_cuda/numba/cuda/tests/cudadrv/test_linker.py +18 -2
  106. numba_cuda/numba/cuda/tests/cudadrv/test_module_callbacks.py +2 -1
  107. numba_cuda/numba/cuda/tests/cudadrv/test_nvjitlink.py +1 -1
  108. numba_cuda/numba/cuda/tests/cudadrv/test_ptds.py +1 -1
  109. numba_cuda/numba/cuda/tests/cudapy/extensions_usecases.py +1 -1
  110. numba_cuda/numba/cuda/tests/cudapy/test_array.py +2 -1
  111. numba_cuda/numba/cuda/tests/cudapy/test_atomics.py +1 -1
  112. numba_cuda/numba/cuda/tests/cudapy/test_bfloat16.py +539 -2
  113. numba_cuda/numba/cuda/tests/cudapy/test_bfloat16_bindings.py +81 -1
  114. numba_cuda/numba/cuda/tests/cudapy/test_caching.py +1 -3
  115. numba_cuda/numba/cuda/tests/cudapy/test_complex.py +1 -1
  116. numba_cuda/numba/cuda/tests/cudapy/test_constmem.py +1 -1
  117. numba_cuda/numba/cuda/tests/cudapy/test_cooperative_groups.py +2 -3
  118. numba_cuda/numba/cuda/tests/cudapy/test_copy_propagate.py +130 -0
  119. numba_cuda/numba/cuda/tests/cudapy/test_datetime.py +1 -1
  120. numba_cuda/numba/cuda/tests/cudapy/test_debug.py +1 -1
  121. numba_cuda/numba/cuda/tests/cudapy/test_debuginfo.py +293 -4
  122. numba_cuda/numba/cuda/tests/cudapy/test_debuginfo_types.py +1 -1
  123. numba_cuda/numba/cuda/tests/cudapy/test_dispatcher.py +1 -1
  124. numba_cuda/numba/cuda/tests/cudapy/test_errors.py +1 -1
  125. numba_cuda/numba/cuda/tests/cudapy/test_exception.py +1 -1
  126. numba_cuda/numba/cuda/tests/cudapy/test_extending.py +2 -1
  127. numba_cuda/numba/cuda/tests/cudapy/test_inline.py +18 -8
  128. numba_cuda/numba/cuda/tests/cudapy/test_intrinsics.py +23 -21
  129. numba_cuda/numba/cuda/tests/cudapy/test_ir_utils.py +10 -37
  130. numba_cuda/numba/cuda/tests/cudapy/test_laplace.py +1 -1
  131. numba_cuda/numba/cuda/tests/cudapy/test_math.py +1 -1
  132. numba_cuda/numba/cuda/tests/cudapy/test_matmul.py +1 -1
  133. numba_cuda/numba/cuda/tests/cudapy/test_operator.py +1 -1
  134. numba_cuda/numba/cuda/tests/cudapy/test_print.py +20 -0
  135. numba_cuda/numba/cuda/tests/cudapy/test_record_dtype.py +1 -1
  136. numba_cuda/numba/cuda/tests/cudapy/test_reduction.py +1 -1
  137. numba_cuda/numba/cuda/tests/cudapy/test_serialize.py +1 -1
  138. numba_cuda/numba/cuda/tests/cudapy/test_sm.py +1 -1
  139. numba_cuda/numba/cuda/tests/cudapy/test_ssa.py +453 -0
  140. numba_cuda/numba/cuda/tests/cudapy/test_sync.py +1 -1
  141. numba_cuda/numba/cuda/tests/cudapy/test_typeinfer.py +538 -0
  142. numba_cuda/numba/cuda/tests/cudapy/test_ufuncs.py +263 -2
  143. numba_cuda/numba/cuda/tests/cudapy/test_userexc.py +1 -1
  144. numba_cuda/numba/cuda/tests/cudapy/test_vector_type.py +1 -1
  145. numba_cuda/numba/cuda/tests/cudapy/test_vectorize_decor.py +112 -6
  146. numba_cuda/numba/cuda/tests/cudapy/test_warning.py +1 -1
  147. numba_cuda/numba/cuda/tests/cudapy/test_warp_ops.py +1 -1
  148. numba_cuda/numba/cuda/tests/doc_examples/test_cg.py +0 -2
  149. numba_cuda/numba/cuda/tests/doc_examples/test_ffi.py +3 -2
  150. numba_cuda/numba/cuda/tests/doc_examples/test_laplace.py +0 -2
  151. numba_cuda/numba/cuda/tests/doc_examples/test_sessionize.py +0 -2
  152. numba_cuda/numba/cuda/tests/nocuda/test_import.py +3 -1
  153. numba_cuda/numba/cuda/tests/nocuda/test_library_lookup.py +24 -12
  154. numba_cuda/numba/cuda/tests/nrt/test_nrt.py +2 -1
  155. numba_cuda/numba/cuda/tests/support.py +55 -15
  156. numba_cuda/numba/cuda/tests/test_tracing.py +200 -0
  157. numba_cuda/numba/cuda/types.py +56 -0
  158. numba_cuda/numba/cuda/typing/__init__.py +9 -1
  159. numba_cuda/numba/cuda/typing/cffi_utils.py +55 -0
  160. numba_cuda/numba/cuda/typing/context.py +751 -0
  161. numba_cuda/numba/cuda/typing/enumdecl.py +74 -0
  162. numba_cuda/numba/cuda/typing/npydecl.py +658 -0
  163. numba_cuda/numba/cuda/typing/templates.py +7 -6
  164. numba_cuda/numba/cuda/ufuncs.py +3 -3
  165. numba_cuda/numba/cuda/utils.py +6 -112
  166. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/METADATA +4 -3
  167. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/RECORD +171 -116
  168. numba_cuda/numba/cuda/tests/cudadrv/test_mvc.py +0 -60
  169. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/WHEEL +0 -0
  170. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/licenses/LICENSE +0 -0
  171. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/licenses/LICENSE.numba +0 -0
  172. {numba_cuda-0.19.1.dist-info → numba_cuda-0.20.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,511 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-2-Clause
3
+
4
+ """
5
+ The ``numba.cuda.core.event`` module provides a simple event system for applications
6
+ to register callbacks to listen to specific compiler events.
7
+
8
+ The following events are built in:
9
+
10
+ - ``"numba-cuda:compile"`` is broadcast when a dispatcher is compiling. Events of
11
+ this kind have ``data`` defined to be a ``dict`` with the following
12
+ key-values:
13
+
14
+ - ``"dispatcher"``: the dispatcher object that is compiling.
15
+ - ``"args"``: the argument types.
16
+ - ``"return_type"``: the return type.
17
+
18
+ - ``"numba-cuda:compiler_lock"`` is broadcast when the internal compiler-lock is
19
+ acquired. This is mostly used internally to measure time spent with the lock
20
+ acquired.
21
+
22
+ - ``"numba-cuda:run_pass"`` is broadcast when a compiler pass is running.
23
+
24
+ - ``"name"``: pass name.
25
+ - ``"qualname"``: qualified name of the function being compiled.
26
+ - ``"module"``: module name of the function being compiled.
27
+ - ``"flags"``: compilation flags.
28
+ - ``"args"``: argument types.
29
+ - ``"return_type"`` return type.
30
+
31
+ Applications can register callbacks that are listening for specific events using
32
+ ``register(kind: str, listener: Listener)``, where ``listener`` is an instance
33
+ of ``Listener`` that defines custom actions on occurrence of the specific event.
34
+ """
35
+
36
+ import os
37
+ import json
38
+ import atexit
39
+ import abc
40
+ import enum
41
+ import time
42
+ import threading
43
+ from timeit import default_timer as timer
44
+ from contextlib import contextmanager, ExitStack
45
+ from collections import defaultdict
46
+ from numba.core import config
47
+ from numba.cuda import utils
48
+
49
+
50
+ class EventStatus(enum.Enum):
51
+ """Status of an event."""
52
+
53
+ START = enum.auto()
54
+ END = enum.auto()
55
+
56
+
57
+ # Builtin event kinds.
58
+ _builtin_kinds = frozenset(
59
+ [
60
+ "numba-cuda:compiler_lock",
61
+ "numba-cuda:compile",
62
+ "numba-cuda:run_pass",
63
+ ]
64
+ )
65
+
66
+
67
+ def _guard_kind(kind):
68
+ """Guard to ensure that an event kind is valid.
69
+
70
+ All event kinds with a "numba-cuda:" prefix must be defined in the pre-defined
71
+ ``numba.cuda.core.event._builtin_kinds``.
72
+ Custom event kinds are allowed by not using the above prefix.
73
+
74
+ Parameters
75
+ ----------
76
+ kind : str
77
+
78
+ Return
79
+ ------
80
+ res : str
81
+ """
82
+ if kind.startswith("numba-cuda:") and kind not in _builtin_kinds:
83
+ msg = (
84
+ f"{kind} is not a valid event kind, "
85
+ "it starts with the reserved prefix 'numba-cuda:'"
86
+ )
87
+ raise ValueError(msg)
88
+ if kind.startswith("numba:"):
89
+ msg = (
90
+ f"{kind} is using invalid reserved prefix 'numba:' "
91
+ "it should either start with the reserved prefix 'numba-cuda:'"
92
+ ", or be a custom event kind"
93
+ )
94
+ raise ValueError(msg)
95
+ return kind
96
+
97
+
98
+ class Event:
99
+ """An event.
100
+
101
+ Parameters
102
+ ----------
103
+ kind : str
104
+ status : EventStatus
105
+ data : any; optional
106
+ Additional data for the event.
107
+ exc_details : 3-tuple; optional
108
+ Same 3-tuple for ``__exit__``.
109
+ """
110
+
111
+ def __init__(self, kind, status, data=None, exc_details=None):
112
+ self._kind = _guard_kind(kind)
113
+ self._status = status
114
+ self._data = data
115
+ self._exc_details = (
116
+ None
117
+ if exc_details is None or exc_details[0] is None
118
+ else exc_details
119
+ )
120
+
121
+ @property
122
+ def kind(self):
123
+ """Event kind
124
+
125
+ Returns
126
+ -------
127
+ res : str
128
+ """
129
+ return self._kind
130
+
131
+ @property
132
+ def status(self):
133
+ """Event status
134
+
135
+ Returns
136
+ -------
137
+ res : EventStatus
138
+ """
139
+ return self._status
140
+
141
+ @property
142
+ def data(self):
143
+ """Event data
144
+
145
+ Returns
146
+ -------
147
+ res : object
148
+ """
149
+ return self._data
150
+
151
+ @property
152
+ def is_start(self):
153
+ """Is it a *START* event?
154
+
155
+ Returns
156
+ -------
157
+ res : bool
158
+ """
159
+ return self._status == EventStatus.START
160
+
161
+ @property
162
+ def is_end(self):
163
+ """Is it an *END* event?
164
+
165
+ Returns
166
+ -------
167
+ res : bool
168
+ """
169
+ return self._status == EventStatus.END
170
+
171
+ @property
172
+ def is_failed(self):
173
+ """Is the event carrying an exception?
174
+
175
+ This is used for *END* event. This method will never return ``True``
176
+ in a *START* event.
177
+
178
+ Returns
179
+ -------
180
+ res : bool
181
+ """
182
+ return self._exc_details is None
183
+
184
+ def __str__(self):
185
+ data = (
186
+ f"{type(self.data).__qualname__}"
187
+ if self.data is not None
188
+ else "None"
189
+ )
190
+ return f"Event({self._kind}, {self._status}, data: {data})"
191
+
192
+ __repr__ = __str__
193
+
194
+
195
+ _registered = defaultdict(list)
196
+
197
+
198
+ def register(kind, listener):
199
+ """Register a listener for a given event kind.
200
+
201
+ Parameters
202
+ ----------
203
+ kind : str
204
+ listener : Listener
205
+ """
206
+ assert isinstance(listener, Listener)
207
+ kind = _guard_kind(kind)
208
+ _registered[kind].append(listener)
209
+
210
+
211
+ def unregister(kind, listener):
212
+ """Unregister a listener for a given event kind.
213
+
214
+ Parameters
215
+ ----------
216
+ kind : str
217
+ listener : Listener
218
+ """
219
+ assert isinstance(listener, Listener)
220
+ kind = _guard_kind(kind)
221
+ lst = _registered[kind]
222
+ lst.remove(listener)
223
+
224
+
225
+ def broadcast(event):
226
+ """Broadcast an event to all registered listeners.
227
+
228
+ Parameters
229
+ ----------
230
+ event : Event
231
+ """
232
+ for listener in _registered[event.kind]:
233
+ listener.notify(event)
234
+
235
+
236
+ class Listener(abc.ABC):
237
+ """Base class for all event listeners."""
238
+
239
+ @abc.abstractmethod
240
+ def on_start(self, event):
241
+ """Called when there is a *START* event.
242
+
243
+ Parameters
244
+ ----------
245
+ event : Event
246
+ """
247
+ pass
248
+
249
+ @abc.abstractmethod
250
+ def on_end(self, event):
251
+ """Called when there is a *END* event.
252
+
253
+ Parameters
254
+ ----------
255
+ event : Event
256
+ """
257
+ pass
258
+
259
+ def notify(self, event):
260
+ """Notify this Listener with the given Event.
261
+
262
+ Parameters
263
+ ----------
264
+ event : Event
265
+ """
266
+ if event.is_start:
267
+ self.on_start(event)
268
+ elif event.is_end:
269
+ self.on_end(event)
270
+ else:
271
+ raise AssertionError("unreachable")
272
+
273
+
274
+ class TimingListener(Listener):
275
+ """A listener that measures the total time spent between *START* and
276
+ *END* events during the time this listener is active.
277
+ """
278
+
279
+ def __init__(self):
280
+ self._depth = 0
281
+
282
+ def on_start(self, event):
283
+ if self._depth == 0:
284
+ self._ts = timer()
285
+ self._depth += 1
286
+
287
+ def on_end(self, event):
288
+ self._depth -= 1
289
+ if self._depth == 0:
290
+ last = getattr(self, "_duration", 0)
291
+ self._duration = (timer() - self._ts) + last
292
+
293
+ @property
294
+ def done(self):
295
+ """Returns a ``bool`` indicating whether a measurement has been made.
296
+
297
+ When this returns ``False``, the matching event has never fired.
298
+ If and only if this returns ``True``, ``.duration`` can be read without
299
+ error.
300
+ """
301
+ return hasattr(self, "_duration")
302
+
303
+ @property
304
+ def duration(self):
305
+ """Returns the measured duration.
306
+
307
+ This may raise ``AttributeError``. Users can use ``.done`` to check
308
+ that a measurement has been made.
309
+ """
310
+ return self._duration
311
+
312
+
313
+ class RecordingListener(Listener):
314
+ """A listener that records all events and stores them in the ``.buffer``
315
+ attribute as a list of 2-tuple ``(float, Event)``, where the first element
316
+ is the time the event occurred as returned by ``time.time()`` and the second
317
+ element is the event.
318
+ """
319
+
320
+ def __init__(self):
321
+ self.buffer = []
322
+
323
+ def on_start(self, event):
324
+ self.buffer.append((time.time(), event))
325
+
326
+ def on_end(self, event):
327
+ self.buffer.append((time.time(), event))
328
+
329
+
330
+ @contextmanager
331
+ def install_listener(kind, listener):
332
+ """Install a listener for event "kind" temporarily within the duration of
333
+ the context.
334
+
335
+ Returns
336
+ -------
337
+ res : Listener
338
+ The *listener* provided.
339
+
340
+ Examples
341
+ --------
342
+
343
+ >>> with install_listener("numba-cuda:compile", listener):
344
+ >>> some_code() # listener will be active here.
345
+ >>> other_code() # listener will be unregistered by this point.
346
+
347
+ """
348
+ register(kind, listener)
349
+ try:
350
+ yield listener
351
+ finally:
352
+ unregister(kind, listener)
353
+
354
+
355
+ @contextmanager
356
+ def install_timer(kind, callback):
357
+ """Install a TimingListener temporarily to measure the duration of
358
+ an event.
359
+
360
+ If the context completes successfully, the *callback* function is executed.
361
+ The *callback* function is expected to take a float argument for the
362
+ duration in seconds.
363
+
364
+ Returns
365
+ -------
366
+ res : TimingListener
367
+
368
+ Examples
369
+ --------
370
+
371
+ This is equivalent to:
372
+
373
+ >>> with install_listener(kind, TimingListener()) as res:
374
+ >>> ...
375
+ """
376
+ tl = TimingListener()
377
+ with install_listener(kind, tl):
378
+ yield tl
379
+
380
+ if tl.done:
381
+ callback(tl.duration)
382
+
383
+
384
+ @contextmanager
385
+ def install_recorder(kind):
386
+ """Install a RecordingListener temporarily to record all events.
387
+
388
+ Once the context is closed, users can use ``RecordingListener.buffer``
389
+ to access the recorded events.
390
+
391
+ Returns
392
+ -------
393
+ res : RecordingListener
394
+
395
+ Examples
396
+ --------
397
+
398
+ This is equivalent to:
399
+
400
+ >>> with install_listener(kind, RecordingListener()) as res:
401
+ >>> ...
402
+ """
403
+ rl = RecordingListener()
404
+ with install_listener(kind, rl):
405
+ yield rl
406
+
407
+
408
+ def start_event(kind, data=None):
409
+ """Trigger the start of an event of *kind* with *data*.
410
+
411
+ Parameters
412
+ ----------
413
+ kind : str
414
+ Event kind.
415
+ data : any; optional
416
+ Extra event data.
417
+ """
418
+ evt = Event(kind=kind, status=EventStatus.START, data=data)
419
+ broadcast(evt)
420
+
421
+
422
+ def end_event(kind, data=None, exc_details=None):
423
+ """Trigger the end of an event of *kind*, *exc_details*.
424
+
425
+ Parameters
426
+ ----------
427
+ kind : str
428
+ Event kind.
429
+ data : any; optional
430
+ Extra event data.
431
+ exc_details : 3-tuple; optional
432
+ Same 3-tuple for ``__exit__``. Or, ``None`` if no error.
433
+ """
434
+ evt = Event(
435
+ kind=kind,
436
+ status=EventStatus.END,
437
+ data=data,
438
+ exc_details=exc_details,
439
+ )
440
+ broadcast(evt)
441
+
442
+
443
+ @contextmanager
444
+ def trigger_event(kind, data=None):
445
+ """A context manager to trigger the start and end events of *kind* with
446
+ *data*. The start event is triggered when entering the context.
447
+ The end event is triggered when exiting the context.
448
+
449
+ Parameters
450
+ ----------
451
+ kind : str
452
+ Event kind.
453
+ data : any; optional
454
+ Extra event data.
455
+ """
456
+ with ExitStack() as scope:
457
+
458
+ @scope.push
459
+ def on_exit(*exc_details):
460
+ end_event(kind, data=data, exc_details=exc_details)
461
+
462
+ start_event(kind, data=data)
463
+ yield
464
+
465
+
466
+ def _prepare_chrome_trace_data(listener: RecordingListener):
467
+ """Prepare events in `listener` for serializing as chrome trace data."""
468
+ # The spec for the trace event format can be found at:
469
+ # https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit # noqa
470
+ # This code only uses the JSON Array Format for simplicity.
471
+ pid = os.getpid()
472
+ tid = threading.get_native_id()
473
+ evs = []
474
+ for ts, rec in listener.buffer:
475
+ data = rec.data
476
+ cat = str(rec.kind)
477
+ ts_scaled = ts * 1_000_000 # scale to microseconds
478
+ ph = "B" if rec.is_start else "E"
479
+ name = data["name"]
480
+ args = data
481
+ ev = dict(
482
+ cat=cat,
483
+ pid=pid,
484
+ tid=tid,
485
+ ts=ts_scaled,
486
+ ph=ph,
487
+ name=name,
488
+ args=args,
489
+ )
490
+ evs.append(ev)
491
+ return evs
492
+
493
+
494
+ def _setup_chrome_trace_exit_handler():
495
+ """Setup a RecordingListener and an exit handler to write the captured
496
+ events to file.
497
+ """
498
+ listener = RecordingListener()
499
+ register("numba-cuda:run_pass", listener)
500
+ filename = config.CHROME_TRACE
501
+
502
+ @atexit.register
503
+ def _write_chrome_trace():
504
+ # The following output file is not multi-process safe.
505
+ evs = _prepare_chrome_trace_data(listener)
506
+ with open(filename, "w") as out:
507
+ json.dump(evs, out, cls=utils._LazyJSONEncoder)
508
+
509
+
510
+ if config.CHROME_TRACE:
511
+ _setup_chrome_trace_exit_handler()