HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
counters.hpp
Go to the documentation of this file.
1// Copyright Take Vos 2019, 2021-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
8#pragma once
9
10#include "log.hpp"
11#include "../utility/utility.hpp"
12#include "../concurrency/concurrency.hpp"
13#include "../time/module.hpp"
14#include "../macros.hpp"
15#include <span>
16#include <typeinfo>
17#include <typeindex>
18#include <string>
19#include <atomic>
20#include <map>
21#include <memory>
22#include <mutex>
23
24
25
26namespace hi::inline v1 {
27namespace detail {
28
29class counter {
30public:
37 [[nodiscard]] static counter *get_if(std::string const& name) noexcept
38 {
39 hilet lock = std::scoped_lock(_mutex);
40 hilet& map_ = _map.get_or_make();
41 hilet it = map_.find(name);
42 if (it == map_.cend()) {
43 return nullptr;
44 } else {
45 hi_assert(it->second);
46 return it->second;
47 }
48 }
49
50 counter(counter const&) = delete;
51 counter(counter&&) = delete;
52 counter& operator=(counter const&) = delete;
53 counter& operator=(counter&&) = delete;
54
55 constexpr counter() noexcept {}
56
57 operator uint64_t() const noexcept
58 {
59 return _total_count.load(std::memory_order::relaxed);
60 }
61
62 static void log() noexcept
63 {
64 hilet lock = std::scoped_lock(_mutex);
65 log_header();
66 for (hilet & [ string, counter ] : _map.get_or_make()) {
67 hi_assert(counter);
68 counter->log(string);
69 }
70 }
71
72 static void log_header() noexcept
73 {
74 hi_log_statistics("");
75 hi_log_statistics("{:>18} {:>9} {:>10} {:>10} {:>10}", "total", "delta", "min", "max", "mean");
76 hi_log_statistics("------------------ --------- ---------- ---------- ----------");
77 }
78
83 void log(std::string const& tag) noexcept
84 {
85 hilet total_count = _total_count.load(std::memory_order::relaxed);
86 hilet prev_count = _prev_count.exchange(_total_count, std::memory_order::relaxed);
87 hilet delta_count = total_count - prev_count;
88 if (delta_count != 0) {
89 hilet duration_max = time_stamp_count::duration_from_count(_duration_max.exchange(0, std::memory_order::relaxed));
90 hilet duration_min = time_stamp_count::duration_from_count(
91 _duration_min.exchange(std::numeric_limits<uint64_t>::max(), std::memory_order::relaxed));
92
93 hilet duration_avg = _duration_avg.exchange(0, std::memory_order::relaxed);
94 if (duration_avg == 0) {
95 hi_log_statistics("{:>18} {:>+9} {:10} {:10} {:10} {}", total_count, delta_count, "", "", "", tag);
96
97 } else {
98 hilet avg_count = duration_avg & 0xffff;
99 hilet avg_sum = duration_avg >> 16;
100 hilet average = time_stamp_count::duration_from_count(avg_sum / avg_count);
101
102 hi_log_statistics(
103 "{:18d} {:+9d} {:>10} {:>10} {:>10} {}",
104 total_count,
105 delta_count,
106 format_engineering(duration_min),
107 format_engineering(duration_max),
108 format_engineering(average),
109 tag);
110 }
111 }
112 }
113
114 uint64_t operator++() noexcept
115 {
116 return _total_count.fetch_add(1, std::memory_order::relaxed) + 1;
117 }
118
119 uint64_t operator++(int) noexcept
120 {
121 return _total_count.fetch_add(1, std::memory_order::relaxed);
122 }
123
124 uint64_t operator--() noexcept
125 {
126 return _total_count.fetch_sub(1, std::memory_order::relaxed) + 1;
127 }
128
129 uint64_t operator--(int) noexcept
130 {
131 return _total_count.fetch_sub(1, std::memory_order::relaxed);
132 }
133
136 void add_duration(uint64_t duration) noexcept
137 {
138 _total_count.fetch_add(1, std::memory_order::relaxed);
139 fetch_max(_duration_max, duration, std::memory_order::relaxed);
140 fetch_min(_duration_min, duration, std::memory_order::relaxed);
141
142 // Combine duration with count in a single atomic.
143 hi_axiom(duration <= (std::numeric_limits<uint64_t>::max() >> 10));
144 duration <<= 16;
145 ++duration;
146 _duration_avg.fetch_add(duration, std::memory_order::relaxed);
147 }
148
149protected:
150 using map_type = std::map<std::string, counter *>;
151
155 constinit static inline unfair_mutex_impl<false> _mutex;
156 constinit static inline atomic_unique_ptr<map_type> _map;
157
158 std::atomic<uint64_t> _total_count = 0;
159 std::atomic<uint64_t> _prev_count = 0;
160 std::atomic<uint64_t> _duration_max = 0;
162
168 std::atomic<uint64_t> _duration_avg = 0;
169};
170
171template<fixed_string Tag>
172class tagged_counter : public counter {
173public:
174 tagged_counter() noexcept : counter()
175 {
176 hilet lock = std::scoped_lock(_mutex);
177 _map.get_or_make()[std::string{Tag}] = this;
178 }
179};
180
181} // namespace detail
182
183template<fixed_string Tag>
184inline detail::tagged_counter<Tag> global_counter;
185
186[[nodiscard]] inline detail::counter *get_global_counter_if(std::string const& name)
187{
188 return detail::counter::get_if(name);
189}
190
191inline void log::log_thread_main(std::stop_token stop_token) noexcept
192{
193 using namespace std::chrono_literals;
194
195 set_thread_name("log");
196 hi_log_info("log thread started");
197
198 auto counter_statistics_deadline = std::chrono::utc_clock::now() + 1min;
199
200 while (not stop_token.stop_requested()) {
201 log_global.flush();
202
203 hilet now = std::chrono::utc_clock::now();
204 if (now >= counter_statistics_deadline) {
205 counter_statistics_deadline = now + 1min;
206 detail::counter::log();
207 }
208
210 }
211
212 hi_log_info("log thread finished");
213}
214
215} // namespace hi::inline v1
void set_thread_name(std::string_view name) noexcept
Set the name of the current thread.
DOXYGEN BUG.
Definition algorithm.hpp:16
T fetch_max(std::atomic< T > &lhs, T rhs, std::memory_order order) noexcept
Lock-free fetch-then-max operation on an atomic.
Definition atomic.hpp:25
T fetch_min(std::atomic< T > &lhs, T rhs, std::memory_order order) noexcept
Lock-free fetch-then-min operation on an atomic.
Definition atomic.hpp:39
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Definition atomic.hpp:51
Definition counters.hpp:29
static counter * get_if(std::string const &name) noexcept
Get the named counter.
Definition counters.hpp:37
void log(std::string const &tag) noexcept
Log the counter.
Definition counters.hpp:83
void add_duration(uint64_t duration) noexcept
Add a duration.
Definition counters.hpp:136
Definition counters.hpp:172
hi_no_inline void flush() noexcept
Flush all messages from the log_queue directly from this thread.
Definition log.hpp:145
T max(T... args)
T sleep_for(T... args)