71 using Mutex = std::mutex;
74 Timing(std::string
const& tag =
"Total",
char const* color =
"");
76 Timing(
char const* tag,
char const* color =
"");
78 template <
class InputIt>
79 Timing(std::string
const& tag, InputIt first, InputIt last)
80 :
Timing(tag,
"", first, last)
84 template <
class InputIt>
85 Timing(
char const* tag, InputIt first, InputIt last)
86 :
Timing(std::string(tag), first, last)
90 template <
class InputIt>
91 Timing(std::string
const& tag,
char const* color, InputIt first, InputIt last)
97 template <
class InputIt>
98 Timing(
char const* tag,
char const* color, InputIt first, InputIt last)
99 :
Timing(std::string(tag), color, first, last)
103 Timing(std::string
const& tag, std::initializer_list<Timing> init);
105 Timing(
char const* tag, std::initializer_list<Timing> init);
107 Timing(std::string
const& tag,
char const* color, std::initializer_list<Timing> init);
109 Timing(
char const* tag,
char const* color, std::initializer_list<Timing> init);
113 Timing& start(std::string
const& tag);
115 Timing& start(std::string
const& tag,
char const* color);
119 std::size_t stop(std::size_t levels);
123 Timing const& operator[](std::string
const& tag)
const;
125 Timing& operator[](std::string
const& tag);
127 void extend(
Timing const& source);
129 void extend(
Timing&& source);
131 template <
class InputIt>
132 void extend(InputIt first, InputIt last)
134 std::lock_guard lock(mutex_);
135 for (; first != last; ++first) {
140 void extend(std::initializer_list<Timing> ilist);
142 void merge(
Timing const& source);
144 void merge(
Timing&& source);
146 template <
class InputIt>
147 void merge(InputIt first, InputIt last)
149 std::lock_guard lock(mutex_);
150 for (; first != last; ++first) {
155 void merge(std::initializer_list<Timing> ilist);
157 std::string
const& tag()
const;
159 std::string
const& color()
const;
161 void setColor(std::string
const& color);
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"; }
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
186 print(
"", random_colors, bold, info, group_colors_level, precision);
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
194 using namespace std::string_literals;
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(),
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 šø ";
207 std::size_t header_length = header_left.length() + header_right.length() + 1;
209 auto timers = timings();
211 std::vector<std::pair<std::wstring, std::wstring>> component{{L
" Component ", L
""}};
212 addTags(component, timers);
214 std::size_t component_length{};
215 for (
auto const& [prefix, tag] : component) {
216 component_length = std::max(component_length, prefix.length() + tag.length());
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>(); }};
231 for (std::size_t i{}; fun.size() > i; ++i) {
232 addFloating<Period>(data[i], timers, precision, fun[i]);
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]);
240 std::vector<std::wstring> samples{L
" Samples "};
241 addNumSamples(samples, timers);
242 std::size_t samples_length = maxLength(samples);
244 std::vector<std::wstring> threads{L
" Threads "};
245 addNumThreads(threads, timers);
246 std::size_t threads_length = maxLength(threads);
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);
255 std::size_t header_sep_pos = std::max(header_left.length(), total_length / 2);
257 auto t_1 = converter.to_bytes(std::wstring(header_sep_pos, L
'ā'));
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
'ā'));
273 converter.to_bytes(std::wstring(header_sep_pos - component_length - 1, L
'ā'));
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());
278 t_1 = converter.to_bytes(std::wstring(header_sep_pos, L
'ā'));
280 converter.to_bytes(std::wstring(component_length - header_sep_pos - 1, L
'ā'));
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());
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,
"");
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(),
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(),
302 std::printf(
"ā\n");
303 auto t_1 = converter.to_bytes(std::wstring(component_length, L
'ā'));
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());
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();
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,
"");
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,
"");
333 std::printf(
"%sā%s", resetColor(), color.c_str());
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]) {
342 std::printf(
"%*s%s%*s", left_pad,
"", data[j][i].c_str(), right_pad,
"");
345 std::printf(
"%s%*s", data[j][i].c_str(), left_pad + right_pad,
"");
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,
"");
355 std::printf(
"%sā\n", resetColor());
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());
369 bool running =
false;
371 bool concurrent =
false;
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) {
383 for (
auto const& [_, tag] : component) {
384 if (2 <= tag.length() && L
'³' == tag[tag.length() - 2]) {
390 if (running || concurrent) {
391 auto t_1 = converter.to_bytes(std::wstring(component_length, L
'ā'));
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());
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,
"");
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,
"");
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,
"");
415 if (running || concurrent) {
416 auto t = converter.to_bytes(std::wstring(total_length, L
'ā'));
417 std::printf(
"ā°%sāÆ\n", t.c_str());
419 auto t_1 = converter.to_bytes(std::wstring(component_length, L
'ā'));
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());
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;
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;
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;
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;
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;
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;
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;
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;
466 Timing(
Timing* parent, std::string
const& tag, std::string
const& color);
495 void unlockParents();
497 Timing* findDeepest(std::thread::id
id);
499 std::size_t stop(std::chrono::time_point<std::chrono::high_resolution_clock> time,
502 std::pair<std::size_t, std::chrono::high_resolution_clock::duration> stopRecurs(
504 std::chrono::time_point<std::chrono::high_resolution_clock> time,
512 TimingNL(
Timing const* timing, std::size_t num,
int level)
513 : timing(timing), num(num), level(level)
518 void extendImpl(
Timing const& source);
520 void extendImpl(
Timing&& source);
522 void mergeImpl(
Timing const& source);
524 void mergeImpl(
Timing&& source);
526 std::vector<TimingNL> timings()
const;
528 void timingsRecurs(std::vector<TimingNL>& data, std::size_t num,
int level)
const;
530 int maxTagLength(std::vector<TimingNL>
const& timers)
const;
532 void addTags(std::vector<std::pair<std::wstring, std::wstring>>& data,
533 std::vector<TimingNL>
const& timers)
const;
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
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());
548 void addNumSamples(std::vector<std::wstring>& data,
549 std::vector<TimingNL>
const& timers)
const;
551 void addNumThreads(std::vector<std::wstring>& data,
552 std::vector<TimingNL>
const& timers)
const;
554 std::size_t maxLength(std::vector<std::string>
const& data)
const;
556 std::size_t maxLength(std::vector<std::wstring>
const& data)
const;
558 std::pair<int, int> centeringPadding(std::string
const& str,
int max_width)
const;
560 std::pair<int, int> centeringPadding(std::wstring
const& str,
int max_width)
const;
562 template <
class Period>
563 static std::wstring unit()
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>) {
578 return L
"[PERIOD/UNIT NOT SUPPORTED]";
582 [[nodiscard]]
int numSamples()
const;
584 void updateMaxConcurrent();
586 [[nodiscard]] std::size_t numRunningThreads()
const;
591 std::chrono::time_point<std::chrono::high_resolution_clock> start;
592 std::chrono::high_resolution_clock::duration extra_time;
595 mutable Mutex mutex_;
596 mutable std::unique_lock<Mutex> lock_;
599 std::map<std::thread::id, SingleTimer> thread_;
604 Timing* parent_ =
nullptr;
605 std::map<std::string, Timing> children_;
607 std::size_t max_concurrent_threads_ = 0;