Skip to main content

dynamo_runtime/
nvtx.rs

1// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4//! NVTX timeline-annotation helpers for Nsight Systems profiling.
5//!
6//! Delegates to the `nvtx` crate, which compiles the header-only NVTX3 shim into
7//! the binary. Nsight Systems attaches through its injection library at launch,
8//! so no libnvToolsExt.so is needed at build or run time.
9//!
10//! # Gating (two-level)
11//!
12//! | Cargo feature `nvtx` | `DYN_ENABLE_RUST_NVTX` env | Effect                                    |
13//! |----------------------|----------------------------|-------------------------------------------|
14//! | off (default)        | any                        | macros compile to nothing; zero overhead  |
15//! | on                   | unset                      | one `Relaxed` load per site (~1 ns)       |
16//! | on                   | `1` / `true` / `yes`       | NVTX calls (~50 ns/annotation)            |
17//!
18//! # Usage
19//!
20//! ```rust,ignore
21//! let _r = dynamo_nvtx_range!("preprocess.tokenize"); // RAII — pops at scope end
22//! dynamo_nvtx_push!("codec.encode");
23//! dynamo_nvtx_pop!();
24//! dynamo_nvtx_name_thread!("tokio-worker-0");
25//! ```
26//!
27//! # Build
28//!
29//! ```bash
30//! cargo build --profile profiling --features nvtx
31//! ```
32
33#[cfg(feature = "nvtx")]
34use std::sync::atomic::{AtomicBool, Ordering};
35
36#[cfg(feature = "nvtx")]
37static NVTX_ENABLED: AtomicBool = AtomicBool::new(false);
38
39// ── Public API ───────────────────────────────────────────────────────────────
40
41/// Initialise the NVTX subsystem from the `DYN_ENABLE_RUST_NVTX` environment variable.
42/// Must be called once at runtime startup before any annotation macros fire.
43/// No-op when the `nvtx` Cargo feature is off.
44pub fn init() {
45    #[cfg(feature = "nvtx")]
46    {
47        let enabled = crate::config::env_is_truthy("DYN_ENABLE_RUST_NVTX");
48        NVTX_ENABLED.store(enabled, Ordering::Relaxed);
49        if enabled {
50            tracing::info!("NVTX annotations enabled (DYN_ENABLE_RUST_NVTX)");
51        }
52    }
53}
54
55/// Returns `true` when the `nvtx` feature is compiled in **and** `DYN_ENABLE_RUST_NVTX` is set.
56#[inline(always)]
57pub fn enabled() -> bool {
58    #[cfg(feature = "nvtx")]
59    {
60        return NVTX_ENABLED.load(Ordering::Relaxed);
61    }
62    #[allow(unreachable_code)]
63    false
64}
65
66/// Push an NVTX range onto the calling thread's stack.
67/// No-op (compiled out) when the `nvtx` feature is off.
68#[inline(always)]
69pub fn push_impl(name: &str) {
70    #[cfg(feature = "nvtx")]
71    {
72        if NVTX_ENABLED.load(Ordering::Relaxed) {
73            nvtx::range_push!("{name}");
74        }
75    }
76    let _ = name;
77}
78
79/// Pop the innermost NVTX range from the calling thread's stack.
80/// No-op (compiled out) when the `nvtx` feature is off.
81#[inline(always)]
82pub fn pop_impl() {
83    #[cfg(feature = "nvtx")]
84    {
85        if NVTX_ENABLED.load(Ordering::Relaxed) {
86            nvtx::range_pop!();
87        }
88    }
89}
90
91/// Name the current OS thread in the Nsight Systems timeline.
92/// No-op (compiled out) when the `nvtx` feature is off.
93#[inline(always)]
94pub fn name_current_thread_impl(name: &str) {
95    #[cfg(feature = "nvtx")]
96    {
97        if NVTX_ENABLED.load(Ordering::Relaxed) {
98            nvtx::name_thread!("{name}");
99        }
100    }
101    let _ = name;
102}
103
104// ── RAII guard ───────────────────────────────────────────────────────────────
105
106/// RAII guard that pops an NVTX range when dropped.
107/// Construct with [`dynamo_nvtx_range!`].
108#[cfg(feature = "nvtx")]
109pub struct NvtxRangeGuard {
110    // Process-scoped range id: guards outlive `.await`s, so the drop can run on a
111    // different worker thread than the open, where a thread-local push/pop pair
112    // would close an unrelated range.
113    range: Option<i32>,
114}
115
116/// Zero-sized no-op guard used when the `nvtx` feature is off.
117#[cfg(not(feature = "nvtx"))]
118pub struct NvtxRangeGuard;
119
120impl NvtxRangeGuard {
121    #[doc(hidden)]
122    pub fn new(name: &str) -> Self {
123        #[cfg(feature = "nvtx")]
124        {
125            let range = NVTX_ENABLED
126                .load(Ordering::Relaxed)
127                .then(|| nvtx::range_start!("{name}"));
128            NvtxRangeGuard { range }
129        }
130        #[cfg(not(feature = "nvtx"))]
131        {
132            let _ = name;
133            NvtxRangeGuard {}
134        }
135    }
136}
137
138#[cfg(feature = "nvtx")]
139impl Drop for NvtxRangeGuard {
140    fn drop(&mut self) {
141        if let Some(range) = self.range {
142            nvtx::range_end!(range);
143        }
144    }
145}
146
147#[cfg(not(feature = "nvtx"))]
148impl Drop for NvtxRangeGuard {
149    fn drop(&mut self) {}
150}
151
152// ── Macros ───────────────────────────────────────────────────────────────────
153
154/// Push a named NVTX range onto the calling thread's stack.
155/// Zero-cost when the `nvtx` Cargo feature is off.
156#[macro_export]
157macro_rules! dynamo_nvtx_push {
158    ($name:expr) => {
159        $crate::nvtx::push_impl($name)
160    };
161}
162
163/// Pop the innermost NVTX range from the calling thread's stack.
164/// Zero-cost when the `nvtx` Cargo feature is off.
165#[macro_export]
166macro_rules! dynamo_nvtx_pop {
167    () => {
168        $crate::nvtx::pop_impl()
169    };
170}
171
172/// Open a named NVTX range that closes automatically at end of scope.
173///
174/// ```rust,ignore
175/// let _r = dynamo_nvtx_range!("preprocess.tokenize");
176/// // range closes here
177/// ```
178/// Zero-cost when the `nvtx` Cargo feature is off.
179#[macro_export]
180macro_rules! dynamo_nvtx_range {
181    ($name:expr) => {
182        $crate::nvtx::NvtxRangeGuard::new($name)
183    };
184}
185
186/// Annotate the current OS thread in the Nsight Systems timeline.
187/// Zero-cost when the `nvtx` Cargo feature is off.
188#[macro_export]
189macro_rules! dynamo_nvtx_name_thread {
190    ($name:expr) => {
191        $crate::nvtx::name_current_thread_impl($name)
192    };
193}