SAT Solver Template
Loading...
Searching...
No Matches
Profiler.hpp
Go to the documentation of this file.
1/**
2* @author Tim Luchterhand
3* @date 09.08.2024
4* @file Profiler.hpp
5* @brief Timing and profiling utilities
6*/
7
8#ifndef PROFILER_HPP
9#define PROFILER_HPP
10
11#include <string>
12#include <cmath>
13#include <chrono>
14#include <unordered_map>
15#include <vector>
16#include <ostream>
17#include <iomanip>
18#include <algorithm>
19#include <bit>
20#include <ranges>
21#include <Iterators.hpp>
22
23namespace sat {
24
25 namespace detail {
26 template<typename T>
27 struct timing_symbol {};
28
29 template<>
30 struct timing_symbol<std::chrono::milliseconds> {
31 static constexpr auto symbol = "ms";
32 };
33
34 template<>
35 struct timing_symbol<std::chrono::microseconds> {
36 static constexpr auto symbol = "µs";
37 };
38
39 template<>
40 struct timing_symbol<std::chrono::seconds> {
41 static constexpr auto symbol = "s";
42 };
43
45
46 template<std::integral T>
47 int printLen(T val) {
48 return static_cast<int>(std::log(val + 1) / std::log(10)) + 1;
49 }
50 }
51
52 /**
53 * @brief Represents an event with a duration.
54 * @details @copybrief
55 */
56 struct TimingEvent {
57 detail::TP start; ///< start point of the event
58 detail::TP end; ///< end point of the event
59
60 /**
61 * Ctor
62 * @param start start point of the event
63 * @param end end point of the event
64 */
65 TimingEvent(const detail::TP &start, const detail::TP &end) noexcept;
66
67 /**
68 * gets the duration of the event
69 * @tparam T duration type
70 * @return duration of the event
71 */
72 template<typename T>
73 auto duration() const noexcept {
74 return std::chrono::duration_cast<T>(end - start).count();
75 }
76 };
77
78 /**
79 * @brief Profiling result
80 * @details @copybrief
81 * @tparam T duration type
82 */
83 template<typename T>
84 struct Result {
85 T min, max, avg, stddev, med, sum;
86 };
87
88 /**
89 * @brief Profiler that manages multiple events.
90 * @details @copybrief
91 */
92 class Profiler {
93 std::unordered_map<std::string, std::vector<TimingEvent>> events;
94
95 public:
96
97 /**
98 * Adds an event ot the profiler
99 * @param event event to be added
100 * @param name event name
101 */
102 void addEvent(const TimingEvent &event, const std::string &name);
103
104 /**
105 * Adds an event ot the profiler
106 * @param start start point of the even
107 * @param end end point of the event
108 * @param name event name
109 */
110 void addEvent(detail::TP start, detail::TP end, const std::string &name);
111
112 /**
113 * gets the profiling result for an event
114 * @tparam T duration type
115 * @param eventName name of the event
116 * @return profiling result
117 */
118 template<typename T>
119 auto getResult(const std::string &eventName) const {
120 using Res = decltype(std::declval<TimingEvent>().duration<T>());
121 Res sum = 0;
122 Res sqSum = 0;
123 Res max = std::numeric_limits<Res>::min();
124 Res min = std::numeric_limits<Res>::max();
125 const auto &eventList = events.at(eventName);
126 std::vector<Res> results;
127 results.reserve(eventList.size());
128 for (const auto &evt : eventList) {
129 auto val = evt.duration<T>();
130 sum += val;
131 results.emplace_back(val);
132 sqSum += val * val;
133 max = std::max(max, val);
134 min = std::min(min, val);
135 }
136
137 Res mean = sum / eventList.size();
138 std::ranges::sort(results);
139 Res med;
140 if (results.empty()) {
141 med = 0;
142 } else if (results.size() % 2 == 0) {
143 med = (results[results.size() / 2] + results[results.size() / 2 - 1]) / 2;
144 } else {
145 med = results[results.size() / 2];
146 }
147
148 auto stddev = static_cast<Res>(std::sqrt(sqSum / eventList.size() - mean * mean));
149 return Result<Res>{.min=min, .max=max, .avg=mean, .stddev=stddev, .med = med, .sum=sum};
150
151 }
152
153 /**
154 * prints a profiling result to an out stream
155 * @tparam T timing type
156 * @param eventName name of the event
157 * @param os out stream
158 * @param nameWidth optional width formatting parameter for the event name
159 * @param valWidth optional width formatting parameter for the result values
160 */
161 template<typename T>
162 void print(const std::string &eventName, std::ostream &os, int nameWidth = 0, int valWidth = 0) const {
163 auto [min, max, avg, stddev, med, sum] = getResult<T>(eventName);
164 constexpr auto s = detail::timing_symbol<T>::symbol;
165 os << "-- " << std::setw(nameWidth) << std::left << eventName << ": \tmin: " << std::setw(valWidth)
166 << std::left << min << s
167 << ", max: " << std::setw(valWidth) << std::left << max << s << ", avg : " << std::setw(valWidth)
168 << std::left << avg << s
169 << ", std: " << std::setw(valWidth) << std::left << stddev << s << ", median: " << std::setw(valWidth)
170 << std::left << med
171 << s << ", total: " << sum << s << "\n";
172 }
173
174 /**
175 * Checks whether profiling data on a specific event is available
176 * @param event event name
177 * @return true if event occurred at least once, false otherwise
178 */
179 bool has(const std::string &event) const noexcept {
180 return events.contains(event);
181 }
182
183 /**
184 * prints all events to an out stream
185 * @tparam T timing type
186 * @param os out stream
187 */
188 template<typename T>
189 void printAll(std::ostream &os) const {
190 using ResT = decltype(getResult<T>("").min);
191 constexpr auto NumFields = sizeof(Result<ResT>) / sizeof(T);
192 constexpr auto s = detail::timing_symbol<T>::symbol;
193 int nameWidth = 0;
194 std::array<int, NumFields> valWidths{0};
195 std::vector<Result<ResT>> results;
196 for (const auto &[name, _] : events) {
197 nameWidth = std::max(nameWidth, static_cast<int>(name.length()) + 1);
198 results.emplace_back(getResult<T>(name));
199 const auto fields = std::bit_cast<std::array<ResT, NumFields>>(results.back());
200 for (auto [f, fWidth] : iterators::zip(fields, valWidths)) {
201 fWidth = std::max(fWidth, static_cast<int>(detail::printLen(f) + 1));
202 }
203 }
204
205 for (auto [name, res] : iterators::zip(events | std::views::keys, results)) {
206 os << "-- " << std::setw(nameWidth)
207 << std::left << name << ": \tmin: " << std::setw(valWidths[0]) << std::left << res.min << s
208 << ", max: " << std::setw(valWidths[1]) << std::left << res.max << s
209 << ", avg : " << std::setw(valWidths[2]) << std::left << res.avg << s
210 << ", std: " << std::setw(valWidths[3]) << std::left << res.stddev << s
211 << ", median: " << std::setw(valWidths[4]) << std::left << res.med << s
212 << ", total: " << std::setw(valWidths[5]) << std::left << res.sum << s << "\n";
213 }
214 }
215 };
216
217 /**
218 * @brief Used to measure time between start a start and a stop event.
219 * @details @copybrief
220 */
221 class StopWatch {
222 protected:
223 detail::TP startTp;
224 public:
225
226 /**
227 * Ctor. Starts the timer
228 */
229 StopWatch();
230
231 /**
232 * Resets the start event to the current time
233 */
234 void start();
235
236 /**
237 * Get the timing event since the last call to start and the time of the call
238 * @return TimingEvent with start point set to the last call to start() and end
239 * event the current time
240 */
241 [[nodiscard]] TimingEvent getTiming() const;
242
243 /**
244 * Get the number of elapsed time units
245 * @tparam T time unit
246 * @return number of time units since call to start
247 */
248 template<typename T>
249 auto elapsed() const {
250 return std::chrono::duration_cast<T>(std::chrono::high_resolution_clock::now() - startTp).count();
251 }
252 };
253
254
255 /**
256 * @brief Stop watch that automatically adds a timing event to a profiler at destruction
257 */
258 class ScopeWatch: protected StopWatch {
259 Profiler &profiler;
260 std::string name;
261 public:
262 /**
263 * CTor. Starts the stop watch
264 * @param profiler profiler where the event is registered
265 * @param eventName name of the event
266 */
267 ScopeWatch(Profiler &profiler, std::string eventName);
268
269 /**
270 * DTor. Stops the stop watch and adds the event to the scheduler
271 */
272 ~ScopeWatch();
273 };
274}
275
276#endif //PROFILER_HPP
Profiler that manages multiple events.
Definition Profiler.hpp:92
auto getResult(const std::string &eventName) const
Definition Profiler.hpp:119
void print(const std::string &eventName, std::ostream &os, int nameWidth=0, int valWidth=0) const
Definition Profiler.hpp:162
bool has(const std::string &event) const noexcept
Definition Profiler.hpp:179
void addEvent(detail::TP start, detail::TP end, const std::string &name)
Definition Profiler.cpp:39
void addEvent(const TimingEvent &event, const std::string &name)
Definition Profiler.cpp:35
void printAll(std::ostream &os) const
Definition Profiler.hpp:189
Stop watch that automatically adds a timing event to a profiler at destruction.
Definition Profiler.hpp:258
~ScopeWatch()
Definition Profiler.cpp:31
ScopeWatch(Profiler &profiler, std::string eventName)
Definition Profiler.cpp:26
Used to measure time between start a start and a stop event.
Definition Profiler.hpp:221
StopWatch()
Definition Profiler.cpp:22
TimingEvent getTiming() const
Definition Profiler.cpp:18
auto elapsed() const
Definition Profiler.hpp:249
void start()
Definition Profiler.cpp:14
Definition heuristics.hpp:33
Definition basic_structures.cpp:10
Profiling result.
Definition Profiler.hpp:84
Represents an event with a duration.
Definition Profiler.hpp:56
auto duration() const noexcept
Definition Profiler.hpp:73
detail::TP end
end point of the event
Definition Profiler.hpp:58
TimingEvent(const detail::TP &start, const detail::TP &end) noexcept
Definition Profiler.cpp:12
detail::TP start
start point of the event
Definition Profiler.hpp:57
Definition Profiler.hpp:27