1#ifndef LIBSBX_CORE_PROFILER_HPP_
2#define LIBSBX_CORE_PROFILER_HPP_
6#include <unordered_map>
10#include <libsbx/units/time.hpp>
12#include <libsbx/utility/hashed_string.hpp>
13#include <libsbx/utility/iterator.hpp>
14#include <libsbx/utility/exception.hpp>
18template<
typename Type>
23 using value_type = Type;
24 using size_type = std::size_t;
26 sampler(
const std::size_t capacity)
27 : _data{utility::make_reserved_vector<value_type>(capacity)},
35 _data.reserve(other._data.capacity());
36 _data.insert(_data.end(), other._data.begin(), other._data.end());
39 auto record(
const value_type value) ->
void {
40 if (_data.size() < _data.capacity()) {
41 _data.push_back(value);
44 _sum = _sum - _data[_index] + value;
45 _data[_index] = value;
48 _index = (_index + 1u) % _data.capacity();
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());
56 [[nodiscard]]
auto size()
const -> size_type {
60 [[nodiscard]]
auto data()
const ->
const value_type* {
67 _sum =
static_cast<value_type
>(0);
70 auto write_in_order(std::span<value_type> target)
const ->
void {
71 utility::assert_that(target.size() >= _data.capacity(),
"Insufficient buffer size");
73 if (_data.size() < _data.capacity()) {
74 for (
auto i = 0u; i < _data.size(); ++i) {
78 for (
auto i = _data.size(); i < _data.capacity(); ++i) {
79 target[i] =
static_cast<value_type
>(0);
84 for (
auto i = _index; i < _data.capacity(); ++i) {
85 target[position++] = _data[i];
88 for (
auto i = 0u; i < _index; ++i) {
89 target[position++] = _data[i];
96 std::vector<value_type> _data;
104 using node_id = std::uint64_t;
106 inline static constexpr auto null_node =
static_cast<node_id
>(-1);
107 inline static constexpr auto max_nodes = std::uint64_t{516u};
109 inline static constexpr auto null_time = std::chrono::microseconds{
static_cast<std::uint64_t
>(-1)};
111 std::string_view label;
112 std::string_view file;
113 std::string_view function;
117 std::chrono::microseconds time;
130 std::array<scope_info, scope_info::max_nodes> nodes;
132 scope_info::node_id next_node_id = 0u;
133 scope_info::node_id current_node_id = scope_info::null_node;
135 std::uint32_t current_depth = 0u;
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]] {
142 const auto id = next_node_id++;
147 .function = function,
149 .time = scope_info::null_time,
151 .parent_id = scope_info::null_node,
152 .depth = current_depth,
158 inline static auto instance() ->
database& {
159 static thread_local auto instance =
database{};
167 using clock = std::chrono::steady_clock;
170 clock::time_point start_time;
171 scope_info::node_id previous_node_id;
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;
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;
193#define SBX_CONCAT_TOKENS_IMPL(a, b) a##b
195#define SBX_CONCAT_TOKENS(a, b) SBX_CONCAT_TOKENS_IMPL(a, b)
197#define SBX_UNIQUE_NAME(name) SBX_CONCAT_TOKENS(name, __LINE__)
199#if defined(__clang__) || defined(__GNUC__)
200 #define FUNC_NAME __PRETTY_FUNCTION__
201#elif defined(_MSC_VER)
202 #define FUNC_NAME __FUNCSIG__
204 #define FUNC_NAME __func__
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)}
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)
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};
Definition: profiler.hpp:19
Definition: profiler.hpp:128
Definition: profiler.hpp:165
Definition: profiler.hpp:102
Definition: exception.hpp:17