guanaqo 1.0.0-alpha.24
Utilities for scientific software
Loading...
Searching...
No Matches
trace.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file
4/// @ingroup trace_core
5/// Tracing logger and macros (ITT, Perfetto, or fallback).
6
7#include <guanaqo/export.h>
10#include <guanaqo/stringify.h>
11
12#include <algorithm>
13#include <span>
14#include <utility>
15
16#if GUANAQO_WITH_ITT
17#include <ittnotify.h>
18#include <iosfwd>
19#else
20#include <atomic>
21#include <chrono>
22#include <cstddef>
23#include <cstdint>
24#include <iomanip>
25#include <ostream>
26#include <thread>
27#endif
28
29namespace guanaqo {
30
31#if GUANAQO_WITH_ITT
32struct TraceLogger {
33 struct Log {
34 friend std::ostream &operator<<(std::ostream &os, Log) { return os; }
35 };
36
37 __itt_domain *domain = __itt_domain_create("guanaqo");
38 static constexpr int64_t max_instance_num = 255;
39 TraceLogger(size_t = 0) {
40 for (int64_t i = 0; i <= max_instance_num; ++i)
41 __itt_id_create(
42 domain,
43 __itt_id_make(nullptr, static_cast<unsigned long long>(i)));
44 }
45
46 TraceLogger(const TraceLogger &) = delete;
47 TraceLogger &operator=(const TraceLogger &) = delete;
48 TraceLogger(TraceLogger &&other) noexcept
49 : domain{std::exchange(other.domain, nullptr)} {}
50 TraceLogger &operator=(TraceLogger &&) = delete;
51 ~TraceLogger() {
52 if (!domain)
53 return;
54 for (int64_t i = 0; i <= max_instance_num; ++i)
55 __itt_id_destroy(
56 domain,
57 __itt_id_make(nullptr, static_cast<unsigned long long>(i)));
58 }
59
60 struct ScopedLog {
61 __itt_domain *domain;
62 ScopedLog(__itt_domain *domain, __itt_string_handle *name,
63 int64_t instance)
64 : domain{domain} {
65 if (!domain)
66 return;
67 instance = std::clamp<int64_t>(instance, 0, max_instance_num);
68 auto id = __itt_id_make(nullptr,
69 static_cast<unsigned long long>(instance));
70 __itt_task_begin(domain, id, __itt_null, name);
71 }
72 ScopedLog(const ScopedLog &) = delete;
73 ScopedLog &operator=(const ScopedLog &) = delete;
74 ScopedLog(ScopedLog &&other) noexcept
75 : domain{std::exchange(other.domain, nullptr)} {}
76 ScopedLog &operator=(ScopedLog &&) = delete;
77 ~ScopedLog() {
78 if (!domain)
79 return;
80 __itt_task_end(domain);
81 }
82 };
83
84 [[nodiscard]] ScopedLog trace(__itt_string_handle *name,
85 int64_t instance) const {
86 return ScopedLog{domain, name, instance};
87 }
88
89 [[nodiscard]] std::span<const Log> get_logs() const { return {}; }
90
91 void reset() {}
92};
93
94GUANAQO_EXPORT TraceLogger &get_trace_logger();
95#define GUANAQO_TRACE_IMPL(var_name, name, instance) \
96 static auto GUANAQO_CAT(var_name, _name) = \
97 __itt_string_handle_create(name); \
98 const auto var_name = ::guanaqo::get_trace_logger().trace( \
99 GUANAQO_CAT(var_name, _name), instance)
100#define GUANAQO_TRACE(name, instance) \
101 GUANAQO_TRACE_IMPL(GUANAQO_CAT(trace_log_, __COUNTER__), name, instance)
102
103#else
104
105/// Class for recording trace logs, used when ITT or Perfetto tracing is not enabled.
106/// @ingroup trace_core
108 struct Log {
109 const char *name = "";
110 int64_t instance{0};
111 std::chrono::nanoseconds start_time{0};
112 std::chrono::nanoseconds duration{0};
113 std::size_t thread_id{0};
114 int64_t flop_count{0};
115
116 friend std::ostream &operator<<(std::ostream &os, const Log &log) {
117 return os << std::quoted(log.name) << ',' << log.instance << ','
118 << log.start_time.count() << ',' << log.duration.count()
119 << ',' << log.thread_id << ',' << log.flop_count;
120 }
121 };
122
123 static std::ostream &write_column_headings(std::ostream &os) {
124 return os << "name" << ',' << "instance" << ',' << "start_time" << ','
125 << "duration" << ',' << "thread_id" << ',' << "flop_count";
126 }
127
128 using clock = std::chrono::steady_clock;
129
130 clock::time_point t0 = clock::now();
131 std::vector<Log> logs;
132 std::atomic_size_t count{0};
133
134 struct ScopedLog {
135 Log *log = nullptr;
136 clock::time_point start_time_point;
137
138 ScopedLog() = default;
141 ScopedLog(const ScopedLog &) = delete;
142 ScopedLog &operator=(const ScopedLog &) = delete;
143 ScopedLog(ScopedLog &&other) noexcept
144 : log{std::exchange(other.log, nullptr)},
145 start_time_point{other.start_time_point} {}
148 if (log)
149 log->duration = clock::now() - start_time_point;
150 }
151 };
152
153 TraceLogger(size_t capacity) { logs.resize(capacity); }
154
155 [[nodiscard]] ScopedLog trace(const char *name, int64_t instance,
156 int64_t flop_count = -1) {
157 size_t index = count.fetch_add(1, std::memory_order_relaxed);
158 if (index >= logs.size())
159 return ScopedLog{nullptr, {}};
160 static constexpr std::hash<std::thread::id> hasher;
161 auto &log = logs[index];
162 auto t1 = clock::now();
163 log.name = name;
164 log.instance = instance;
165 log.flop_count = flop_count;
166 log.start_time = t1 - t0;
167 log.thread_id = hasher(std::this_thread::get_id());
168 return ScopedLog{&log, t1};
169 }
170
171 [[nodiscard]] std::span<const Log> get_logs() const {
172 auto n = std::min(logs.size(), count.load(std::memory_order_relaxed));
173 return std::span{logs}.first(n);
174 }
175
176 void reset() { count.store(0, std::memory_order_relaxed); }
177};
178
179#if GUANAQO_WITH_TRACING
180/// Get a reference to the global trace logger instance.
181/// @note Tracing is thread-safe, but for performance reasons, it may be desirable to use a separate
182/// logger for each thread.
183/// @ingroup trace_core
184GUANAQO_EXPORT TraceLogger &get_trace_logger();
185#endif
186
187#endif
188
189#if GUANAQO_WITH_TRACING && !GUANAQO_WITH_PERFETTO
190#define GUANAQO_TRACE(name, ...) \
191 const auto GUANAQO_CAT(trace_log_, __COUNTER__) = \
192 ::guanaqo::get_trace_logger().trace(name, __VA_ARGS__)
193#define GUANAQO_TRACE_INSTANT(category, name, instance) \
194 do { \
195 ::guanaqo::get_trace_logger().trace(name, __VA_ARGS__)->log = nullptr; \
196 } while (0)
197#define GUANAQO_TRACE_LINALG(name, gflops) GUANAQO_TRACE(name, 0, gflops)
198#define GUANAQO_TRACE_REGION(name, instance) GUANAQO_TRACE(name, instance)
199#define GUANAQO_TRACE_STATIC_STR(s) s
200#endif
201
202#if !GUANAQO_WITH_TRACING && !GUANAQO_WITH_ITT && !GUANAQO_WITH_PERFETTO
203#define GUANAQO_TRACE(...) GUANAQO_NOOP()
204#define GUANAQO_TRACE_INSTANT(...) GUANAQO_NOOP()
205#define GUANAQO_TRACE_LINALG(...) GUANAQO_NOOP()
206#define GUANAQO_TRACE_REGION(...) GUANAQO_NOOP()
207#define GUANAQO_TRACE_STATIC_STR(s) s
208#endif
209
210#if GUANAQO_WITH_ITT
211#define GUANAQO_IF_ITT(...) __VA_ARGS__
212#else
213#define GUANAQO_IF_ITT(...)
214#endif
215
216} // namespace guanaqo
TraceLogger & get_trace_logger()
Get a reference to the global trace logger instance.
Definition trace.cpp:5
Perfetto tracing macros and session helpers.
Token concatenation and argument-counting helpers.
Stringify and token concatenation helpers.
std::chrono::nanoseconds start_time
Definition trace.hpp:111
std::chrono::nanoseconds duration
Definition trace.hpp:112
friend std::ostream & operator<<(std::ostream &os, const Log &log)
Definition trace.hpp:116
ScopedLog(Log *log, clock::time_point start_time_point)
Definition trace.hpp:139
ScopedLog(const ScopedLog &)=delete
ScopedLog & operator=(ScopedLog &&)=delete
ScopedLog(ScopedLog &&other) noexcept
Definition trace.hpp:143
clock::time_point start_time_point
Definition trace.hpp:136
ScopedLog & operator=(const ScopedLog &)=delete
Class for recording trace logs, used when ITT or Perfetto tracing is not enabled.
Definition trace.hpp:107
clock::time_point t0
Definition trace.hpp:130
std::vector< Log > logs
Definition trace.hpp:131
TraceLogger(size_t capacity)
Definition trace.hpp:153
std::chrono::steady_clock clock
Definition trace.hpp:128
ScopedLog trace(const char *name, int64_t instance, int64_t flop_count=-1)
Definition trace.hpp:155
static std::ostream & write_column_headings(std::ostream &os)
Definition trace.hpp:123
std::atomic_size_t count
Definition trace.hpp:132
std::span< const Log > get_logs() const
Definition trace.hpp:171