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