303 if (aggregated.empty()) {
304 os <<
"No benchmark data collected\n";
310 print_separator(os,
true);
311 os << Colors::BOLD <<
" Benchmark Results" << Colors::RESET <<
"\n";
312 print_separator(os,
true);
316 for (
auto& [
key, entry_map] : aggregated) {
317 for (
auto& [parent_key, entry] : entry_map) {
318 if (entry.count > 0) {
319 keys_to_parents[
key].insert(parent_key);
325 auto print_entry = [&](
const AggregateEntry& entry,
size_t indent_level,
bool is_last, uint64_t parent_time) {
326 std::string indent(indent_level * 2,
' ');
327 std::string prefix = (indent_level == 0) ?
"" : (is_last ?
"└─ " :
"├─ ");
330 const size_t name_width = 80;
331 std::string display_name = std::string(entry.
key);
332 if (display_name.length() > name_width) {
333 display_name = display_name.substr(0, name_width - 3) +
"...";
336 double time_ms =
static_cast<double>(entry.
time_max) / 1000000.0;
337 auto colors = get_time_colors(time_ms);
340 os << indent << prefix << colors.name_color;
341 if (time_ms >= 1000.0 && colors.name_color == Colors::BOLD) {
342 os << Colors::YELLOW;
344 os << std::left << std::setw(static_cast<int>(name_width)) << display_name << Colors::RESET;
348 if (time_ms < 100.0) {
350 std::ostringstream minimal_oss;
351 minimal_oss << Colors::MAGENTA <<
"[" << indent_level <<
"] " << Colors::RESET;
352 minimal_oss << format_percentage_section(time_ms, static_cast<double>(parent_time), indent_level);
353 minimal_oss <<
" " <<
std::setw(10) <<
"";
354 os <<
" " << colors.time_color <<
std::setw(40) << std::left << minimal_oss.str() << Colors::RESET;
356 std::string aligned_section =
357 format_aligned_section(time_ms,
static_cast<double>(parent_time), entry.
count, indent_level);
358 os <<
" " << colors.time_color <<
std::setw(40) << std::left << aligned_section << Colors::RESET;
360 double mean_ms = entry.
time_mean / 1000000.0;
362 os <<
" " << entry.
num_threads <<
" threads " << mean_ms <<
"ms average " << stddev_percentage
376 uint64_t parent_time,
378 auto it = aggregated.find(
key);
379 if (it == aggregated.end()) {
385 for (
const auto& [parent_key, entry] : it->second) {
386 if ((indent_level == 0 && parent_key.empty()) || (indent_level > 0 && parent_key == current_parent)) {
387 entry_to_print = &entry;
392 if (!entry_to_print) {
397 print_entry(*entry_to_print, indent_level, is_last, parent_time);
401 if (!printed_in_detail.contains(
key)) {
402 for (
const auto& [child_key, parent_map] : aggregated) {
403 for (
const auto& [parent_key, entry] : parent_map) {
404 if (parent_key ==
key && entry.
time_max >= 500000) {
405 children.push_back(child_key);
410 printed_in_detail.insert(
key);
417 if (
auto it = aggregated.find(
a); it != aggregated.end()) {
418 for (
const auto& [parent_key, entry] : it->second) {
419 if (parent_key ==
key) {
425 if (
auto it = aggregated.find(
b); it != aggregated.end()) {
426 for (
const auto& [parent_key, entry] : it->second) {
427 if (parent_key ==
key) {
433 return time_a > time_b;
437 uint64_t children_total_time = 0;
438 for (
const auto& child_key : children) {
439 if (
auto it = aggregated.find(child_key); it != aggregated.end()) {
440 for (
const auto& [parent_key, entry] : it->second) {
441 if (parent_key ==
key && entry.
time_max >= 500000) {
442 children_total_time += entry.
time_max;
447 uint64_t parent_total_time = entry_to_print->
time_max;
448 bool should_add_other =
false;
449 if (!children.empty() && parent_total_time > 0 && children_total_time < parent_total_time) {
450 uint64_t unaccounted = parent_total_time - children_total_time;
451 double percentage = (
static_cast<double>(unaccounted) /
static_cast<double>(parent_total_time)) * 100.0;
452 should_add_other = percentage > 5.0 && unaccounted > 0;
454 uint64_t other_time = should_add_other ? (parent_total_time - children_total_time) : 0;
456 if (!children.empty() && keys_to_parents[
key].size() > 1) {
457 os << std::string(indent_level * 2,
' ') <<
" ├─ NOTE: Shared children. Can add up to > 100%.\n";
461 for (
size_t i = 0; i < children.size(); ++i) {
462 bool is_last_child = (i == children.size() - 1) && !should_add_other;
463 print_hierarchy(children[i], indent_level + 1, is_last_child, entry_to_print->
time,
key);
467 if (should_add_other && keys_to_parents[
key].size() <= 1) {
469 other_entry.
key =
"(other)";
470 other_entry.
time = other_time;
472 other_entry.
count = 1;
474 print_entry(other_entry, indent_level + 1,
true, parent_total_time);
480 for (
const auto& [
key, parent_map] : aggregated) {
481 auto empty_parent_it = parent_map.find(
"");
482 if (empty_parent_it != parent_map.end() && empty_parent_it->second.time > 0) {
483 roots.push_back(
key);
491 if (
auto it_a = aggregated.find(
a); it_a != aggregated.end()) {
492 if (
auto parent_it = it_a->second.find(
""); parent_it != it_a->second.end()) {
493 time_a = parent_it->second.time_max;
496 if (
auto it_b = aggregated.find(
b); it_b != aggregated.end()) {
497 if (
auto parent_it = it_b->second.find(
""); parent_it != it_b->second.end()) {
498 time_b = parent_it->second.time_max;
501 return time_a > time_b;
505 for (
size_t i = 0; i < roots.size(); ++i) {
506 print_hierarchy(roots[i], 0, i == roots.size() - 1, 0,
"");
510 print_separator(os,
false);
514 for (
const auto& [
key, _] : aggregated) {
515 unique_funcs.insert(
key);
517 size_t unique_functions_count = unique_funcs.size();
519 uint64_t shared_count = 0;
520 for (
const auto& [
key, parents] : keys_to_parents) {
521 if (parents.size() > 1) {
526 uint64_t total_time = 0;
527 for (
const auto& [_, parent_map] : aggregated) {
528 if (
auto it = parent_map.find(
""); it != parent_map.end()) {
529 total_time =
std::max(
static_cast<size_t>(total_time), it->second.time_max);
533 uint64_t total_calls = 0;
534 for (
const auto& [_, parent_map] : aggregated) {
535 for (
const auto& [__, entry] : parent_map) {
536 total_calls += entry.
count;
540 double total_time_ms =
static_cast<double>(total_time) / 1000000.0;
542 os <<
" " << Colors::BOLD <<
"Total: " << Colors::RESET << Colors::MAGENTA << unique_functions_count
543 <<
" functions" << Colors::RESET;
544 if (shared_count > 0) {
545 os <<
" (" << Colors::RED << shared_count <<
" shared" << Colors::RESET <<
")";
547 os <<
", " << Colors::GREEN << total_calls <<
" measurements" << Colors::RESET <<
", " << Colors::YELLOW;
548 if (total_time_ms >= 1000.0) {
556 print_separator(os,
true);