nsight-python 1.0.0__py3-none-any.whl → 1.1.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.
- nsight/__init__.py +29 -5
- nsight/analyze.py +200 -17
- nsight/annotation.py +26 -11
- nsight/collection/__init__.py +2 -1
- nsight/collection/core.py +191 -20
- nsight/collection/cupti.py +448 -0
- nsight/collection/ncu.py +158 -47
- nsight/exceptions.py +2 -0
- nsight/experimental.py +86 -0
- nsight/extraction.py +258 -51
- nsight/info_collector.py +364 -0
- nsight/thermovision.py +10 -18
- nsight/tools_manager.py +98 -0
- nsight/transformation.py +31 -12
- nsight/utils.py +40 -12
- {nsight_python-1.0.0.dist-info → nsight_python-1.1.1.dist-info}/METADATA +4 -2
- nsight_python-1.1.1.dist-info/RECORD +20 -0
- {nsight_python-1.0.0.dist-info → nsight_python-1.1.1.dist-info}/WHEEL +1 -1
- nsight_python-1.0.0.dist-info/RECORD +0 -16
- {nsight_python-1.0.0.dist-info → nsight_python-1.1.1.dist-info}/licenses/LICENSE +0 -0
nsight/__init__.py
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
|
+
import os
|
|
4
5
|
from importlib.metadata import version
|
|
5
6
|
|
|
6
|
-
from nsight import analyze
|
|
7
|
+
from nsight import analyze, exceptions, experimental
|
|
7
8
|
from nsight.annotation import annotate
|
|
8
|
-
from nsight.collection
|
|
9
|
+
from nsight.collection import ncu
|
|
10
|
+
from nsight.info_collector import CollectionScope, InfoCollector
|
|
11
|
+
from nsight.tools_manager import Tool, activate, deactivate, get_active_tool
|
|
9
12
|
from nsight.utils import VerbosityLevel, col_panel, row_panel
|
|
10
13
|
|
|
11
|
-
#
|
|
12
|
-
|
|
14
|
+
# NVIDIA Nsight Compute must attach before CUDA is initialized, so by default it
|
|
15
|
+
# is activated here -- importing nsight early is enough to satisfy that. Set
|
|
16
|
+
# NSPY_NCU_INIT_AT_IMPORT=0 to disable this and activate a tool yourself.
|
|
17
|
+
if os.environ.get("NSPY_NCU_INIT_AT_IMPORT", "1") != "0":
|
|
18
|
+
try:
|
|
19
|
+
activate(Tool.NCU)
|
|
20
|
+
except (exceptions.ProfilerException, exceptions.NCUNotAvailableError) as exc:
|
|
21
|
+
# Importing nsight must keep working when the NVIDIA Nsight Compute
|
|
22
|
+
# injection fails to load, so record the error rather than raising it
|
|
23
|
+
# here. NCUCollector.collect re-raises it late, when a decorated
|
|
24
|
+
# function is first called.
|
|
25
|
+
ncu.injection_load_error = exc
|
|
13
26
|
|
|
14
27
|
__version__ = version("nsight-python")
|
|
15
28
|
|
|
16
|
-
__all__ = [
|
|
29
|
+
__all__ = [
|
|
30
|
+
"CollectionScope",
|
|
31
|
+
"InfoCollector",
|
|
32
|
+
"Tool",
|
|
33
|
+
"VerbosityLevel",
|
|
34
|
+
"activate",
|
|
35
|
+
"analyze",
|
|
36
|
+
"annotate",
|
|
37
|
+
"deactivate",
|
|
38
|
+
"experimental",
|
|
39
|
+
"get_active_tool",
|
|
40
|
+
]
|
nsight/analyze.py
CHANGED
|
@@ -16,6 +16,8 @@ from numpy.typing import NDArray
|
|
|
16
16
|
|
|
17
17
|
import nsight.collection as collection
|
|
18
18
|
import nsight.visualization as visualization
|
|
19
|
+
from nsight.info_collector import CollectionScope, InfoCollector
|
|
20
|
+
from nsight.tools_manager import Tool, activate, get_active_tool
|
|
19
21
|
from nsight.utils import VerbosityLevel
|
|
20
22
|
|
|
21
23
|
|
|
@@ -54,6 +56,14 @@ def kernel(
|
|
|
54
56
|
combine_kernel_metrics: Callable[[float, float], float] | None = None,
|
|
55
57
|
output_prefix: str | None = None,
|
|
56
58
|
output_csv: bool = False,
|
|
59
|
+
info_collectors: (
|
|
60
|
+
Sequence[
|
|
61
|
+
InfoCollector
|
|
62
|
+
| tuple[str, Callable[..., Any], str | CollectionScope]
|
|
63
|
+
| Callable[..., Any]
|
|
64
|
+
]
|
|
65
|
+
| None
|
|
66
|
+
) = None,
|
|
57
67
|
) -> Callable[[Callable[..., None]], Callable[..., collection.core.ProfileResults]]: ...
|
|
58
68
|
|
|
59
69
|
|
|
@@ -84,12 +94,20 @@ def kernel(
|
|
|
84
94
|
combine_kernel_metrics: Callable[[float, float], float] | None = None,
|
|
85
95
|
output_prefix: str | None = None,
|
|
86
96
|
output_csv: bool = False,
|
|
97
|
+
info_collectors: (
|
|
98
|
+
Sequence[
|
|
99
|
+
InfoCollector
|
|
100
|
+
| tuple[str, Callable[..., Any], str | CollectionScope]
|
|
101
|
+
| Callable[..., Any]
|
|
102
|
+
]
|
|
103
|
+
| None
|
|
104
|
+
) = None,
|
|
87
105
|
) -> (
|
|
88
106
|
Callable[..., collection.core.ProfileResults]
|
|
89
107
|
| Callable[[Callable[..., None]], Callable[..., collection.core.ProfileResults]]
|
|
90
108
|
):
|
|
91
109
|
"""
|
|
92
|
-
A decorator that collects profiling data
|
|
110
|
+
A decorator that collects profiling data from GPU kernels.
|
|
93
111
|
|
|
94
112
|
Can be used with or without parentheses:
|
|
95
113
|
- ``@nsight.analyze.kernel`` (no parentheses)
|
|
@@ -106,6 +124,14 @@ def kernel(
|
|
|
106
124
|
- ``**kwargs``: Original function keyword arguments
|
|
107
125
|
- Returns ``ProfileResults`` object containing profiling data
|
|
108
126
|
|
|
127
|
+
.. warning::
|
|
128
|
+
Decorating a function changes its return value. Any value returned by the
|
|
129
|
+
original function is ignored, and calling the decorated function returns a
|
|
130
|
+
``ProfileResults`` object instead. If the original function returns a
|
|
131
|
+
non-``None`` value, Nsight Python emits a ``RuntimeWarning``. Keep any
|
|
132
|
+
computation whose result you need in an undecorated helper and use the
|
|
133
|
+
decorated function only as a profiling wrapper.
|
|
134
|
+
|
|
109
135
|
.. note::
|
|
110
136
|
Configs are passed to the decorated function as positional arguments.
|
|
111
137
|
Both positional and keyword-only parameters are supported in the function
|
|
@@ -113,6 +139,14 @@ def kernel(
|
|
|
113
139
|
but will always be empty during profiling. Parameters with default values
|
|
114
140
|
may be omitted from configs — missing values will be filled from the defaults.
|
|
115
141
|
|
|
142
|
+
Profiling uses NVIDIA Nsight Compute by default. Activating it is automatic
|
|
143
|
+
-- it happens during ``import nsight`` -- and needs no attention from you.
|
|
144
|
+
For more control over when NVIDIA Nsight Compute is loaded, see the
|
|
145
|
+
:doc:`/tools` page.
|
|
146
|
+
|
|
147
|
+
``@nsight.analyze.kernel`` supports collection of
|
|
148
|
+
``"gpu__time_duration.sum"`` via the low-overhead, experimental CUPTI
|
|
149
|
+
backend. See the :doc:`/tools` page for how to enable it.
|
|
116
150
|
|
|
117
151
|
Parameters:
|
|
118
152
|
configs: An iterable of configurations to run the function with. Each configuration can be either:
|
|
@@ -134,7 +168,7 @@ def kernel(
|
|
|
134
168
|
derive_metric:
|
|
135
169
|
A function to transform the collected metrics.
|
|
136
170
|
This can be used to compute derived metrics like TFLOPs that cannot
|
|
137
|
-
be captured
|
|
171
|
+
be captured directly. The function takes the metric values and
|
|
138
172
|
the arguments of the profile-decorated function and returns the new
|
|
139
173
|
metrics. Return value can be either:
|
|
140
174
|
|
|
@@ -157,7 +191,7 @@ def kernel(
|
|
|
157
191
|
Combine with ``derive_metric`` to compute speedup (reciprocal of normalized value).
|
|
158
192
|
metrics: The metrics to collect. By default, kernel runtimes in nanoseconds
|
|
159
193
|
are collected. Default: ``["gpu__time_duration.sum"]``. To see the available
|
|
160
|
-
metrics on your system, use the command: ``ncu --query-metrics``.
|
|
194
|
+
metrics on your system when using ncu, use the command: ``ncu --query-metrics``.
|
|
161
195
|
ignore_kernel_list:
|
|
162
196
|
List of kernel names to ignore. If you call a library within an annotated range context, you might not have precise control over which and how many kernels are being launched.
|
|
163
197
|
If some of these kernels should be ignored in the profile, their names can be provided in this parameter. Default: ``None``
|
|
@@ -167,13 +201,19 @@ def kernel(
|
|
|
167
201
|
how to summarize the collected metrics into a single number. For example,
|
|
168
202
|
if we profile runtime and want to sum the times of all kernels we can specify
|
|
169
203
|
``combine_kernel_metrics = lambda x, y: x + y``. The function should take
|
|
170
|
-
two arguments and return a single value.
|
|
204
|
+
two arguments and return a single value. Each annotation must still launch
|
|
205
|
+
the same number of kernels across configurations and repeated runs;
|
|
206
|
+
different counts may cause an error or incorrect results. Default: ``None``.
|
|
171
207
|
clock_control: Control the behavior of the GPU clocks during profiling. Allowed values:
|
|
172
208
|
|
|
173
209
|
- ``"base"``: GPC and memory clocks are locked to their respective base frequency during profiling. This has no impact on thermal throttling. Note that actual clocks might still vary, depending on the level of driver support for this feature. As an alternative, use nvidia-smi to lock the clocks externally and set this option to ``"none"``.
|
|
174
210
|
- ``"none"``: No GPC or memory frequencies are changed during profiling.
|
|
175
211
|
|
|
176
212
|
Default: ``"none"``
|
|
213
|
+
|
|
214
|
+
Refer the `NVIDIA Nsight Compute documentation on Clock Control <https://docs.nvidia.com/nsight-compute/ProfilingGuide/index.html#clock-control>`_ for more details.
|
|
215
|
+
|
|
216
|
+
Ignored when the CUPTI backend is active.
|
|
177
217
|
cache_control: Control the behavior of the GPU caches during profiling. Allowed values:
|
|
178
218
|
|
|
179
219
|
- ``"all"``: All GPU caches are flushed before each kernel replay iteration during profiling. While metric values in the execution environment of the application might be slightly different without invalidating the caches, this mode offers the most reproducible metric results across the replay passes and also across multiple runs of the target application.
|
|
@@ -181,6 +221,8 @@ def kernel(
|
|
|
181
221
|
|
|
182
222
|
Default: ``"all"``
|
|
183
223
|
|
|
224
|
+
Ignored when the CUPTI backend is active.
|
|
225
|
+
|
|
184
226
|
replay_mode: Mechanism used for replaying a kernel launch multiple times to collect selected metrics. Allowed values:
|
|
185
227
|
|
|
186
228
|
- ``"kernel"``: Replay individual kernel launches during the execution of the application.
|
|
@@ -188,6 +230,8 @@ def kernel(
|
|
|
188
230
|
|
|
189
231
|
Default: ``"kernel"``
|
|
190
232
|
|
|
233
|
+
Ignored when the CUPTI backend is active.
|
|
234
|
+
|
|
191
235
|
thermal_mode: Controls GPU thermal management mode. Default: ``"auto"``
|
|
192
236
|
|
|
193
237
|
Monitors GPU thermal headroom (T.Limit) and pauses profiling when GPU gets
|
|
@@ -280,8 +324,8 @@ def kernel(
|
|
|
280
324
|
- ``NumRuns``: Number of runs used for aggregation
|
|
281
325
|
- ``CI95_Lower``: Lower bound of the 95% confidence interval
|
|
282
326
|
- ``CI95_Upper``: Upper bound of the 95% confidence interval
|
|
283
|
-
- ``RelativeStdDevPct``: Standard deviation as a percentage of the mean
|
|
284
|
-
- ``StableMeasurement``: Boolean
|
|
327
|
+
- ``RelativeStdDevPct``: Standard deviation as a percentage of the absolute mean
|
|
328
|
+
- ``StableMeasurement``: Nullable Boolean: true if ``RelativeStdDevPct`` < 2%, false otherwise, and ``pd.NA`` when relative variability is undefined (for example, with one valid sample or a zero mean).
|
|
285
329
|
- ``Metric``: The metrics being collected and the metrics being derived
|
|
286
330
|
- ``Unit``: Unit of measurement for the metric value (e.g., ``ns`` for nanoseconds)
|
|
287
331
|
- ``Kernel``: Name of the GPU kernel(s) launched
|
|
@@ -289,10 +333,70 @@ def kernel(
|
|
|
289
333
|
- ``Host``: Host machine name
|
|
290
334
|
- ``ComputeClock``: GPU compute clock frequency
|
|
291
335
|
- ``MemoryClock``: GPU memory clock frequency
|
|
336
|
+
info_collectors: Optional list of custom information collectors.
|
|
337
|
+
Collectors should be functions decorated with
|
|
338
|
+
``@nsight.experimental.collect(scope=...)``.
|
|
339
|
+
The function name will be used as the column name in the DataFrame.
|
|
340
|
+
|
|
341
|
+
**Decorated functions** (recommended)::
|
|
342
|
+
|
|
343
|
+
@nsight.experimental.collect(scope=nsight.CollectionScope.ONCE)
|
|
344
|
+
def driver_version():
|
|
345
|
+
from cuda.core import system
|
|
346
|
+
return ".".join(
|
|
347
|
+
str(part) for part in system.get_user_mode_driver_version()
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
@nsight.experimental.collect(scope=nsight.CollectionScope.CONFIG)
|
|
351
|
+
def matrix_elements(n, dtype):
|
|
352
|
+
return n * n
|
|
353
|
+
|
|
354
|
+
@nsight.experimental.collect(scope=nsight.CollectionScope.RUN)
|
|
355
|
+
def gpu_temp_c(n, dtype):
|
|
356
|
+
return get_gpu_temperature()
|
|
357
|
+
|
|
358
|
+
@nsight.experimental.collect(
|
|
359
|
+
scope=nsight.CollectionScope.ANNOTATION
|
|
360
|
+
)
|
|
361
|
+
def ann_timestamp(annotation_name, *config_args):
|
|
362
|
+
return time.time()
|
|
363
|
+
|
|
364
|
+
@nsight.analyze.kernel(
|
|
365
|
+
info_collectors=[
|
|
366
|
+
driver_version, matrix_elements, gpu_temp_c, ann_timestamp
|
|
367
|
+
]
|
|
368
|
+
)
|
|
369
|
+
def my_benchmark(n, dtype):
|
|
370
|
+
...
|
|
371
|
+
|
|
372
|
+
Collectors may also be supplied as ``(name, callback, scope)``
|
|
373
|
+
tuples, where ``scope`` is ``"once"``, ``"config"``, ``"run"``,
|
|
374
|
+
or ``"annotation"`` (or a :class:`nsight.CollectionScope`).
|
|
375
|
+
|
|
376
|
+
Column naming: ``once``-scope and non-numeric collectors produce a
|
|
377
|
+
single column named after the function. Numeric ``config``/``run``/
|
|
378
|
+
``annotation``-scope collectors are aggregated across runs into four
|
|
379
|
+
columns ``<name>_Avg``, ``<name>_Std``, ``<name>_Min``, ``<name>_Max``.
|
|
292
380
|
"""
|
|
293
381
|
# Strip whitespace
|
|
294
382
|
metrics = [m.strip() for m in metrics]
|
|
295
383
|
|
|
384
|
+
# These are decorator parameters, so reject illegal values now rather than
|
|
385
|
+
# when the decorated function is first called. Whether the active tool
|
|
386
|
+
# supports a legal value is a separate check, made by the collector.
|
|
387
|
+
if clock_control not in ("base", "none"):
|
|
388
|
+
raise ValueError(
|
|
389
|
+
f"Invalid clock_control {clock_control!r}. Expected 'base' or 'none'."
|
|
390
|
+
)
|
|
391
|
+
if cache_control not in ("all", "none"):
|
|
392
|
+
raise ValueError(
|
|
393
|
+
f"Invalid cache_control {cache_control!r}. Expected 'all' or 'none'."
|
|
394
|
+
)
|
|
395
|
+
if replay_mode not in ("kernel", "range"):
|
|
396
|
+
raise ValueError(
|
|
397
|
+
f"Invalid replay_mode {replay_mode!r}. Expected 'kernel' or 'range'."
|
|
398
|
+
)
|
|
399
|
+
|
|
296
400
|
# Validate thermal parameters if both are provided
|
|
297
401
|
if thermal_wait is not None and thermal_cont is not None:
|
|
298
402
|
if thermal_cont <= thermal_wait:
|
|
@@ -309,10 +413,72 @@ def kernel(
|
|
|
309
413
|
prefix = output_prefix
|
|
310
414
|
if prefix is None:
|
|
311
415
|
prefix = tempfile.mkdtemp(prefix="nspy_")
|
|
312
|
-
prefix = os.path.join(prefix, "") # Adds a trailing
|
|
416
|
+
prefix = os.path.join(prefix, "") # Adds a trailing path separator
|
|
313
417
|
else:
|
|
314
418
|
os.makedirs(os.path.dirname(prefix) or ".", exist_ok=True)
|
|
315
419
|
|
|
420
|
+
# Normalize info_collectors: convert decorated functions to
|
|
421
|
+
# (name, callback, scope) tuples with a canonical string scope.
|
|
422
|
+
valid_scopes = {s.value for s in CollectionScope}
|
|
423
|
+
|
|
424
|
+
def _canonical_scope(scope: Any, label: str) -> str:
|
|
425
|
+
if isinstance(scope, CollectionScope):
|
|
426
|
+
return scope.value
|
|
427
|
+
if isinstance(scope, str) and scope in valid_scopes:
|
|
428
|
+
return scope
|
|
429
|
+
raise ValueError(
|
|
430
|
+
f"Invalid scope {scope!r} for collector {label}. "
|
|
431
|
+
f"Expected one of {sorted(valid_scopes)} or a CollectionScope."
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
normalized_collectors = None
|
|
435
|
+
if info_collectors:
|
|
436
|
+
normalized_collectors = []
|
|
437
|
+
for collector in info_collectors:
|
|
438
|
+
if isinstance(collector, InfoCollector):
|
|
439
|
+
# An InfoCollector instance (the exported public class).
|
|
440
|
+
normalized_collectors.append(
|
|
441
|
+
(
|
|
442
|
+
collector.name,
|
|
443
|
+
collector.callback,
|
|
444
|
+
_canonical_scope(collector.scope, repr(collector.name)),
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
elif isinstance(collector, tuple):
|
|
448
|
+
# Raw tuple format: (name, callback, scope)
|
|
449
|
+
if len(collector) != 3:
|
|
450
|
+
raise ValueError(
|
|
451
|
+
"Tuple-format info collector must be "
|
|
452
|
+
f"(name, callback, scope); got {collector!r}."
|
|
453
|
+
)
|
|
454
|
+
name, callback, scope = collector
|
|
455
|
+
if not isinstance(name, str) or not callable(callback):
|
|
456
|
+
raise TypeError(
|
|
457
|
+
"Tuple-format info collector must be "
|
|
458
|
+
f"(str name, callable callback, scope); got {collector!r}."
|
|
459
|
+
)
|
|
460
|
+
normalized_collectors.append(
|
|
461
|
+
(name, callback, _canonical_scope(scope, repr(name)))
|
|
462
|
+
)
|
|
463
|
+
elif callable(collector):
|
|
464
|
+
# Decorated function: extract metadata
|
|
465
|
+
if hasattr(collector, "_nsight_collector_scope"):
|
|
466
|
+
name = getattr(collector, "_nsight_collector_name")
|
|
467
|
+
scope = getattr(collector, "_nsight_collector_scope")
|
|
468
|
+
normalized_collectors.append(
|
|
469
|
+
(name, collector, _canonical_scope(scope, repr(name)))
|
|
470
|
+
)
|
|
471
|
+
else:
|
|
472
|
+
raise ValueError(
|
|
473
|
+
f"Collector function {collector.__name__} is missing "
|
|
474
|
+
"@nsight.experimental.collect(scope=...) decorator"
|
|
475
|
+
)
|
|
476
|
+
else:
|
|
477
|
+
raise TypeError(
|
|
478
|
+
f"Invalid collector type: {type(collector)}. "
|
|
479
|
+
"Expected tuple (name, callback, scope) or decorated function."
|
|
480
|
+
)
|
|
481
|
+
|
|
316
482
|
settings = collection.core.ProfileSettings(
|
|
317
483
|
configs=configs,
|
|
318
484
|
runs=runs,
|
|
@@ -326,16 +492,28 @@ def kernel(
|
|
|
326
492
|
thermal_device=thermal_device,
|
|
327
493
|
output_prefix=prefix,
|
|
328
494
|
output_csv=output_csv,
|
|
495
|
+
info_collectors=normalized_collectors,
|
|
329
496
|
)
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
497
|
+
|
|
498
|
+
def get_collector() -> collection.core.NsightCollector:
|
|
499
|
+
if get_active_tool() == Tool.CUPTI:
|
|
500
|
+
# clock_control, cache_control and replay_mode have no
|
|
501
|
+
# meaning for CUPTI, so the collector does not take them.
|
|
502
|
+
return collection.cupti.CUPTICollector(
|
|
503
|
+
metrics=metrics,
|
|
504
|
+
ignore_kernel_list=ignore_kernel_list,
|
|
505
|
+
combine_kernel_metrics=combine_kernel_metrics,
|
|
506
|
+
)
|
|
507
|
+
return collection.ncu.NCUCollector(
|
|
508
|
+
metrics=metrics,
|
|
509
|
+
ignore_kernel_list=ignore_kernel_list,
|
|
510
|
+
combine_kernel_metrics=combine_kernel_metrics,
|
|
511
|
+
clock_control=clock_control,
|
|
512
|
+
cache_control=cache_control,
|
|
513
|
+
replay_mode=replay_mode,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
return collection.core.NsightProfiler(settings, get_collector)
|
|
339
517
|
|
|
340
518
|
# Support both @kernel and @kernel() syntax
|
|
341
519
|
if _func is None:
|
|
@@ -410,7 +588,7 @@ def plot(
|
|
|
410
588
|
ylabel: str | None = None,
|
|
411
589
|
annotate_points: bool = False,
|
|
412
590
|
show_aggregate: str | None = None,
|
|
413
|
-
plot_type:
|
|
591
|
+
plot_type: Literal["line", "bar"] = "line",
|
|
414
592
|
plot_width: int = 6,
|
|
415
593
|
plot_height: int = 4,
|
|
416
594
|
row_panels: Sequence[str] | None = None,
|
|
@@ -476,6 +654,11 @@ def plot(
|
|
|
476
654
|
variant_fields: List of config fields to use as variant fields (lines).
|
|
477
655
|
variant_annotations: List of annotated range names for which to apply variant splitting. The provided strings must each match one of the names defined using nsight.annotate.
|
|
478
656
|
"""
|
|
657
|
+
# Reject illegal values now rather than when the decorated function is
|
|
658
|
+
# first called.
|
|
659
|
+
if plot_type not in ("line", "bar"):
|
|
660
|
+
raise ValueError(f"Invalid plot_type {plot_type!r}. Expected 'line' or 'bar'.")
|
|
661
|
+
|
|
479
662
|
show_avg = show_aggregate == "avg"
|
|
480
663
|
show_geomean = show_aggregate == "geomean"
|
|
481
664
|
|
nsight/annotation.py
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
import functools
|
|
5
5
|
import threading
|
|
6
|
+
import warnings
|
|
6
7
|
from collections.abc import Callable
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
import nvtx
|
|
10
11
|
|
|
11
12
|
import nsight.utils as utils
|
|
13
|
+
from nsight import info_collector
|
|
12
14
|
from nsight.exceptions import CUDA_CORE_UNAVAILABLE_MSG
|
|
13
15
|
|
|
14
16
|
# Thread-local storage for tracking annotations
|
|
@@ -88,7 +90,7 @@ def is_active_annotation(name: str) -> bool:
|
|
|
88
90
|
return False
|
|
89
91
|
|
|
90
92
|
|
|
91
|
-
class annotate
|
|
93
|
+
class annotate:
|
|
92
94
|
"""
|
|
93
95
|
A decorator/context-manager hybrid for marking profiling regions.
|
|
94
96
|
The encapsulated code will be profiled and associated with an NVTX
|
|
@@ -111,7 +113,9 @@ class annotate(nvtx.annotate): # type: ignore[misc]
|
|
|
111
113
|
ignore_failures: Flag indicating whether to ignore
|
|
112
114
|
failures in the annotate context. If set to ``True``, any exceptions
|
|
113
115
|
raised within the context will be ignored, and the profiling will
|
|
114
|
-
continue. The measured metric for this run will be set to NaN
|
|
116
|
+
continue. The measured metric for this run will be set to NaN, and
|
|
117
|
+
a ``RuntimeWarning`` naming the annotation and the ignored exception
|
|
118
|
+
is emitted.
|
|
115
119
|
Default: ``False``
|
|
116
120
|
|
|
117
121
|
Raises:
|
|
@@ -129,10 +133,10 @@ class annotate(nvtx.annotate): # type: ignore[misc]
|
|
|
129
133
|
Nested annotations are currently not supported. However, since each
|
|
130
134
|
annotation is expected to contain a single kernel launch by default,
|
|
131
135
|
nested annotations should not be necessary in typical usage scenarios.
|
|
132
|
-
Entering and exiting the annotation context
|
|
133
|
-
range
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
Entering and exiting the annotation context starts and ends an NVTX
|
|
137
|
+
range that is visible across all threads in the profiled process, so
|
|
138
|
+
kernels launched from worker threads within the annotated region are
|
|
139
|
+
also captured.
|
|
136
140
|
|
|
137
141
|
"""
|
|
138
142
|
|
|
@@ -144,9 +148,7 @@ class annotate(nvtx.annotate): # type: ignore[misc]
|
|
|
144
148
|
if ignore_failures and not utils.CUDA_CORE_AVAILABLE:
|
|
145
149
|
raise ImportError(CUDA_CORE_UNAVAILABLE_MSG)
|
|
146
150
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def __enter__(self) -> nvtx.annotate:
|
|
151
|
+
def __enter__(self) -> "annotate":
|
|
150
152
|
|
|
151
153
|
# Check for nested annotations
|
|
152
154
|
if _is_in_annotation():
|
|
@@ -164,19 +166,32 @@ class annotate(nvtx.annotate): # type: ignore[misc]
|
|
|
164
166
|
|
|
165
167
|
add_active_annotation(self.name)
|
|
166
168
|
_set_in_annotation(True)
|
|
167
|
-
|
|
169
|
+
# Collect annotation-scope information
|
|
170
|
+
info_collector.collect_for_annotation(self.name)
|
|
171
|
+
self._range_id = nvtx.start_range(message=self.name, domain=utils.NVTX_DOMAIN)
|
|
172
|
+
return self
|
|
168
173
|
|
|
169
174
|
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> bool:
|
|
170
175
|
try:
|
|
171
176
|
if exc_type and self.ignore_failures:
|
|
172
177
|
utils.launch_dummy_kernel_module()
|
|
173
178
|
finally:
|
|
174
|
-
|
|
179
|
+
nvtx.end_range(self._range_id)
|
|
175
180
|
_set_in_annotation(False)
|
|
176
181
|
|
|
177
182
|
if exc_type and not self.ignore_failures:
|
|
178
183
|
return False # propagate the exception
|
|
179
184
|
|
|
185
|
+
if exc_type:
|
|
186
|
+
# The failed run only shows up as NaN in the results, so report why
|
|
187
|
+
# it failed rather than discarding the exception.
|
|
188
|
+
warnings.warn(
|
|
189
|
+
f"Ignored failure in annotation '{self.name}': "
|
|
190
|
+
f"{exc_type.__name__}: {exc_value}",
|
|
191
|
+
category=RuntimeWarning,
|
|
192
|
+
stacklevel=2,
|
|
193
|
+
)
|
|
194
|
+
|
|
180
195
|
return True
|
|
181
196
|
|
|
182
197
|
def __call__(self, func: Callable[..., Any]) -> Callable[..., Any]:
|
nsight/collection/__init__.py
CHANGED