VIPRA Documentation
Loading...
Searching...
No Matches
performance_testing.hpp
1#pragma once
2
3#include <cassert>
4#include <chrono>
5#include <iostream>
6#include <map>
7#include <stdexcept>
8#include <string>
9#include <string_view>
10#include <type_traits>
11#include <utility>
12#include <vector>
13
14#include "vipra/util/clock.hpp"
15
16namespace VIPRA {
17
18#ifndef VIPRA_PERF_TESTING
19
20class Perf {
21 public:
22 static void add_call(std::string_view /*unused*/) {}
23 static void start(std::string_view /*unused*/) {}
24 static void stop(std::string_view /*unused*/) {}
25 static auto report() -> std::string { return ""; }
26
27 class FunctionTimer {};
28};
29
30#else
31
32class Perf {
33 public:
39 static void add_call(std::string_view name) noexcept
40 {
41 if ( ! std::is_constant_evaluated() ) {
42 if ( get_calls().find(name) == get_calls().end() ) {
43 get_calls().emplace(std::string(name), 0);
44 }
45 (get_calls().find(name))->second++;
46 }
47 }
48
54 static void start(std::string_view name) noexcept
55 {
56 if ( ! std::is_constant_evaluated() ) {
57 if ( get_clocks().find(name) == get_clocks().end() ) {
58 get_clocks().emplace(std::string(name), Util::Clock<Util::micro>{});
59 }
60
61 (get_clocks().find(name))->second.start();
62 }
63 }
64
70 static void stop(std::string_view name) noexcept
71 {
72 if ( ! std::is_constant_evaluated() ) {
73 auto time = (get_clocks().find(name))->second.stop();
74 if ( get_timings().find(name) == get_timings().end() ) {
75 get_timings().emplace(std::string(name), Util::micro{0});
76 }
77
78 (get_timings().find(name))->second += time;
79 }
80 }
81
87 static auto report() -> std::string
88 {
89 std::string report{"\n\nFunction Call Counts:\n"};
90
91 auto sortedCalls = sort_calls();
92 for ( auto const& [name, count] : sortedCalls ) {
93 report += "\t" + name + ": " + std::to_string(count) + " calls\n";
94 }
95
96 report += "\n\nTimings:\n";
97
98 auto sortedTimings = sort_timings();
99 for ( auto const& [name, time] : sortedTimings ) {
100 auto percall = time / get_calls().at(name);
101 auto percallMilli = std::chrono::duration_cast<Util::milli>(percall);
102 percall -= percallMilli;
103 auto percallMicro = std::chrono::duration_cast<Util::micro>(percall);
104
105 auto copy = time;
106 auto seconds = std::chrono::duration_cast<Util::seconds>(copy);
107 copy -= seconds;
108 auto milli = std::chrono::duration_cast<Util::milli>(copy);
109 copy -= milli;
110 auto micro = std::chrono::duration_cast<Util::micro>(copy);
111
112 report += "\t" + name + ":\n\t\tTotal Time: " + std::to_string(seconds.count()) +
113 "s " + std::to_string(milli.count()) + "ms " +
114 std::to_string(micro.count()) + "us\n";
115 report += "\t\tPer Call: " + std::to_string(percallMilli.count()) + "ms " +
116 std::to_string(percallMicro.count()) + "us\n";
117 }
118 return report;
119 return "";
120 }
121
126 class FunctionTimer {
127 public:
128 FunctionTimer(FunctionTimer const&) = delete;
129 FunctionTimer(FunctionTimer&&) noexcept = delete;
130 auto operator=(FunctionTimer const&) -> FunctionTimer& = delete;
131 auto operator=(FunctionTimer&&) -> FunctionTimer& = delete;
132 explicit FunctionTimer(std::string_view name) noexcept : _name(name)
133 {
134 Perf::add_call(_name);
135 Perf::start(_name);
136 }
137
138 ~FunctionTimer() noexcept { Perf::stop(_name); }
139
140 private:
141 std::string_view _name;
142 };
143
144 private:
145 [[nodiscard]] static auto get_calls() -> std::map<std::string, size_t, std::less<>>&
146 {
147 static std::map<std::string, size_t, std::less<>> calls;
148 return calls;
149 }
150
151 [[nodiscard]] static auto get_timings()
152 -> std::map<std::string, Util::micro, std::less<>>&
153 {
154 static std::map<std::string, Util::micro, std::less<>> timings;
155 return timings;
156 }
157
158 [[nodiscard]] static auto get_clocks()
159 -> std::map<std::string, Util::Clock<Util::micro>, std::less<>>&
160 {
161 static std::map<std::string, Util::Clock<Util::micro>, std::less<>> timings;
162 return timings;
163 }
164
165 [[nodiscard]] static auto sort_calls() -> std::vector<std::pair<std::string, size_t>>
166 {
167 const auto cmp = [](auto const& left, auto const& right) -> bool {
168 return left.second > right.second;
169 };
170
171 std::vector<std::pair<std::string, size_t>> retVal;
172 for ( auto const& val : get_calls() ) {
173 retVal.emplace_back(val);
174 }
175
176 std::sort(retVal.begin(), retVal.end(), cmp);
177
178 return retVal;
179 }
180
181 [[nodiscard]] static auto sort_timings()
182 -> std::vector<std::pair<std::string, Util::micro>>
183 {
184 const auto cmp = [](auto const& left, auto const& right) -> bool {
185 return left.second > right.second;
186 };
187
188 std::vector<std::pair<std::string, Util::micro>> retVal;
189 for ( auto const& val : get_timings() ) {
190 retVal.emplace_back(val);
191 }
192
193 std::sort(retVal.begin(), retVal.end(), cmp);
194
195 return retVal;
196 }
197};
198
199#endif
200} // namespace VIPRA
Definition performance_testing.hpp:27
Definition performance_testing.hpp:20