Skip to content

Commit 1e81951

Browse files
committed
Add documentation for the nsight.activate, nsight.deactivate APIs and
multiple profiling backends Signed-off-by: Alok Joshi <aljoshi@nvidia.com>
1 parent c33def2 commit 1e81951

8 files changed

Lines changed: 174 additions & 59 deletions

File tree

‎docs/source/index.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Welcome to the Nsight Python documentation!
4747

4848
analyze
4949
annotation
50+
tools
5051

5152
.. toctree::
5253
:hidden:

‎docs/source/installation/installation_from_pypi.rst‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ For CUDA Toolkit 13:
3535
3636
pip install nsight-python[cu13]
3737
38-
.. note::
39-
The ``[cu12]`` and ``[cu13]`` extras install the ``cuda-core`` package, which is only required if you plan to use ``ignore_failures=True`` in ``nsight.annotate``.
40-
All other features of Nsight Python work without this dependency.
38+
Optional: Installing with CUPTI Support
39+
----------------------------------------
4140

41+
If you want to use the experimental CUPTI backend with ``nsight.analyze.kernel``, you need to install the ``cupti-python`` package.
42+
This is an optional dependency that enables low-overhead collection of kernel time durations.
43+
44+
.. code-block:: bash
45+
46+
pip install nsight-python[cupti]

‎docs/source/installation/runtime_requirements.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Runtime Requirements
66

77
* **NVIDIA Nsight Compute from CUDA Toolkit 13.3 Update 1 or later**: nsight-python uses the NVIDIA Nsight Compute CLI (ncu 2026.2.1+). Ensure ``ncu`` is installed and available on the system path.
88

9+
* **NVIDIA CUPTI**: The :ref:`CUPTI backend <tools-cupti>` requires the CUPTI Library from CUDA 13.4 or greater. Installing Nsight Python with the ``cupti`` extra (see :doc:`installation_from_pypi`) installs ``nvidia-cuda-cupti``, which provides this library. See `CUPTI Compatibility and Requirements <https://docs.nvidia.com/cupti/main/main.html#cupti-compatibility-and-requirements>`_ for driver compatibility and other setup requirements.
10+
911
Import Ordering
1012
---------------
1113

@@ -15,4 +17,6 @@ Import Ordering
1517

1618
**Recommendation**: Place ``import nsight`` at the top of your script, before all CUDA-related imports and calls.
1719

20+
This ordering matters because ``import nsight`` activates NVIDIA Nsight Compute; see :doc:`/tools`.
21+
1822
* **Any NVTX API calls**: nsight-python updates ``NVTX_INJECTION64_PATH`` at import time. Any NVTX usage before ``import nsight`` will cause NVTX annotation ranges to be missed and profiling results to be incomplete.

‎docs/source/tools.rst‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
.. SPDX-License-Identifier: Apache-2.0
3+
4+
Tools
5+
=====
6+
7+
Nsight Python collects its data through an underlying NVIDIA tool. Only one tool
8+
can be active at a time, and :func:`nsight.activate` selects and loads it.
9+
10+
.. autofunction:: nsight.activate
11+
12+
.. autofunction:: nsight.deactivate
13+
14+
.. autofunction:: nsight.get_active_tool
15+
16+
.. autoclass:: nsight.Tool
17+
18+
Supported tools
19+
---------------
20+
21+
NVIDIA Nsight Compute
22+
^^^^^^^^^^^^^^^^^^^^^
23+
24+
:attr:`nsight.Tool.NCU` profiles individual kernels with the NVIDIA Nsight
25+
Compute CLI. It is the default tool for ``@nsight.analyze.kernel``.
26+
27+
Activation
28+
""""""""""
29+
30+
NVIDIA Nsight Compute's injection library has to be loaded before CUDA is
31+
initialized. It is loaded in one of three ways.
32+
33+
* **At import, by default.** ``import nsight`` activates NCU.
34+
* **Explicitly, for control over the timing.** Ensure the environment variable
35+
``NSPY_NCU_INIT_AT_IMPORT`` is set to ``0`` before ``nsight`` is imported,
36+
then activate NCU where it suits you. Explicit activation also surfaces setup
37+
errors at that point.
38+
39+
.. code-block:: python
40+
41+
import os
42+
43+
os.environ["NSPY_NCU_INIT_AT_IMPORT"] = "0"
44+
45+
import nsight
46+
47+
nsight.activate(nsight.Tool.NCU)
48+
49+
* **Lazily, on first use.** Under ``NSPY_NCU_INIT_AT_IMPORT=0``, the first call
50+
to a function decorated with ``@nsight.analyze.kernel`` loads the library.
51+
Ensure the first decorated call comes before any code that initializes CUDA.
52+
53+
Limitations
54+
"""""""""""
55+
56+
**Deactivation.** :func:`~nsight.deactivate` is not supported. The injection
57+
library cannot be unloaded, so NCU stays loaded for the lifetime of the process
58+
and remains the active tool once activated.
59+
60+
.. _tools-cupti:
61+
62+
NVIDIA CUPTI (experimental)
63+
^^^^^^^^^^^^^^^^^^^^^^^^^^^
64+
65+
:attr:`nsight.Tool.CUPTI` is an opt-in, low-overhead alternative for collecting
66+
kernel time durations, measured using CUPTI's Activity API.
67+
68+
Activation
69+
""""""""""
70+
71+
To activate CUPTI, ensure the environment variable
72+
``NSPY_NCU_INIT_AT_IMPORT`` is set to ``0`` before ``nsight`` is imported:
73+
74+
.. code-block:: python
75+
76+
import os
77+
78+
os.environ["NSPY_NCU_INIT_AT_IMPORT"] = "0"
79+
80+
import torch
81+
82+
import nsight
83+
84+
nsight.activate(nsight.Tool.CUPTI)
85+
86+
87+
@nsight.analyze.kernel
88+
def benchmark_matmul(n):
89+
a = torch.randn(n, n, device="cuda")
90+
b = torch.randn(n, n, device="cuda")
91+
92+
with nsight.annotate("matmul"):
93+
c = a @ b
94+
95+
96+
if __name__ == "__main__":
97+
result = benchmark_matmul(1024)
98+
print(result.to_dataframe())
99+
100+
nsight.deactivate()
101+
102+
Activating CUPTI updates ``NVTX_INJECTION64_PATH``. Any NVTX usage before
103+
``nsight.activate(nsight.Tool.CUPTI)`` will cause NVTX annotation ranges to be
104+
missed and profiling results to be incomplete.
105+
106+
CUPTI may be activated either before or after CUDA is initialized.
107+
108+
Limitations
109+
"""""""""""
110+
111+
**Platform.** Linux only for now.
112+
113+
**Metrics.** Only ``"gpu__time_duration.sum"`` is supported. CUPTI measures it
114+
by recording kernel start and end timestamps from CUPTI activity records
115+
(``CONCURRENT_KERNEL``) and computing ``end - start`` per kernel launch.
116+
117+
**Replay mode.** Neither kernel replay nor range replay is supported. CUPTI
118+
activity records capture one execution of each kernel in a single pass, so no
119+
replay mechanism is engaged and ``replay_mode`` is ignored.
120+
121+
**Clock and cache control.** ``clock_control`` and ``cache_control`` are
122+
ignored. Clocks are not locked and caches are not flushed between runs, which
123+
may affect measurement stability.
124+
125+
**Device synchronization.** The whole device is synchronized once after the
126+
profiling session, before the activity buffers are flushed, because a kernel's
127+
activity record is only written when the kernel completes. Work that the
128+
profiled function left running asynchronously is therefore waited for before
129+
profiling returns.

‎nsight/analyze.py‎

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -109,22 +109,6 @@ def kernel(
109109
"""
110110
A decorator that collects profiling data from GPU kernels.
111111
112-
**Tool selection**
113-
114-
By default, ``@nsight.analyze.kernel`` profiles using NVIDIA Nsight Compute
115-
(NCU). NCU's injection library is loaded automatically on the first call to
116-
a decorated function, but it **must** be loaded before CUDA is initialized.
117-
This means the first decorated function call must occur before any CUDA-initializing code.
118-
To guarantee early loading and avoid this constraint, call ``nsight.activate(nsight.Tool.NCU)``
119-
explicitly at the very top of your script, before any CUDA imports.
120-
121-
CUPTI is also supported *(experimental)* for lightweight collection of
122-
``"gpu__time_duration.sum"`` using kernel timestamps, without launching
123-
``ncu``. To enable it, call ``nsight.activate(nsight.Tool.CUPTI)`` before
124-
profiling. CUPTI does not have the CUDA initialization ordering constraint.
125-
See :class:`~nsight.collection.cupti.CUPTICollector` for supported metrics
126-
and limitations.
127-
128112
Can be used with or without parentheses:
129113
- ``@nsight.analyze.kernel`` (no parentheses)
130114
- ``@nsight.analyze.kernel()`` (empty parentheses)
@@ -155,6 +139,14 @@ def wrapped_function(*args, configs=None, **kwargs) -> ProfileResults
155139
but will always be empty during profiling. Parameters with default values
156140
may be omitted from configs — missing values will be filled from the defaults.
157141
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.
158150
159151
Parameters:
160152
configs: An iterable of configurations to run the function with. Each configuration can be either:
@@ -218,20 +210,26 @@ def wrapped_function(*args, configs=None, **kwargs) -> ProfileResults
218210
Default: ``"none"``
219211
220212
Refer the `NVIDIA Nsight Compute documentation on Clock Control <https://docs.nvidia.com/nsight-compute/ProfilingGuide/index.html#clock-control>`_ for more details.
213+
214+
Ignored when the CUPTI backend is active.
221215
cache_control: Control the behavior of the GPU caches during profiling. Allowed values:
222216
223217
- ``"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.
224218
- ``"none"``: No GPU caches are flushed during profiling. This can improve performance and better replicates the application behavior if only a single kernel replay pass is necessary for metric collection. However, some metric results will vary depending on prior GPU work, and between replay iterations. This can lead to inconsistent and out-of-bounds metric values.
225219
226220
Default: ``"all"``
227221
222+
Ignored when the CUPTI backend is active.
223+
228224
replay_mode: Mechanism used for replaying a kernel launch multiple times to collect selected metrics. Allowed values:
229225
230226
- ``"kernel"``: Replay individual kernel launches during the execution of the application.
231227
- ``"range"``: Replay range of kernel launches during the execution of the application. Ranges are defined using nsight.annotate.
232228
233229
Default: ``"kernel"``
234230
231+
Ignored when the CUPTI backend is active.
232+
235233
thermal_mode: Controls GPU thermal management mode. Default: ``"auto"``
236234
237235
Monitors GPU thermal headroom (T.Limit) and pauses profiling when GPU gets

‎nsight/collection/cupti.py‎

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -214,28 +214,9 @@ class CUPTICollector(core.NsightCollector):
214214
Experimental CUPTI-based collector for Nsight Python.
215215
216216
Uses CUPTI activity records to measure kernel durations directly, without
217-
launching ``ncu``. This makes it lower-overhead than the NCU collector but
218-
comes with the following limitations:
219-
220-
**Supported metrics:** Only ``"gpu__time_duration.sum"`` is supported. CUPTI
221-
measures this by recording kernel start and end timestamps from CUPTI activity
222-
records (``CONCURRENT_KERNEL``), computing ``end - start`` per kernel launch.
223-
224-
**Replay mode:** Neither kernel replay nor range replay is supported. CUPTI
225-
activity records capture one execution of each kernel in a single pass; no
226-
replay mechanism is engaged.
227-
228-
**Clock and cache control:** ``clock_control`` and ``cache_control`` are not
229-
supported. Clocks are not locked and caches are not flushed between runs,
230-
which may affect measurement stability. Use :attr:`ProfileSettings.runs` with
231-
a sufficient number of repetitions and check ``StableMeasurement`` in the
232-
output to detect high variance.
233-
234-
.. note::
235-
The whole device is synchronized once after the profiling session, before
236-
the activity buffers are flushed, because a kernel's activity record is
237-
only written when the kernel completes. Work the profiled function left
238-
running asynchronously is therefore waited for before profiling returns.
217+
launching ``ncu``. This makes it lower-overhead than the NCU collector, at
218+
the cost of supporting fewer metrics and settings. See the :doc:`/tools`
219+
page for the supported metrics and limitations.
239220
240221
Args:
241222
metrics: Must be ``["gpu__time_duration.sum"]``.

‎nsight/thermovision.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,15 +210,19 @@ def throttle_guard(self) -> None:
210210
Thermal headroom = temperature margin before GPU starts throttling.
211211
212212
Operates in two modes:
213+
213214
- Auto mode: Automatically adjusts thermal_cont based on workload
214215
- Manual mode: Uses user-provided thresholds without adaptation
215216
216217
Adaptive Algorithm (in auto mode):
218+
217219
1. When thermal headroom reaches thermal_cont after cooling, start counting iterations
218220
2. Run kernel as headroom drops from thermal_cont toward thermal_wait
219221
3. When headroom drops below thermal_wait, analyze iteration count:
222+
220223
- Few iterations (<TARGET_MIN_ITERATIONS): GPU heats quickly → increase thermal_cont (cool more)
221224
- Many iterations (>TARGET_MAX_ITERATIONS): GPU heats slowly → decrease thermal_cont (cool less)
225+
222226
4. Wait until GPU cools back to thermal_cont, then repeat
223227
"""
224228
self._refresh_device_for_current_cuda_context()

‎nsight/tools_manager.py‎

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,30 +20,22 @@ class Tool(str, enum.Enum):
2020
def activate(tool: Tool) -> None:
2121
"""Select and load ``tool`` as the active tool for this process.
2222
23-
Only one tool is active at a time. The first call loads the tool; calling
23+
Only one tool can be active at a time. The first call loads the tool; calling
2424
again with the *same* tool is a no-op. Calling with a *different* tool while
2525
one is already active raises, because the tools are mutually exclusive.
2626
27-
For :attr:`Tool.NCU` in particular, this **must** be called before CUDA is
28-
initialized otherwise the NVIDIA Nsight Compute injection cannot attach.
27+
Each tool has its own prerequisites and its own constraints on when it may
28+
be activated. See the :doc:`/tools` page for the tool you are selecting.
2929
3030
Args:
31-
tool: The tool to activate. See :class:`Tool`.
31+
tool: The tool to activate.
3232
3333
Raises:
3434
ValueError: If ``tool`` is not a :class:`Tool`.
3535
RuntimeError: If a different, incompatible tool is already active.
36-
NCUNotAvailableError: If :attr:`Tool.NCU` was selected but the ``ncu``
37-
CLI is not on ``$PATH``.
3836
ProfilerException: If the selected tool fails to load (wrong version,
39-
injection library missing, etc.).
40-
41-
Example:
42-
>>> import nsight
43-
>>> nsight.activate(nsight.Tool.NCU) # before any CUDA use
44-
>>> @nsight.analyze.kernel
45-
... def matmul(n: int) -> None:
46-
... ...
37+
injection library missing, etc.). A tool whose prerequisites are
38+
missing may raise its own exception type instead.
4739
"""
4840
global _active_tool
4941
if not isinstance(tool, Tool):
@@ -70,10 +62,11 @@ def deactivate() -> None:
7062
Stops and tears down the tool :func:`activate` last activated. It is a
7163
no-op if no tool is active.
7264
73-
Not every tool can be torn down. NVIDIA Nsight Compute cannot: it stays
74-
loaded for the lifetime of the process, so it also stays the active tool and
75-
this call is a no-op that warns rather than clearing the selection. A
76-
different tool therefore cannot be activated afterwards.
65+
Not every tool can be torn down. A tool that cannot be stopped stays loaded
66+
for the lifetime of the process, so it also stays the active tool and this
67+
call is a no-op that warns rather than clearing the selection; no other tool
68+
can be activated afterwards. See the :doc:`/tools` page for which tools this
69+
applies to.
7770
7871
Raises:
7972
ProfilerException: If tearing down the active tool fails.

0 commit comments

Comments
 (0)