MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
profile.cpp
Go to the documentation of this file.
1#include "mim/util/profile.h"
2
3#include <algorithm>
4#include <format>
5#include <print>
6
7#include <absl/container/btree_map.h>
8#include <absl/container/flat_hash_map.h>
9
10using namespace std::literals;
11
12namespace mim {
13
14namespace {
15double ms(Profiler::Duration d) {
16 return std::chrono::duration_cast<std::chrono::duration<double, std::milli>>(d).count();
17}
18
19double us(Profiler::Duration d) {
20 return std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(d).count();
21}
22
23/// Escapes @p str for inclusion in a JSON string literal.
24std::string json_escape(std::string_view str) {
25 std::string res;
26 for (auto c : str) {
27 switch (c) {
28 case '"': res += "\\\""; break;
29 case '\\': res += "\\\\"; break;
30 case '\n': res += "\\n"; break;
31 case '\t': res += "\\t"; break;
32 case '\r': res += "\\r"; break;
33 default: res += c;
34 }
35 }
36 return res;
37}
38} // namespace
39
40Vector<Profiler::Duration> Profiler::children_durations() const {
41 auto children = Vector<Duration>(spans_.size(), Duration::zero());
42 for (const auto& span : spans_)
43 if (span.parent != No_Parent) children[span.parent] += span.elapsed();
44 return children;
45}
46
47void Profiler::summary(std::ostream& os) const {
48 struct Agg {
49 Duration total = Duration::zero();
50 Duration self = Duration::zero();
51 size_t count = 0;
52 };
53
54 auto children = children_durations();
55 auto by_name = absl::flat_hash_map<std::string_view, Agg>();
56 auto total = Duration::zero();
57
58 for (size_t i = 0, e = spans_.size(); i != e; ++i) {
59 auto self = spans_[i].elapsed() - children[i];
60 auto& agg = by_name[spans_[i].name];
61 agg.total += spans_[i].elapsed();
62 agg.self += self;
63 ++agg.count;
64 total += self;
65 }
66
67 auto ordered = Vector<std::pair<std::string_view, Agg>>(by_name.begin(), by_name.end());
68 std::ranges::sort(ordered, [](const auto& a, const auto& b) { return a.second.total > b.second.total; });
69
70 std::println(os, "Phase profile (flat):");
71 std::println(os, "{:>12} {:>12} {:>7} {:>6} {}", "total[ms]", "self[ms]", "self[%]", "#runs", "phase");
72 for (const auto& [name, agg] : ordered) {
73 auto percent = total > Duration::zero() ? 100.0 * ms(agg.self) / ms(total) : 0.0;
74 std::println(os, "{:>12.3f} {:>12.3f} {:>6.1f}% {:>6} {}", ms(agg.total), ms(agg.self), percent, agg.count,
75 name);
76 }
77 std::println(os, "{:>12.3f} {:>12.3f} {:>6.1f}% {:>6} {}", ms(total), ms(total), 100.0, spans_.size(), "TOTAL");
78
79 // aggregate custom counters by (phase, counter) - deterministically ordered
80 auto counters = absl::btree_map<std::pair<std::string_view, std::string_view>, uint64_t>();
81 for (const auto& span : spans_)
82 for (const auto& [key, val] : span.counters)
83 counters[{span.name, key}] += val;
84
85 if (!counters.empty()) {
86 std::println(os, "");
87 std::println(os, "Phase counters:");
88 std::println(os, "{:>12} {}", "value", "phase: counter");
89 for (const auto& [name_key, val] : counters)
90 std::println(os, "{:>12} {}: {}", val, name_key.first, name_key.second);
91 }
92}
93
94void Profiler::tree(std::ostream& os) const {
95 auto children = children_durations();
96 auto total = Duration::zero();
97 for (const auto& span : spans_)
98 if (span.parent == No_Parent) total += span.elapsed();
99
100 std::println(os, "Phase profile (tree):");
101 std::println(os, "{:>12} {:>12} {:>7} {}", "total[ms]", "self[ms]", "tot[%]", "phase");
102 for (size_t i = 0, e = spans_.size(); i != e; ++i) {
103 const auto& span = spans_[i];
104 auto self = span.elapsed() - children[i];
105 auto percent = total > Duration::zero() ? 100.0 * ms(span.elapsed()) / ms(total) : 0.0;
106 auto counters = std::string();
107 for (const auto& [key, val] : span.counters)
108 counters += std::format("{}{}={}", counters.empty() ? " [" : " ", key, val);
109 if (!counters.empty()) counters += "]";
110 std::println(os, "{:>12.3f} {:>12.3f} {:>6.1f}% {:>{}}{}{}", ms(span.elapsed()), ms(self), percent, "",
111 span.depth * 2, span.name, counters);
112 }
113 std::println(os, "{:>12.3f} {:>12} {:>6.1f}% {}", ms(total), "", 100.0, "TOTAL");
114}
115
116void Profiler::chrome_trace(std::ostream& os) const {
117 auto origin = spans_.empty() ? Clock::time_point{} : spans_.front().start;
118
119 std::println(os, "{{\"displayTimeUnit\":\"ms\",\"traceEvents\":[");
120 for (size_t i = 0, e = spans_.size(); i != e; ++i) {
121 const auto& span = spans_[i];
122 auto args = std::string();
123 for (const auto& [key, val] : span.counters)
124 args += std::format("{}\"{}\":{}", args.empty() ? ",\"args\":{"sv : ","sv, json_escape(key), val);
125 if (!args.empty()) args += "}";
126 std::println(
127 os,
128 "{{\"name\":\"{}\",\"cat\":\"phase\",\"ph\":\"X\",\"pid\":1,\"tid\":1,\"ts\":{:.3f},\"dur\":{:.3f}{}}}{}",
129 json_escape(span.name), us(span.start - origin), us(span.elapsed()), args, i + 1 == e ? "" : ",");
130 }
131 std::println(os, "]}}");
132}
133
134} // namespace mim
void chrome_trace(std::ostream &) const
Dumps all Spans as Chrome Trace Event Format JSON.
Definition profile.cpp:116
void count(std::string_view key, uint64_t n=1)
Adds n to counter key of the currently running Span; no-op if no Span is running or n is 0.
Definition profile.h:67
void summary(std::ostream &) const
Definition profile.cpp:47
static constexpr size_t No_Parent
Definition profile.h:29
void tree(std::ostream &) const
Prints the Spans as an indented tree, preserving the order in which Phasees ran.
Definition profile.cpp:94
Clock::duration Duration
Definition profile.h:27
This is a thin wrapper for absl::InlinedVector<T, N, A> which is a drop-in replacement for std::vecto...
Definition vector.h:18
Definition ast.h:14
Vector(I, I, A=A()) -> Vector< typename std::iterator_traits< I >::value_type, Default_Inlined_Size< typename std::iterator_traits< I >::value_type >, A >