sandbox
Loading...
Searching...
No Matches
profiler.hpp
1#ifndef LIBSBX_CORE_PROFILER_HPP_
2#define LIBSBX_CORE_PROFILER_HPP_
3
4#include <string>
5#include <string_view>
6#include <unordered_map>
7#include <ranges>
8#include <chrono>
9
10#include <libsbx/units/time.hpp>
11
12#include <libsbx/utility/hashed_string.hpp>
13#include <libsbx/utility/iterator.hpp>
14#include <libsbx/utility/exception.hpp>
15
16namespace sbx::core {
17
18template<typename Type>
19class sampler {
20
21public:
22
23 using value_type = Type;
24 using size_type = std::size_t;
25
26 sampler(const std::size_t capacity)
27 : _data{utility::make_reserved_vector<value_type>(capacity)},
28 _index{0},
29 _sum{0} { }
30
31 sampler(const sampler& other)
32 : _data{},
33 _index{other._index},
34 _sum{other._sum} {
35 _data.reserve(other._data.capacity());
36 _data.insert(_data.end(), other._data.begin(), other._data.end());
37 }
38
39 auto record(const value_type value) -> void {
40 if (_data.size() < _data.capacity()) {
41 _data.push_back(value);
42 _sum += value;
43 } else {
44 _sum = _sum - _data[_index] + value;
45 _data[_index] = value;
46 }
47
48 _index = (_index + 1u) % _data.capacity();
49 }
50
51 template<typename Other>
52 [[nodiscard]] auto average_as() const -> Other {
53 return _data.size() == 0u ? static_cast<Other>(0) : static_cast<Other>(_sum) / static_cast<Other>(_data.size());
54 }
55
56 [[nodiscard]] auto size() const -> size_type {
57 return _data.size();
58 }
59
60 [[nodiscard]] auto data() const -> const value_type* {
61 return _data.data();
62 }
63
64 void clear() {
65 _data.clear();
66 _index = 0u;
67 _sum = static_cast<value_type>(0);
68 }
69
70 auto write_in_order(std::span<value_type> target) const -> void {
71 utility::assert_that(target.size() >= _data.capacity(), "Insufficient buffer size");
72
73 if (_data.size() < _data.capacity()) {
74 for (auto i = 0u; i < _data.size(); ++i) {
75 target[i] = _data[i];
76 }
77
78 for (auto i = _data.size(); i < _data.capacity(); ++i) {
79 target[i] = static_cast<value_type>(0);
80 }
81 } else {
82 auto position = 0u;
83
84 for (auto i = _index; i < _data.capacity(); ++i) {
85 target[position++] = _data[i];
86 }
87
88 for (auto i = 0u; i < _index; ++i) {
89 target[position++] = _data[i];
90 }
91 }
92 }
93
94private:
95
96 std::vector<value_type> _data;
97 size_type _index;
98 value_type _sum;
99
100}; // class sampler
101
103
104 using node_id = std::uint64_t;
105
106 inline static constexpr auto null_node = static_cast<node_id>(-1);
107 inline static constexpr auto max_nodes = std::uint64_t{516u};
108
109 inline static constexpr auto null_time = std::chrono::microseconds{static_cast<std::uint64_t>(-1)};
110
111 std::string_view label;
112 std::string_view file;
113 std::string_view function;
114
115 std::uint32_t line;
116
117 std::chrono::microseconds time;
118
119 node_id id;
120 node_id parent_id;
121
122 std::uint32_t depth;
123
124}; // struct scope_info
125
126namespace detail {
127
128struct database {
129
130 std::array<scope_info, scope_info::max_nodes> nodes;
131
132 scope_info::node_id next_node_id = 0u;
133 scope_info::node_id current_node_id = scope_info::null_node;
134
135 std::uint32_t current_depth = 0u;
136
137 [[nodiscard]] auto create_node(const std::string_view label, const std::string_view file, const std::string_view function, const std::uint32_t line) -> scope_info& {
138 if (next_node_id >= scope_info::max_nodes) [[unlikely]] {
139 throw utility::runtime_error("Profiler exceeded maximum number of nodes ({})", scope_info::max_nodes);
140 }
141
142 const auto id = next_node_id++;
143
144 nodes[id] = scope_info{
145 .label = label,
146 .file = file,
147 .function = function,
148 .line = line,
149 .time = scope_info::null_time,
150 .id = id,
151 .parent_id = scope_info::null_node,
152 .depth = current_depth,
153 };
154
155 return nodes[id];
156 }
157
158 inline static auto instance() -> database& {
159 static thread_local auto instance = database{};
160 return instance;
161 }
162
163}; // struct database
164
166
167 using clock = std::chrono::steady_clock;
168
169 scope_info& info;
170 clock::time_point start_time;
171 scope_info::node_id previous_node_id;
172
173
174 explicit scope_guard(scope_info& info)
175 : info{info},
176 start_time{clock::now()},
177 previous_node_id{database::instance().current_node_id} {
178 info.parent_id = database::instance().current_node_id;
179 database::instance().current_node_id = info.id;
180 database::instance().current_depth = info.depth + 1u;
181 }
182
183
184
185 ~scope_guard() {
186 info.time = std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start_time);
187 database::instance().current_node_id = previous_node_id;
188 database::instance().current_depth = info.depth;
189 }
190
191}; // struct scope_guard
192
193#define SBX_CONCAT_TOKENS_IMPL(a, b) a##b
194
195#define SBX_CONCAT_TOKENS(a, b) SBX_CONCAT_TOKENS_IMPL(a, b)
196
197#define SBX_UNIQUE_NAME(name) SBX_CONCAT_TOKENS(name, __LINE__)
198
199#if defined(__clang__) || defined(__GNUC__)
200 #define FUNC_NAME __PRETTY_FUNCTION__
201#elif defined(_MSC_VER)
202 #define FUNC_NAME __FUNCSIG__
203#else
204 #define FUNC_NAME __func__
205#endif
206
207} // namespace detail
208
209#define SBX_PROFILE_SCOPE(label) \
210 static thread_local auto& SBX_UNIQUE_NAME(profiler_scope_info) = ::sbx::core::detail::database::instance().create_node((label), __FILE__, FUNC_NAME, __LINE__); \
211 const auto SBX_UNIQUE_NAME(profiler_scope_guard) = ::sbx::core::detail::scope_guard{SBX_UNIQUE_NAME(profiler_scope_info)}
212
213#define SBX_PROFILE_BLOCK(label) \
214 static thread_local auto& SBX_UNIQUE_NAME(profiler_scope_info) = ::sbx::core::detail::database::instance().create_node((label), __FILE__, FUNC_NAME, __LINE__); \
215 if (const auto SBX_UNIQUE_NAME(profiler_scope_guard) = ::sbx::core::detail::scope_guard{SBX_UNIQUE_NAME(profiler_scope_info)}; true)
216
217inline auto scope_infos() -> std::span<const scope_info> {
218 return std::span<const scope_info>{detail::database::instance().nodes.data(), detail::database::instance().next_node_id};
219}
220
221} // namespace sbx::core
222
223#endif // LIBSBX_CORE_PROFILER_HPP_
Definition: profiler.hpp:19
Definition: profiler.hpp:128
Definition: profiler.hpp:165
Definition: profiler.hpp:102
Definition: exception.hpp:17