UFO 1.0.0
An Efficient Probabilistic 3D Mapping Framework That Embraces the Unknown
Loading...
Searching...
No Matches
timing.hpp
1
42#ifndef UFO_TIME_TIMING_HPP
43#define UFO_TIME_TIMING_HPP
44
45// UFO
46#include <ufo/time/timer.hpp>
47
48// STL
49#include <array>
50#include <codecvt>
51#include <cstdlib>
52#include <functional>
53#include <iomanip>
54#include <limits>
55#include <list>
56#include <locale>
57#include <map>
58#include <mutex>
59#include <numeric>
60#include <set>
61#include <sstream>
62#include <string>
63#include <thread>
64#include <utility>
65#include <vector>
66
67namespace ufo
68{
69class Timing
70{
71 using Mutex = std::mutex;
72
73 public:
74 Timing(std::string const& tag = "Total", char const* color = "");
75
76 Timing(char const* tag, char const* color = "");
77
78 template <class InputIt>
79 Timing(std::string const& tag, InputIt first, InputIt last)
80 : Timing(tag, "", first, last)
81 {
82 }
83
84 template <class InputIt>
85 Timing(char const* tag, InputIt first, InputIt last)
86 : Timing(std::string(tag), first, last)
87 {
88 }
89
90 template <class InputIt>
91 Timing(std::string const& tag, char const* color, InputIt first, InputIt last)
92 : Timing(tag, color)
93 {
94 extend(first, last);
95 }
96
97 template <class InputIt>
98 Timing(char const* tag, char const* color, InputIt first, InputIt last)
99 : Timing(std::string(tag), color, first, last)
100 {
101 }
102
103 Timing(std::string const& tag, std::initializer_list<Timing> init);
104
105 Timing(char const* tag, std::initializer_list<Timing> init);
106
107 Timing(std::string const& tag, char const* color, std::initializer_list<Timing> init);
108
109 Timing(char const* tag, char const* color, std::initializer_list<Timing> init);
110
111 Timing& start();
112
113 Timing& start(std::string const& tag);
114
115 Timing& start(std::string const& tag, char const* color);
116
117 bool stop();
118
119 std::size_t stop(std::size_t levels);
120
121 void stopAll();
122
123 Timing const& operator[](std::string const& tag) const;
124
125 Timing& operator[](std::string const& tag);
126
127 void extend(Timing const& source);
128
129 void extend(Timing&& source);
130
131 template <class InputIt>
132 void extend(InputIt first, InputIt last)
133 {
134 std::lock_guard lock(mutex_);
135 for (; first != last; ++first) {
136 extendImpl(*first);
137 }
138 }
139
140 void extend(std::initializer_list<Timing> ilist);
141
142 void merge(Timing const& source);
143
144 void merge(Timing&& source);
145
146 template <class InputIt>
147 void merge(InputIt first, InputIt last)
148 {
149 std::lock_guard lock(mutex_);
150 for (; first != last; ++first) {
151 mergeImpl(*first);
152 }
153 }
154
155 void merge(std::initializer_list<Timing> ilist);
156
157 std::string const& tag() const;
158
159 std::string const& color() const;
160
161 void setColor(std::string const& color);
162
163 static constexpr char const* resetColor() { return "\033[0m"; }
164 static constexpr char const* blackColor() { return "\033[30m"; }
165 static constexpr char const* redColor() { return "\033[31m"; }
166 static constexpr char const* greenColor() { return "\033[32m"; }
167 static constexpr char const* yellowColor() { return "\033[33m"; }
168 static constexpr char const* blueColor() { return "\033[34m"; }
169 static constexpr char const* magentaColor() { return "\033[35m"; }
170 static constexpr char const* cyanColor() { return "\033[36m"; }
171 static constexpr char const* whiteColor() { return "\033[37m"; }
172 static constexpr char const* boldBlackColor() { return "\033[1m\033[30m"; }
173 static constexpr char const* boldRedColor() { return "\033[1m\033[31m"; }
174 static constexpr char const* boldGreenColor() { return "\033[1m\033[32m"; }
175 static constexpr char const* boldYellowColor() { return "\033[1m\033[33m"; }
176 static constexpr char const* boldBlueColor() { return "\033[1m\033[34m"; }
177 static constexpr char const* boldMagentaColor() { return "\033[1m\033[35m"; }
178 static constexpr char const* boldCyanColor() { return "\033[1m\033[36m"; }
179 static constexpr char const* boldWhiteColor() { return "\033[1m\033[37m"; }
180
181 template <class Period = std::chrono::seconds::period>
182 void print(bool random_colors = false, bool bold = false, bool info = true,
183 int group_colors_level = std::numeric_limits<int>::max(),
184 int precision = 4) const
185 {
186 print("", random_colors, bold, info, group_colors_level, precision);
187 }
188
189 template <class Period = std::chrono::seconds::period>
190 void print(std::string const& name, bool random_colors = false, bool bold = false,
191 bool info = true, int group_colors_level = std::numeric_limits<int>::max(),
192 int precision = 4) const
193 {
194 using namespace std::string_literals;
195
196 std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
197 static constexpr std::array const RC{redColor(), greenColor(), yellowColor(),
198 blueColor(), magentaColor(), cyanColor(),
199 whiteColor()};
200
201 std::wstring header_left =
202 L" " + (name.empty() ? L"Timings" : converter.from_bytes(name) + L" timings") +
203 L" in " + unit<Period>() + L" ";
204 std::wstring header_right = L" UFO šŸ›ø ";
205
206 // Left + right + seperator
207 std::size_t header_length = header_left.length() + header_right.length() + 1;
208
209 auto timers = timings();
210
211 std::vector<std::pair<std::wstring, std::wstring>> component{{L" Component ", L""}};
212 addTags(component, timers);
213
214 std::size_t component_length{};
215 for (auto const& [prefix, tag] : component) {
216 component_length = std::max(component_length, prefix.length() + tag.length());
217 }
218
219 std::array data{
220 std::vector<std::string>{" Total "s}, std::vector<std::string>{" Last "s},
221 std::vector<std::string>{" Mean "s}, std::vector<std::string>{" Std dev "s},
222 std::vector<std::string>{" Min "s}, std::vector<std::string>{" Max "s}};
223 std::array<std::function<double(Timing const&)>, data.size()> fun{
224 [](Timing const& t) { return t.timer_.total<Period>(); },
225 [](Timing const& t) { return t.timer_.last<Period>(); },
226 [](Timing const& t) { return t.timer_.mean<Period>(); },
227 [](Timing const& t) { return t.timer_.std<Period>(); },
228 [](Timing const& t) { return t.timer_.min<Period>(); },
229 [](Timing const& t) { return t.timer_.max<Period>(); }};
230
231 for (std::size_t i{}; fun.size() > i; ++i) {
232 addFloating<Period>(data[i], timers, precision, fun[i]);
233 }
234
235 std::array<std::size_t, data.size()> data_length;
236 for (std::size_t i{}; data.size() > i; ++i) {
237 data_length[i] = maxLength(data[i]);
238 }
239
240 std::vector<std::wstring> samples{L" Samples "};
241 addNumSamples(samples, timers);
242 std::size_t samples_length = maxLength(samples);
243
244 std::vector<std::wstring> threads{L" Threads "};
245 addNumThreads(threads, timers);
246 std::size_t threads_length = maxLength(threads);
247
248 std::size_t total_length =
249 std::accumulate(std::begin(data_length), std::end(data_length),
250 component_length + 1 + samples_length + threads_length);
251 total_length = std::max(total_length, header_length);
252
253 {
254 // Header
255 std::size_t header_sep_pos = std::max(header_left.length(), total_length / 2);
256
257 auto t_1 = converter.to_bytes(std::wstring(header_sep_pos, L'─'));
258 auto t_2 =
259 converter.to_bytes(std::wstring(total_length - header_sep_pos - 1, L'─'));
260 std::printf("ā•­%s┬%sā•®\n", t_1.c_str(), t_2.c_str());
261 t_1 = converter.to_bytes(header_left);
262 t_2 = converter.to_bytes(header_right);
263 int s_1 = static_cast<int>(header_sep_pos);
264 int s_2 = total_length - header_sep_pos + 1;
265 std::printf("│%-*s│%*s│\n", s_1, t_1.c_str(), s_2, t_2.c_str());
266 if (component_length == header_sep_pos) {
267 t_1 = converter.to_bytes(std::wstring(header_sep_pos, L'─'));
268 t_2 = converter.to_bytes(std::wstring(total_length - header_sep_pos - 1, L'─'));
269 std::printf("ā”œ%s┼%s┤\n", t_1.c_str(), t_2.c_str());
270 } else if (component_length < header_sep_pos) {
271 t_1 = converter.to_bytes(std::wstring(component_length, L'─'));
272 t_2 =
273 converter.to_bytes(std::wstring(header_sep_pos - component_length - 1, L'─'));
274 auto t_3 =
275 converter.to_bytes(std::wstring(total_length - header_sep_pos - 1, L'─'));
276 std::printf("ā”œ%s┬%s┓%s┤\n", t_1.c_str(), t_2.c_str(), t_3.c_str());
277 } else {
278 t_1 = converter.to_bytes(std::wstring(header_sep_pos, L'─'));
279 t_2 =
280 converter.to_bytes(std::wstring(component_length - header_sep_pos - 1, L'─'));
281 auto t_3 =
282 converter.to_bytes(std::wstring(total_length - component_length - 1, L'─'));
283 std::printf("ā”œ%s┓%s┬%s┤\n", t_1.c_str(), t_2.c_str(), t_3.c_str());
284 }
285 }
286
287 {
288 // Labels
289 auto [left_pad, right_pad] = centeringPadding(component[0].first, component_length);
290 std::printf("│%*s%s%*s│", left_pad, "",
291 converter.to_bytes(component[0].first).c_str(), right_pad, "");
292 for (std::size_t i{0}; data.size() != i; ++i) {
293 auto [left_pad, right_pad] = centeringPadding(data[i][0], data_length[i]);
294 std::printf("%*s%s%*s", left_pad, "", data[i][0].c_str(), right_pad, "");
295 }
296 std::tie(left_pad, right_pad) = centeringPadding(samples[0], samples_length);
297 std::printf("%*s%s%*s", left_pad, "", converter.to_bytes(samples[0]).c_str(),
298 right_pad, "");
299 std::tie(left_pad, right_pad) = centeringPadding(threads[0], threads_length);
300 std::printf("%*s%s%*s", left_pad, "", converter.to_bytes(threads[0]).c_str(),
301 right_pad, "");
302 std::printf("│\n");
303 auto t_1 = converter.to_bytes(std::wstring(component_length, L'─'));
304 auto t_2 =
305 converter.to_bytes(std::wstring(total_length - component_length - 1, L'─'));
306 std::printf("ā”œ%s┼%s┤\n", t_1.c_str(), t_2.c_str());
307 }
308
309 {
310 // Data
311 int rng_color = 0;
312 for (std::size_t i{1}; component.size() > i; ++i) {
313 rng_color += timers[i - 1].level <= group_colors_level;
314 std::string color = bold ? "\033[1m" : "";
315 color += random_colors ? RC[rng_color % RC.size()]
316 : timers[i - 1].timing->color().c_str();
317
318 std::printf("│");
319 {
320 // Component
321 if (1 == i) {
322 auto [left_pad, right_pad] =
323 centeringPadding(component[i].second, component_length);
324 std::printf("%*s%s%s%*s", left_pad, "", color.c_str(),
325 converter.to_bytes(component[i].second).c_str(), right_pad, "");
326 } else {
327 auto prefix = converter.to_bytes(component[i].first);
328 auto tag = converter.to_bytes(component[i].second);
329 int s = component_length - component[i].first.length() -
330 component[i].second.length();
331 std::printf("%s%s%s%*s", prefix.c_str(), color.c_str(), tag.c_str(), s, "");
332 }
333 std::printf("%s│%s", resetColor(), color.c_str());
334 }
335
336 {
337 // Other data
338 for (std::size_t j{0}; data.size() > j; ++j) {
339 auto [left_pad, right_pad] = centeringPadding(data[j][i], data_length[j]);
340 if (" nan " == data[j][i]) {
341 // Center aligned
342 std::printf("%*s%s%*s", left_pad, "", data[j][i].c_str(), right_pad, "");
343 } else {
344 // Left aligned
345 std::printf("%s%*s", data[j][i].c_str(), left_pad + right_pad, "");
346 }
347 }
348 int s = static_cast<int>(samples_length - samples[i].length());
349 std::printf("%s%*s", converter.to_bytes(samples[i]).c_str(), s, "");
350 int t = static_cast<int>(threads_length - threads[i].length());
351 std::printf("%s%*s", converter.to_bytes(threads[i]).c_str(), t, "");
352 }
353
354 // Reset color
355 std::printf("%s│\n", resetColor());
356
357 {
358 // First seperator
359 if (1 == i && component.size() > 2) {
360 auto t_1 = converter.to_bytes(std::wstring(component_length, L'ā•Œ'));
361 auto t_2 = converter.to_bytes(
362 std::wstring(total_length - component_length - 1, L'ā•Œ'));
363 std::printf("ā”œ%s┼%s┤\n", t_1.c_str(), t_2.c_str());
364 }
365 }
366 }
367 }
368
369 bool running = false;
370 bool paused = false;
371 bool concurrent = false;
372 if (info) {
373 // Info
374 for (auto const& s : samples) {
375 running = running || (2 <= s.length() && L'¹' == s[s.length() - 2]) ||
376 (3 <= s.length() && L'¹' == s[s.length() - 3]);
377 paused = paused || (2 <= s.length() && L'²' == s[s.length() - 2]);
378 if (running && paused) {
379 break;
380 }
381 }
382
383 for (auto const& [_, tag] : component) {
384 if (2 <= tag.length() && L'³' == tag[tag.length() - 2]) {
385 concurrent = true;
386 break;
387 }
388 }
389
390 if (running || concurrent) {
391 auto t_1 = converter.to_bytes(std::wstring(component_length, L'─'));
392 auto t_2 =
393 converter.to_bytes(std::wstring(total_length - component_length - 1, L'─'));
394 std::printf("ā”œ%s┓%s┤\n", t_1.c_str(), t_2.c_str());
395 if (running) {
396 std::wstring info = L" ¹ # running threads that are not accounted for ";
397 int s = static_cast<int>(total_length - info.length());
398 std::printf("│%s%*s│\n", converter.to_bytes(info).c_str(), s, "");
399 }
400 if (paused) {
401 std::wstring info = L" ² Indicates that the timer is paused ";
402 int s = static_cast<int>(total_length - info.length());
403 std::printf("│%s%*s│\n", converter.to_bytes(info).c_str(), s, "");
404 }
405 if (concurrent) {
406 std::wstring info = L" ³ Indicates that the timer has run concurrently ";
407 int s = static_cast<int>(total_length - info.length());
408 std::printf("│%s%*s│\n", converter.to_bytes(info).c_str(), s, "");
409 }
410 }
411 }
412
413 {
414 // Footer
415 if (running || concurrent) {
416 auto t = converter.to_bytes(std::wstring(total_length, L'─'));
417 std::printf("ā•°%s╯\n", t.c_str());
418 } else {
419 auto t_1 = converter.to_bytes(std::wstring(component_length, L'─'));
420 auto t_2 =
421 converter.to_bytes(std::wstring(total_length - component_length - 1, L'─'));
422 std::printf("ā•°%s┓%s╯\n", t_1.c_str(), t_2.c_str());
423 }
424 }
425 }
426
427 void printSeconds(bool random_colors = false, bool bold = false, bool info = true,
428 int group_colors_level = std::numeric_limits<int>::max(),
429 int precision = 4) const;
430
431 void printSeconds(std::string const& name, bool random_colors = false,
432 bool bold = false, bool info = true,
433 int group_colors_level = std::numeric_limits<int>::max(),
434 int precision = 4) const;
435
436 void printMilliseconds(bool random_colors = false, bool bold = false, bool info = true,
437 int group_colors_level = std::numeric_limits<int>::max(),
438 int precision = 4) const;
439
440 void printMilliseconds(std::string const& name, bool random_colors = false,
441 bool bold = false, bool info = true,
442 int group_colors_level = std::numeric_limits<int>::max(),
443 int precision = 4) const;
444
445 void printMicroseconds(bool random_colors = false, bool bold = false, bool info = true,
446 int group_colors_level = std::numeric_limits<int>::max(),
447 int precision = 4) const;
448
449 void printMicroseconds(std::string const& name, bool random_colors = false,
450 bool bold = false, bool info = true,
451 int group_colors_level = std::numeric_limits<int>::max(),
452 int precision = 4) const;
453
454 void printNanoseconds(bool random_colors = false, bool bold = false, bool info = true,
455 int group_colors_level = std::numeric_limits<int>::max(),
456 int precision = 4) const;
457
458 void printNanoseconds(std::string const& name, bool random_colors = false,
459 bool bold = false, bool info = true,
460 int group_colors_level = std::numeric_limits<int>::max(),
461 int precision = 4) const;
462
463 private:
464 Timing(Timing* parent, std::string const& tag);
465
466 Timing(Timing* parent, std::string const& tag, std::string const& color);
467
468 // Timing(Timing const& other);
469
470 // Timing(Timing const& other, std::unique_lock<Mutex> rhs_lk);
471
472 // Timing(Timing&& other);
473
474 // Timing(Timing&& other, std::unique_lock<Mutex> rhs_lk);
475
476 // Timing& operator=(Timing const& rhs);
477
478 // Timing& operator=(Timing&& rhs);
479
480 // friend void swap(Timing& a, Timing& b)
481 // {
482 // if (&a != &b) {
483 // std::scoped_lock lock(a.mutex_, b.mutex_);
484 // using std::swap;
485 // swap(a.timings_, b.timings_);
486 // swap(a.tag_, b.tag_);
487 // swap(a.color_, b.color_);
488 // swap(a.started_id_, b.started_id_);
489 // swap(a.has_run_concurrent_, b.has_run_concurrent_);
490 // }
491 // }
492
493 void lockParents();
494
495 void unlockParents();
496
497 Timing* findDeepest(std::thread::id id);
498
499 std::size_t stop(std::chrono::time_point<std::chrono::high_resolution_clock> time,
500 std::size_t levels);
501
502 std::pair<std::size_t, std::chrono::high_resolution_clock::duration> stopRecurs(
503 std::thread::id id,
504 std::chrono::time_point<std::chrono::high_resolution_clock> time,
505 std::size_t levels);
506
507 struct TimingNL {
508 Timing const* timing;
509 std::size_t num;
510 int level;
511
512 TimingNL(Timing const* timing, std::size_t num, int level)
513 : timing(timing), num(num), level(level)
514 {
515 }
516 };
517
518 void extendImpl(Timing const& source);
519
520 void extendImpl(Timing&& source);
521
522 void mergeImpl(Timing const& source);
523
524 void mergeImpl(Timing&& source);
525
526 std::vector<TimingNL> timings() const;
527
528 void timingsRecurs(std::vector<TimingNL>& data, std::size_t num, int level) const;
529
530 int maxTagLength(std::vector<TimingNL> const& timers) const;
531
532 void addTags(std::vector<std::pair<std::wstring, std::wstring>>& data,
533 std::vector<TimingNL> const& timers) const;
534
535 template <class Period, class Fun>
536 void addFloating(std::vector<std::string>& data, std::vector<TimingNL> const& timers,
537 int precision, Fun f) const
538 {
539 std::stringstream ss;
540 for (auto const& t : timers) {
541 ss << std::fixed << std::setprecision(precision);
542 ss << ' ' << f(*t.timing) << ' ';
543 data.push_back(ss.str());
544 ss = {};
545 }
546 }
547
548 void addNumSamples(std::vector<std::wstring>& data,
549 std::vector<TimingNL> const& timers) const;
550
551 void addNumThreads(std::vector<std::wstring>& data,
552 std::vector<TimingNL> const& timers) const;
553
554 std::size_t maxLength(std::vector<std::string> const& data) const;
555
556 std::size_t maxLength(std::vector<std::wstring> const& data) const;
557
558 std::pair<int, int> centeringPadding(std::string const& str, int max_width) const;
559
560 std::pair<int, int> centeringPadding(std::wstring const& str, int max_width) const;
561
562 template <class Period>
563 static std::wstring unit()
564 {
565 if constexpr (std::is_same_v<Period, std::chrono::nanoseconds::period>) {
566 return L"nanoseconds (ns)";
567 } else if constexpr (std::is_same_v<Period, std::chrono::microseconds::period>) {
568 return L"microseconds (µs)";
569 } else if constexpr (std::is_same_v<Period, std::chrono::milliseconds::period>) {
570 return L"milliseconds (ms)";
571 } else if constexpr (std::is_same_v<Period, std::chrono::seconds::period>) {
572 return L"seconds (s)";
573 } else if constexpr (std::is_same_v<Period, std::chrono::minutes::period>) {
574 return L"minutes (min)";
575 } else if constexpr (std::is_same_v<Period, std::chrono::hours::period>) {
576 return L"hours (h)";
577 } else {
578 return L"[PERIOD/UNIT NOT SUPPORTED]";
579 }
580 }
581
582 [[nodiscard]] int numSamples() const;
583
584 void updateMaxConcurrent();
585
586 [[nodiscard]] std::size_t numRunningThreads() const;
587
588 private:
589 struct SingleTimer {
590 bool independent;
591 std::chrono::time_point<std::chrono::high_resolution_clock> start;
592 std::chrono::high_resolution_clock::duration extra_time;
593 };
594
595 mutable Mutex mutex_;
596 mutable std::unique_lock<Mutex> lock_;
597
598 Timer timer_;
599 std::map<std::thread::id, SingleTimer> thread_;
600
601 std::string tag_;
602 std::string color_;
603
604 Timing* parent_ = nullptr;
605 std::map<std::string, Timing> children_;
606
607 std::size_t max_concurrent_threads_ = 0;
608};
609} // namespace ufo
610
611#endif // UFO_TIME_TIMING_HPP
All vision-related classes and functions.
Definition cloud.hpp:49