Thread System 0.3.1
High-performance C++20 thread pool with work stealing and DAG scheduling
Loading...
Searching...
No Matches
work_stealing_stats.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
12#pragma once
13
14#include <atomic>
15#include <cstdint>
16
17namespace kcenon::thread
18{
19
28{
29 std::uint64_t steal_attempts{0};
30 std::uint64_t successful_steals{0};
31 std::uint64_t failed_steals{0};
32 std::uint64_t jobs_stolen{0};
33 std::uint64_t same_node_steals{0};
34 std::uint64_t cross_node_steals{0};
35 std::uint64_t batch_steals{0};
36 std::uint64_t total_batch_size{0};
37 std::uint64_t total_steal_time_ns{0};
38 std::uint64_t total_backoff_time_ns{0};
39
40 [[nodiscard]] auto steal_success_rate() const -> double
41 {
42 if (steal_attempts == 0)
43 {
44 return 0.0;
45 }
46 return static_cast<double>(successful_steals) / static_cast<double>(steal_attempts);
47 }
48
49 [[nodiscard]] auto avg_batch_size() const -> double
50 {
51 if (batch_steals == 0)
52 {
53 return 0.0;
54 }
55 return static_cast<double>(total_batch_size) / static_cast<double>(batch_steals);
56 }
57
58 [[nodiscard]] auto cross_node_ratio() const -> double
59 {
61 if (total == 0)
62 {
63 return 0.0;
64 }
65 return static_cast<double>(cross_node_steals) / static_cast<double>(total);
66 }
67
68 [[nodiscard]] auto avg_steal_time_ns() const -> double
69 {
70 if (steal_attempts == 0)
71 {
72 return 0.0;
73 }
74 return static_cast<double>(total_steal_time_ns) / static_cast<double>(steal_attempts);
75 }
76};
77
111{
112 // ========================================================================
113 // Steal Counts
114 // ========================================================================
115
117 std::atomic<std::uint64_t> steal_attempts{0};
118
120 std::atomic<std::uint64_t> successful_steals{0};
121
123 std::atomic<std::uint64_t> failed_steals{0};
124
126 std::atomic<std::uint64_t> jobs_stolen{0};
127
128 // ========================================================================
129 // NUMA Statistics
130 // ========================================================================
131
133 std::atomic<std::uint64_t> same_node_steals{0};
134
136 std::atomic<std::uint64_t> cross_node_steals{0};
137
138 // ========================================================================
139 // Batch Statistics
140 // ========================================================================
141
143 std::atomic<std::uint64_t> batch_steals{0};
144
146 std::atomic<std::uint64_t> total_batch_size{0};
147
148 // ========================================================================
149 // Timing Statistics
150 // ========================================================================
151
153 std::atomic<std::uint64_t> total_steal_time_ns{0};
154
156 std::atomic<std::uint64_t> total_backoff_time_ns{0};
157
158 // ========================================================================
159 // Computed Metrics
160 // ========================================================================
161
168 [[nodiscard]] auto steal_success_rate() const -> double
169 {
170 auto attempts = steal_attempts.load(std::memory_order_relaxed);
171 if (attempts == 0)
172 {
173 return 0.0;
174 }
175 auto successes = successful_steals.load(std::memory_order_relaxed);
176 return static_cast<double>(successes) / static_cast<double>(attempts);
177 }
178
185 [[nodiscard]] auto avg_batch_size() const -> double
186 {
187 auto batches = batch_steals.load(std::memory_order_relaxed);
188 if (batches == 0)
189 {
190 return 0.0;
191 }
192 auto total = total_batch_size.load(std::memory_order_relaxed);
193 return static_cast<double>(total) / static_cast<double>(batches);
194 }
195
203 [[nodiscard]] auto cross_node_ratio() const -> double
204 {
205 auto same = same_node_steals.load(std::memory_order_relaxed);
206 auto cross = cross_node_steals.load(std::memory_order_relaxed);
207 auto total = same + cross;
208 if (total == 0)
209 {
210 return 0.0;
211 }
212 return static_cast<double>(cross) / static_cast<double>(total);
213 }
214
221 [[nodiscard]] auto avg_steal_time_ns() const -> double
222 {
223 auto attempts = steal_attempts.load(std::memory_order_relaxed);
224 if (attempts == 0)
225 {
226 return 0.0;
227 }
228 auto total_time = total_steal_time_ns.load(std::memory_order_relaxed);
229 return static_cast<double>(total_time) / static_cast<double>(attempts);
230 }
231
241 void reset()
242 {
243 steal_attempts.store(0, std::memory_order_relaxed);
244 successful_steals.store(0, std::memory_order_relaxed);
245 failed_steals.store(0, std::memory_order_relaxed);
246 jobs_stolen.store(0, std::memory_order_relaxed);
247 same_node_steals.store(0, std::memory_order_relaxed);
248 cross_node_steals.store(0, std::memory_order_relaxed);
249 batch_steals.store(0, std::memory_order_relaxed);
250 total_batch_size.store(0, std::memory_order_relaxed);
251 total_steal_time_ns.store(0, std::memory_order_relaxed);
252 total_backoff_time_ns.store(0, std::memory_order_relaxed);
253 }
254
262 [[nodiscard]] auto snapshot() const -> work_stealing_stats_snapshot
263 {
265 snap.steal_attempts = steal_attempts.load(std::memory_order_acquire);
266 snap.successful_steals = successful_steals.load(std::memory_order_acquire);
267 snap.failed_steals = failed_steals.load(std::memory_order_acquire);
268 snap.jobs_stolen = jobs_stolen.load(std::memory_order_acquire);
269 snap.same_node_steals = same_node_steals.load(std::memory_order_acquire);
270 snap.cross_node_steals = cross_node_steals.load(std::memory_order_acquire);
271 snap.batch_steals = batch_steals.load(std::memory_order_acquire);
272 snap.total_batch_size = total_batch_size.load(std::memory_order_acquire);
273 snap.total_steal_time_ns = total_steal_time_ns.load(std::memory_order_acquire);
274 snap.total_backoff_time_ns = total_backoff_time_ns.load(std::memory_order_acquire);
275 return snap;
276 }
277};
278
279} // namespace kcenon::thread
Core threading foundation of the thread system library.
Definition thread_impl.h:17
Non-atomic snapshot of work-stealing statistics.
Statistics for work-stealing operations.
std::atomic< std::uint64_t > total_steal_time_ns
Total time spent in steal operations (nanoseconds)
std::atomic< std::uint64_t > total_backoff_time_ns
Total time spent in backoff delays (nanoseconds)
auto avg_batch_size() const -> double
Calculate the average batch size.
std::atomic< std::uint64_t > failed_steals
Number of failed steal operations.
auto snapshot() const -> work_stealing_stats_snapshot
Create a snapshot of current statistics.
std::atomic< std::uint64_t > jobs_stolen
Total number of jobs successfully stolen.
std::atomic< std::uint64_t > total_batch_size
Total size of all batch steals (for averaging)
auto avg_steal_time_ns() const -> double
Calculate average steal operation time.
std::atomic< std::uint64_t > cross_node_steals
Steals from workers on different NUMA nodes.
std::atomic< std::uint64_t > successful_steals
Number of successful steal operations.
std::atomic< std::uint64_t > steal_attempts
Total number of steal attempts.
std::atomic< std::uint64_t > batch_steals
Number of batch steal operations (stealing multiple jobs)
void reset()
Reset all statistics to zero.
std::atomic< std::uint64_t > same_node_steals
Steals from workers on the same NUMA node.
auto cross_node_ratio() const -> double
Calculate the cross-NUMA node steal ratio.
auto steal_success_rate() const -> double
Calculate the steal success rate.