Skip to content

Commit 02d785c

Browse files
committed
Split out nvtx range impl according to owner's review
Pros: clarity, mutex free cons: similar code duplication, double hooks from nvtx::push_range_name()
1 parent b063ec8 commit 02d785c

5 files changed

Lines changed: 127 additions & 104 deletions

File tree

cpp/include/raft/core/detail/nvtx.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/*
2-
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION.
2+
* SPDX-FileCopyrightText: Copyright (c) 2021-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
#pragma once
77

88
#include <raft/core/detail/macros.hpp>
9+
#include <raft/core/detail/nvtx_range_path_stack.hpp>
910
#include <raft/core/detail/nvtx_range_stack.hpp>
1011

1112
#include <rmm/cuda_stream_view.hpp>
@@ -150,7 +151,8 @@ inline void push_range_name(const char* name)
150151
event_attrib.messageType = NVTX_MESSAGE_TYPE_ASCII;
151152
event_attrib.message.ascii = name;
152153
nvtxDomainRangePushEx(domain_store<Domain>::value(), &event_attrib);
153-
detail::range_name_stack_instance.push(name);
154+
detail::range_name_stack_instance.push(name); // tracks inner range and depth, cross-thread
155+
detail::full_range_stack_instance.push(name); // tracks full range stack, thread-local
154156
}
155157

156158
template <typename Domain, typename... Args>
@@ -174,6 +176,7 @@ template <typename Domain>
174176
inline void pop_range()
175177
{
176178
detail::range_name_stack_instance.pop();
179+
detail::full_range_stack_instance.pop();
177180
nvtxDomainRangePop(domain_store<Domain>::value());
178181
}
179182

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
#pragma once
6+
7+
#include <raft/core/detail/macros.hpp>
8+
9+
#include <atomic>
10+
#include <cstddef>
11+
#include <cstdint>
12+
#include <string>
13+
#include <utility>
14+
#include <vector>
15+
16+
namespace raft {
17+
namespace common::nvtx {
18+
19+
namespace detail {
20+
21+
/**
22+
* Process-wide counter producing a unique id to every pushed range, so that
23+
* two nvtx ranges sharing the same name can differentiate.
24+
*
25+
* Motivation: To sample different allocation stats over different runs of the
26+
* same block of code. User can aggregate them into a distribution of allocations
27+
* over multiple runs.
28+
*/
29+
RAFT_EXPORT inline std::atomic<std::uint64_t> range_instance_counter{0};
30+
31+
/**
32+
* @brief Per-thread NVTX range stack that records the full range path with
33+
* unique range instance ids.
34+
*/
35+
struct nvtx_full_range_stack {
36+
void push(const char* name)
37+
{
38+
auto id = range_instance_counter.fetch_add(1, std::memory_order_relaxed) + 1;
39+
stack_.emplace_back(id, name ? name : "");
40+
}
41+
42+
void pop()
43+
{
44+
if (!stack_.empty()) { stack_.pop_back(); }
45+
}
46+
47+
/** Innermost range name and stack depth (empty/0 when no range is active). */
48+
[[nodiscard]] auto inner_range_and_depth() const noexcept -> std::pair<std::string, std::size_t>
49+
{
50+
if (stack_.empty()) { return {"", 0}; }
51+
return {stack_.back().second, stack_.size()};
52+
}
53+
54+
/** Full range path "name#id -> name#id -> ..." (empty when no range is active). */
55+
[[nodiscard]] auto current_path() const -> std::string
56+
{
57+
std::string path;
58+
for (auto const& [id, name] : stack_) {
59+
if (!path.empty()) { path += " -> "; }
60+
path += name + '#' + std::to_string(id);
61+
}
62+
return path;
63+
}
64+
65+
private:
66+
// (instance id, range name), outer -> inner (top).
67+
std::vector<std::pair<std::uint64_t, std::string>> stack_{};
68+
};
69+
70+
RAFT_EXPORT inline thread_local nvtx_full_range_stack full_range_stack_instance{};
71+
72+
} // namespace detail
73+
74+
/**
75+
* Mutex-free read of the current thread's innermost NVTX range name and stack depth.
76+
*
77+
* ONLY safe to call from the thread that owns this range stack (the current thread).
78+
*/
79+
RAFT_EXPORT inline auto thread_local_inner_range_and_depth() -> std::pair<std::string, std::size_t>
80+
{
81+
return detail::full_range_stack_instance.inner_range_and_depth();
82+
}
83+
84+
/**
85+
* Mutex-free read of the current thread's full NVTX range path "name#id -> name#id -> ...".
86+
*
87+
* ONLY safe to call from the thread that owns this range stack (the current thread).
88+
*/
89+
RAFT_EXPORT inline auto thread_local_nvtx_full_path() -> std::string
90+
{
91+
return detail::full_range_stack_instance.current_path();
92+
}
93+
94+
} // namespace common::nvtx
95+
} // namespace raft

cpp/include/raft/core/detail/nvtx_range_stack.hpp

Lines changed: 9 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,12 @@
66

77
#include <raft/core/detail/macros.hpp>
88

9-
#include <atomic>
109
#include <cstddef>
11-
#include <cstdint>
1210
#include <memory>
1311
#include <mutex>
12+
#include <stack>
1413
#include <string>
1514
#include <utility>
16-
#include <vector>
1715

1816
namespace raft {
1917
namespace common::nvtx {
@@ -37,16 +35,6 @@ class current_range {
3735
return {value_, depth_};
3836
}
3937

40-
/**
41-
* Read the full nvtx range path with instance ids, formatted as
42-
* "name#id > name#id > ..." (empty when no range is active).
43-
*/
44-
auto get_path() const -> std::string
45-
{
46-
std::lock_guard lock(mu_);
47-
return path_;
48-
}
49-
5038
operator std::string() const
5139
{
5240
std::lock_guard lock(mu_);
@@ -57,81 +45,35 @@ class current_range {
5745
mutable std::mutex mu_;
5846
std::string value_;
5947
std::size_t depth_{0};
60-
std::string path_;
6148

62-
void set(const char* name, std::size_t depth, std::string path)
49+
void set(const char* name, std::size_t depth)
6350
{
6451
std::lock_guard lock(mu_);
6552
value_ = name ? name : "";
6653
depth_ = depth;
67-
path_ = std::move(path);
6854
}
6955
};
7056

7157
namespace detail {
7258

73-
RAFT_EXPORT inline std::atomic<std::uint64_t> range_instance_counter{0};
74-
7559
struct nvtx_range_name_stack {
7660
void push(const char* name)
7761
{
78-
ensure_current();
79-
auto id = range_instance_counter.fetch_add(1, std::memory_order_relaxed) + 1;
80-
stack_.emplace_back(id, name ? name : "");
81-
current_->set(stack_.back().second.c_str(), stack_.size(), build_path());
62+
stack_.emplace(name);
63+
current_->set(name, stack_.size());
8264
}
8365

8466
void pop()
8567
{
86-
ensure_current();
87-
if (!stack_.empty()) { stack_.pop_back(); }
88-
current_->set(
89-
stack_.empty() ? nullptr : stack_.back().second.c_str(), stack_.size(), build_path());
90-
}
91-
92-
[[nodiscard]] auto current() const -> std::shared_ptr<const current_range>
93-
{
94-
ensure_current();
95-
return current_;
96-
}
97-
98-
/**
99-
* Innermost NVTX range name and stack depth.
100-
* Mutex-free — ONLY safe to call from the thread that owns this stack.
101-
*/
102-
[[nodiscard]] auto current_name_and_depth() const noexcept -> std::pair<std::string, std::size_t>
103-
{
104-
if (stack_.empty()) { return {"", 0}; }
105-
return {stack_.back().second, stack_.size()};
68+
if (!stack_.empty()) { stack_.pop(); }
69+
current_->set(stack_.empty() ? nullptr : stack_.top().c_str(), stack_.size());
10670
}
10771

108-
/**
109-
* Full NVTX range path "name#id > name#id > ...".
110-
* Mutex-free — ONLY safe to call from the thread that owns this stack.
111-
*/
112-
[[nodiscard]] auto current_path() const -> std::string { return build_path(); }
72+
auto current() const -> std::shared_ptr<const current_range> { return current_; }
11373

11474
private:
115-
void ensure_current() const
116-
{
117-
if (!current_) { current_ = std::make_shared<current_range>(); }
118-
}
119-
120-
// Serialize the active stack as "name#id > name#id > ..." (outer -> inner).
121-
[[nodiscard]] auto build_path() const -> std::string
122-
{
123-
std::string path;
124-
for (auto const& [id, name] : stack_) {
125-
if (!path.empty()) { path += " > "; }
126-
path += name;
127-
path += '#';
128-
path += std::to_string(id);
129-
}
130-
return path;
131-
}
132-
133-
std::vector<std::pair<std::uint64_t, std::string>> stack_{};
134-
mutable std::shared_ptr<current_range> current_{std::make_shared<current_range>()};
75+
std::stack<std::string> stack_{};
76+
std::shared_ptr<current_range> current_{std::make_shared<current_range>()};
13577
};
13678

13779
RAFT_EXPORT inline thread_local nvtx_range_name_stack range_name_stack_instance{};
@@ -148,25 +90,5 @@ RAFT_EXPORT inline auto thread_local_current_range() -> std::shared_ptr<const cu
14890
return detail::range_name_stack_instance.current();
14991
}
15092

151-
/**
152-
* Mutex-free read of the current thread's innermost NVTX range name and stack depth.
153-
* ONLY safe to call from the thread that owns this range stack (the current thread).
154-
* Use instead of thread_local_current_range()->get() when no cross-thread sharing is needed.
155-
*/
156-
RAFT_EXPORT inline auto thread_local_current_name_and_depth() -> std::pair<std::string, std::size_t>
157-
{
158-
return detail::range_name_stack_instance.current_name_and_depth();
159-
}
160-
161-
/**
162-
* Mutex-free read of the current thread's full NVTX range path "name#id > name#id > ...".
163-
* ONLY safe to call from the thread that owns this range stack (the current thread).
164-
* Use instead of thread_local_current_range()->get_path() when no cross-thread sharing is needed.
165-
*/
166-
RAFT_EXPORT inline auto thread_local_current_path() -> std::string
167-
{
168-
return detail::range_name_stack_instance.current_path();
169-
}
170-
17193
} // namespace common::nvtx
17294
} // namespace raft

cpp/include/raft/core/memory_logging_resources.hpp

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,51 +138,54 @@ class memory_logging_resources : public resources {
138138

139139
// --- Host (global) ---
140140
{
141-
int id = recorder_->register_source("host");
142-
host_adaptor_ = std::make_unique<host_record_t>(old_host_, queue, id);
141+
int source_id = recorder_->register_source("host");
142+
host_adaptor_ = std::make_unique<host_record_t>(old_host_, queue, source_id);
143143
raft::mr::set_default_host_resource(*host_adaptor_);
144144
}
145145

146146
// --- Pinned ---
147147
{
148-
int id = recorder_->register_source("pinned");
148+
int source_id = recorder_->register_source("pinned");
149149
raft::resource::set_pinned_memory_resource(
150150
*this,
151-
raft::mr::recording_adaptor<raft::mr::host_device_resource_ref>{pinned_ref, queue, id});
151+
raft::mr::recording_adaptor<raft::mr::host_device_resource_ref>{
152+
pinned_ref, queue, source_id});
152153
}
153154

154155
// --- Managed ---
155156
{
156-
int id = recorder_->register_source("managed");
157+
int source_id = recorder_->register_source("managed");
157158
raft::resource::set_managed_memory_resource(
158159
*this,
159-
raft::mr::recording_adaptor<raft::mr::host_device_resource_ref>{managed_ref, queue, id});
160+
raft::mr::recording_adaptor<raft::mr::host_device_resource_ref>{
161+
managed_ref, queue, source_id});
160162
}
161163

162164
// --- Device (global) ---
163165
{
164166
// Invalidate the cached thrust policy — its resource_ref will be stale
165167
// once we replace the global device resource.
166168
cells_[resource::resource_type::THRUST_POLICY] = std::make_shared<resource::resource_cell>();
167-
int id = recorder_->register_source("device");
168-
device_adaptor_ = std::make_unique<device_record_t>(old_device_, queue, id);
169+
int source_id = recorder_->register_source("device");
170+
device_adaptor_ = std::make_unique<device_record_t>(old_device_, queue, source_id);
169171
rmm::mr::set_current_device_resource(*device_adaptor_);
170172
}
171173

172174
// --- Workspace (track upstream to preserve limiting_resource_adaptor) ---
173175
{
174-
int id = recorder_->register_source("workspace");
176+
int source_id = recorder_->register_source("workspace");
175177
raft::resource::set_workspace_resource(
176178
*this,
177-
raft::mr::recording_adaptor<rmm::device_async_resource_ref>{upstream_ref, queue, id},
179+
raft::mr::recording_adaptor<rmm::device_async_resource_ref>{upstream_ref, queue, source_id},
178180
ws_free);
179181
}
180182

181183
// --- Large workspace ---
182184
{
183-
int id = recorder_->register_source("large_workspace");
185+
int source_id = recorder_->register_source("large_workspace");
184186
raft::resource::set_large_workspace_resource(
185-
*this, raft::mr::recording_adaptor<rmm::device_async_resource_ref>{lws_ref, queue, id});
187+
*this,
188+
raft::mr::recording_adaptor<rmm::device_async_resource_ref>{lws_ref, queue, source_id});
186189
}
187190

188191
recorder_->start();

cpp/include/raft/mr/recording_adaptor.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#pragma once
66

77
#include <raft/core/detail/macros.hpp>
8-
#include <raft/core/detail/nvtx_range_stack.hpp> // thread_local_current_path, thread_local_current_name_and_depth
8+
#include <raft/core/detail/nvtx_range_path_stack.hpp> // thread_local_nvtx_full_path, thread_local_inner_range_and_depth
99
#include <raft/mr/recording_monitor.hpp> // allocation_event, allocation_event_queue
1010
#include <raft/mr/statistics_adaptor.hpp> // resource_stats (atomic counters, reused)
1111

@@ -53,7 +53,7 @@ class recording_adaptor : public cuda::forward_property<recording_adaptor<Upstre
5353
{
5454
std::string path = "";
5555
if (ptr != nullptr) {
56-
path = raft::common::nvtx::thread_local_current_path();
56+
path = raft::common::nvtx::thread_local_nvtx_full_path();
5757
if (!path.empty()) {
5858
std::lock_guard<std::mutex> lock(alloc_map_->mtx);
5959
alloc_map_->paths[ptr] = path;
@@ -78,7 +78,7 @@ class recording_adaptor : public cuda::forward_property<recording_adaptor<Upstre
7878
// Enqueue an event. Called on the allocating/deallocating thread — mutex-free NVTX read.
7979
void emit(std::string nvtx_full_range, std::int64_t signed_bytes) noexcept
8080
{
81-
auto [name, depth] = raft::common::nvtx::thread_local_current_name_and_depth();
81+
auto [name, depth] = raft::common::nvtx::thread_local_inner_range_and_depth();
8282
allocation_event event;
8383
event.source_id = source_id_;
8484
event.current = stats_->bytes_current.load(std::memory_order_relaxed);

0 commit comments

Comments
 (0)