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 [`cudarc::nvtx`] for the actual NVTX calls
7//!
8//! # Gating (two-level)
9//!
10//! | Cargo feature `nvtx` | `DYN_ENABLE_RUST_NVTX` env | Effect                                    |
11//! |----------------------|----------------------------|-------------------------------------------|
12//! | off (default)        | any                        | macros compile to nothing; zero overhead  |
13//! | on                   | unset                      | one `Relaxed` load per site (~1 ns)       |
14//! | on                   | `1` / `true` / `yes`       | cudarc NVTX calls (~50 ns/annotation)     |
15//!
16//! # Usage
17//!
18//! ```rust,ignore
19//! let _r = dynamo_nvtx_range!("preprocess.tokenize"); // RAII — pops at scope end
20//! dynamo_nvtx_push!("codec.encode");
21//! dynamo_nvtx_pop!();
22//! dynamo_nvtx_name_thread!("tokio-worker-0");
23//! ```
24//!
25//! # Build
26//!
27//! ```bash
28//! cargo build --profile profiling --features nvtx
29//! ```
30//! Requires `libnvToolsExt.so` at runtime (CUDA Toolkit or NVHPC).
31
32#[cfg(feature = "nvtx")]
33use std::sync::atomic::{AtomicBool, Ordering};
34
35#[cfg(feature = "nvtx")]
36static NVTX_ENABLED: AtomicBool = AtomicBool::new(false);
37
38// ── Public API ───────────────────────────────────────────────────────────────
39
40/// Initialise the NVTX subsystem from the `DYN_ENABLE_RUST_NVTX` environment variable.
41/// Must be called once at runtime startup before any annotation macros fire.
42/// No-op when the `nvtx` Cargo feature is off.
43pub fn init() {
44    #[cfg(feature = "nvtx")]
45    {
46        let enabled = crate::config::env_is_truthy("DYN_ENABLE_RUST_NVTX");
47        NVTX_ENABLED.store(enabled, Ordering::Relaxed);
48        if enabled {
49            tracing::info!("NVTX annotations enabled (DYN_ENABLE_RUST_NVTX)");
50        }
51    }
52}
53
54/// Returns `true` when the `nvtx` feature is compiled in **and** `DYN_ENABLE_RUST_NVTX` is set.
55#[inline(always)]
56pub fn enabled() -> bool {
57    #[cfg(feature = "nvtx")]
58    {
59        return NVTX_ENABLED.load(Ordering::Relaxed);
60    }
61    #[allow(unreachable_code)]
62    false
63}
64
65/// Push an NVTX range onto the calling thread's stack.
66/// No-op (compiled out) when the `nvtx` feature is off.
67#[inline(always)]
68pub fn push_impl(name: &str) {
69    #[cfg(feature = "nvtx")]
70    {
71        if NVTX_ENABLED.load(Ordering::Relaxed) {
72            cudarc::nvtx::result::range_push(name);
73        }
74    }
75    let _ = name;
76}
77
78/// Pop the innermost NVTX range from the calling thread's stack.
79/// No-op (compiled out) when the `nvtx` feature is off.
80#[inline(always)]
81pub fn pop_impl() {
82    #[cfg(feature = "nvtx")]
83    {
84        if NVTX_ENABLED.load(Ordering::Relaxed) {
85            cudarc::nvtx::result::range_pop();
86        }
87    }
88}
89
90/// Name the current OS thread in the Nsight Systems timeline.
91/// No-op (compiled out) when the `nvtx` feature is off.
92#[inline(always)]
93pub fn name_current_thread_impl(name: &str) {
94    #[cfg(feature = "nvtx")]
95    {
96        if NVTX_ENABLED.load(Ordering::Relaxed) {
97            #[cfg(target_os = "linux")]
98            let tid = unsafe { libc::syscall(libc::SYS_gettid) as u32 };
99            #[cfg(not(target_os = "linux"))]
100            let tid = 0u32;
101            cudarc::nvtx::result::name_os_thread(tid, name);
102        }
103    }
104    let _ = name;
105}
106
107// ── RAII guard ───────────────────────────────────────────────────────────────
108
109/// RAII guard that pops an NVTX range when dropped.
110/// Construct with [`dynamo_nvtx_range!`].
111#[cfg(feature = "nvtx")]
112pub struct NvtxRangeGuard {
113    active: bool,
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 active = NVTX_ENABLED.load(Ordering::Relaxed);
126            if active {
127                cudarc::nvtx::result::range_push(name);
128            }
129            return NvtxRangeGuard { active };
130        }
131        #[cfg(not(feature = "nvtx"))]
132        {
133            let _ = name;
134            NvtxRangeGuard {}
135        }
136    }
137}
138
139#[cfg(feature = "nvtx")]
140impl Drop for NvtxRangeGuard {
141    fn drop(&mut self) {
142        if self.active {
143            cudarc::nvtx::result::range_pop();
144        }
145    }
146}
147
148#[cfg(not(feature = "nvtx"))]
149impl Drop for NvtxRangeGuard {
150    fn drop(&mut self) {}
151}
152
153// ── Macros ───────────────────────────────────────────────────────────────────
154
155/// Push a named NVTX range onto the calling thread's stack.
156/// Zero-cost when the `nvtx` Cargo feature is off.
157#[macro_export]
158macro_rules! dynamo_nvtx_push {
159    ($name:expr) => {
160        $crate::nvtx::push_impl($name)
161    };
162}
163
164/// Pop the innermost NVTX range from the calling thread's stack.
165/// Zero-cost when the `nvtx` Cargo feature is off.
166#[macro_export]
167macro_rules! dynamo_nvtx_pop {
168    () => {
169        $crate::nvtx::pop_impl()
170    };
171}
172
173/// Open a named NVTX range that closes automatically at end of scope.
174///
175/// ```rust,ignore
176/// let _r = dynamo_nvtx_range!("preprocess.tokenize");
177/// // range closes here
178/// ```
179/// Zero-cost when the `nvtx` Cargo feature is off.
180#[macro_export]
181macro_rules! dynamo_nvtx_range {
182    ($name:expr) => {
183        $crate::nvtx::NvtxRangeGuard::new($name)
184    };
185}
186
187/// Annotate the current OS thread in the Nsight Systems timeline.
188/// Zero-cost when the `nvtx` Cargo feature is off.
189#[macro_export]
190macro_rules! dynamo_nvtx_name_thread {
191    ($name:expr) => {
192        $crate::nvtx::name_current_thread_impl($name)
193    };
194}