355 if (aggregated.empty()) {
356 os <<
"No benchmark data collected\n";
362 print_separator(os,
true);
363 os << Colors::BOLD <<
" Benchmark Results" << Colors::RESET <<
"\n";
364 print_separator(os,
true);
368 for (
auto& [
key, entry_map] : aggregated) {
369 for (
auto& [parent_key, entry] : entry_map) {
370 if (entry.count > 0) {
371 keys_to_parents[
key].insert(parent_key);
377 auto print_entry = [&](
const AggregateEntry& entry,
size_t indent_level,
bool is_last, uint64_t parent_time) {
378 std::string indent(indent_level * 2,
' ');
379 std::string prefix = (indent_level == 0) ?
"" : (is_last ?
"└─ " :
"├─ ");
382 const size_t name_width = 80;
383 std::string display_name = std::string(entry.
key);
384 if (display_name.length() > name_width) {
385 display_name = display_name.substr(0, name_width - 3) +
"...";
388 double time_ms =
static_cast<double>(entry.
time_max) / 1000000.0;
389 auto colors = get_time_colors(time_ms);
392 os << indent << prefix << colors.name_color;
393 if (time_ms >= 1000.0 && colors.name_color == Colors::BOLD) {
394 os << Colors::YELLOW;
396 os << std::left << std::setw(static_cast<int>(name_width)) << display_name << Colors::RESET;
400 if (time_ms < 100.0) {
402 std::ostringstream minimal_oss;
403 minimal_oss << Colors::MAGENTA <<
"[" << indent_level <<
"] " << Colors::RESET;
404 minimal_oss << format_percentage_section(time_ms, static_cast<double>(parent_time), indent_level);
405 minimal_oss <<
" " <<
std::setw(10) <<
"";
406 os <<
" " << colors.time_color <<
std::setw(40) << std::left << minimal_oss.str() << Colors::RESET;
408 std::string aligned_section =
409 format_aligned_section(time_ms,
static_cast<double>(parent_time), entry.
count, indent_level);
410 os <<
" " << colors.time_color <<
std::setw(40) << std::left << aligned_section << Colors::RESET;
412 double mean_ms = entry.
time_mean / 1000000.0;
414 os <<
" " << entry.
num_threads <<
" threads " << mean_ms <<
"ms average " << stddev_percentage
428 uint64_t parent_time,
430 auto it = aggregated.find(
key);
431 if (it == aggregated.end()) {
437 for (
const auto& [parent_key, entry] : it->second) {
438 if ((indent_level == 0 && parent_key.empty()) || (indent_level > 0 && parent_key == current_parent)) {
439 entry_to_print = &entry;
444 if (!entry_to_print) {
449 print_entry(*entry_to_print, indent_level, is_last, parent_time);
453 if (!printed_in_detail.contains(
key)) {
454 for (
const auto& [child_key, parent_map] : aggregated) {
455 for (
const auto& [parent_key, entry] : parent_map) {
456 if (parent_key ==
key && entry.
time_max >= 500000) {
457 children.push_back(child_key);
462 printed_in_detail.insert(
key);
469 if (
auto it = aggregated.find(
a); it != aggregated.end()) {
470 for (
const auto& [parent_key, entry] : it->second) {
471 if (parent_key ==
key) {
477 if (
auto it = aggregated.find(
b); it != aggregated.end()) {
478 for (
const auto& [parent_key, entry] : it->second) {
479 if (parent_key ==
key) {
485 return time_a > time_b;
489 uint64_t children_total_time = 0;
490 for (
const auto& child_key : children) {
491 if (
auto it = aggregated.find(child_key); it != aggregated.end()) {
492 for (
const auto& [parent_key, entry] : it->second) {
493 if (parent_key ==
key && entry.
time_max >= 500000) {
494 children_total_time += entry.
time_max;
499 uint64_t parent_total_time = entry_to_print->
time_max;
500 bool should_add_other =
false;
501 if (!children.empty() && parent_total_time > 0 && children_total_time < parent_total_time) {
502 uint64_t unaccounted = parent_total_time - children_total_time;
503 double percentage = (
static_cast<double>(unaccounted) /
static_cast<double>(parent_total_time)) * 100.0;
504 should_add_other = percentage > 5.0 && unaccounted > 0;
506 uint64_t other_time = should_add_other ? (parent_total_time - children_total_time) : 0;
508 if (!children.empty() && keys_to_parents[
key].size() > 1) {
509 os << std::string(indent_level * 2,
' ') <<
" ├─ NOTE: Shared children. Can add up to > 100%.\n";
513 for (
size_t i = 0; i < children.size(); ++i) {
514 bool is_last_child = (i == children.size() - 1) && !should_add_other;
515 print_hierarchy(children[i], indent_level + 1, is_last_child, entry_to_print->
time,
key);
519 if (should_add_other && keys_to_parents[
key].size() <= 1) {
521 other_entry.
key =
"(other)";
522 other_entry.
time = other_time;
524 other_entry.
count = 1;
526 print_entry(other_entry, indent_level + 1,
true, parent_total_time);
532 for (
const auto& [
key, parent_map] : aggregated) {
533 auto empty_parent_it = parent_map.find(
"");
534 if (empty_parent_it != parent_map.end() && empty_parent_it->second.time > 0) {
535 roots.push_back(
key);
543 if (
auto it_a = aggregated.find(
a); it_a != aggregated.end()) {
544 if (
auto parent_it = it_a->second.find(
""); parent_it != it_a->second.end()) {
545 time_a = parent_it->second.time_max;
548 if (
auto it_b = aggregated.find(
b); it_b != aggregated.end()) {
549 if (
auto parent_it = it_b->second.find(
""); parent_it != it_b->second.end()) {
550 time_b = parent_it->second.time_max;
553 return time_a > time_b;
557 for (
size_t i = 0; i < roots.size(); ++i) {
558 print_hierarchy(roots[i], 0, i == roots.size() - 1, 0,
"");
562 print_separator(os,
false);
566 for (
const auto& [
key, _] : aggregated) {
567 unique_funcs.insert(
key);
569 size_t unique_functions_count = unique_funcs.size();
571 uint64_t shared_count = 0;
572 for (
const auto& [
key, parents] : keys_to_parents) {
573 if (parents.size() > 1) {
578 uint64_t total_time = 0;
579 for (
const auto& [_, parent_map] : aggregated) {
580 if (
auto it = parent_map.find(
""); it != parent_map.end()) {
581 total_time =
std::max(total_time, it->second.time_max);
585 uint64_t total_calls = 0;
586 for (
const auto& [_, parent_map] : aggregated) {
587 for (
const auto& [__, entry] : parent_map) {
588 total_calls += entry.
count;
592 double total_time_ms =
static_cast<double>(total_time) / 1000000.0;
594 os <<
" " << Colors::BOLD <<
"Total: " << Colors::RESET << Colors::MAGENTA << unique_functions_count
595 <<
" functions" << Colors::RESET;
596 if (shared_count > 0) {
597 os <<
" (" << Colors::RED << shared_count <<
" shared" << Colors::RESET <<
")";
599 os <<
", " << Colors::GREEN << total_calls <<
" measurements" << Colors::RESET <<
", " << Colors::YELLOW;
600 if (total_time_ms >= 1000.0) {
608 print_separator(os,
true);