Line data Source code
1 : #ifndef HASHBROWNS_TIMER_H
2 : #define HASHBROWNS_TIMER_H
3 :
4 : #include <chrono>
5 : #include <vector>
6 : #include <string>
7 : #include <algorithm>
8 : #include <numeric>
9 : #include <cmath>
10 : #include <functional>
11 : #include <thread>
12 :
13 : namespace hashbrowns {
14 :
15 : /**
16 : * @brief High-precision timer for benchmarking operations
17 : *
18 : * This class provides microsecond-precision timing with statistical analysis
19 : * capabilities. It's designed to handle the challenges of accurate performance
20 : * measurement including warm-up periods, outlier detection, and statistical
21 : * aggregation across multiple runs.
22 : */
23 : class Timer {
24 : public:
25 : using clock_type = std::chrono::high_resolution_clock;
26 : using time_point = clock_type::time_point;
27 : using duration = std::chrono::nanoseconds;
28 :
29 : /**
30 : * @brief Statistical summary of timing measurements
31 : */
32 : struct Statistics {
33 : double mean_ns; ///< Mean time in nanoseconds
34 : double median_ns; ///< Median time in nanoseconds
35 : double std_dev_ns; ///< Standard deviation in nanoseconds
36 : double min_ns; ///< Minimum time in nanoseconds
37 : double max_ns; ///< Maximum time in nanoseconds
38 : size_t sample_count; ///< Number of samples
39 : double outlier_ratio; ///< Ratio of outliers removed
40 :
41 : // Convenience accessors for different time units
42 : double mean_us() const { return mean_ns / 1000.0; }
43 2 : double mean_ms() const { return mean_ns / 1000000.0; }
44 : double median_us() const { return median_ns / 1000.0; }
45 2 : double median_ms() const { return median_ns / 1000000.0; }
46 : double std_dev_us() const { return std_dev_ns / 1000.0; }
47 2 : double std_dev_ms() const { return std_dev_ns / 1000000.0; }
48 : };
49 :
50 : /**
51 : * @brief Construct timer with optional outlier detection
52 : * @param remove_outliers If true, automatically remove statistical outliers
53 : * @param outlier_threshold Z-score threshold for outlier detection (default 2.0)
54 : */
55 : explicit Timer(bool remove_outliers = true, double outlier_threshold = 2.0);
56 :
57 : /**
58 : * @brief Start timing an operation
59 : */
60 : void start();
61 :
62 : /**
63 : * @brief Stop timing and record the duration
64 : * @return Duration of the timed operation in nanoseconds
65 : */
66 : duration stop();
67 :
68 : /**
69 : * @brief Add a pre-measured duration to the sample set
70 : * @param d Duration to add
71 : */
72 : void add_sample(duration d);
73 :
74 : /**
75 : * @brief Get the duration of the last measured operation
76 : * @return Last measured duration in nanoseconds
77 : */
78 1 : duration last_duration() const { return last_duration_; }
79 :
80 : /**
81 : * @brief Clear all recorded samples
82 : */
83 : void reset();
84 :
85 : /**
86 : * @brief Get statistical summary of all recorded samples
87 : * @return Statistics object with aggregated timing data
88 : */
89 : Statistics get_statistics() const;
90 :
91 : /**
92 : * @brief Get all raw sample durations
93 : * @return Vector of all recorded durations
94 : */
95 : const std::vector<duration>& get_samples() const { return samples_; }
96 :
97 : /**
98 : * @brief Get number of recorded samples
99 : */
100 2 : size_t sample_count() const { return samples_.size(); }
101 :
102 : /**
103 : * @brief Check if timer is currently running
104 : */
105 1 : bool is_running() const { return is_running_; }
106 :
107 : /**
108 : * @brief Perform warm-up runs to stabilize CPU caches and frequency
109 : * @param warmup_count Number of warm-up iterations
110 : * @param operation Function to execute during warm-up
111 : */
112 : template<typename Func>
113 2 : void warmup(size_t warmup_count, Func&& operation) {
114 12 : for (size_t i = 0; i < warmup_count; ++i) {
115 10 : operation();
116 : }
117 : // Give CPU time to settle after warm-up
118 2 : std::this_thread::sleep_for(std::chrono::milliseconds(1));
119 2 : }
120 :
121 : /**
122 : * @brief Time a function multiple times and collect statistics
123 : * @param operation Function to time
124 : * @param iterations Number of iterations to run
125 : * @param warmup_runs Number of warm-up runs before timing
126 : * @return Statistics for the timed operations
127 : */
128 : template<typename Func>
129 2 : Statistics time_operation(Func&& operation, size_t iterations, size_t warmup_runs = 3) {
130 2 : reset();
131 :
132 : // Warm-up phase
133 2 : if (warmup_runs > 0) {
134 2 : warmup(warmup_runs, operation);
135 : }
136 :
137 : // Actual timing
138 22 : for (size_t i = 0; i < iterations; ++i) {
139 20 : start();
140 20 : operation();
141 20 : stop();
142 : }
143 :
144 2 : return get_statistics();
145 : }
146 :
147 : private:
148 : time_point start_time_;
149 : duration last_duration_;
150 : std::vector<duration> samples_;
151 : bool is_running_;
152 : bool remove_outliers_;
153 : double outlier_threshold_;
154 :
155 : /**
156 : * @brief Remove statistical outliers from samples
157 : * @param data Vector of durations to filter
158 : * @return Filtered vector with outliers removed
159 : */
160 : std::vector<duration> remove_outliers(const std::vector<duration>& data) const;
161 :
162 : /**
163 : * @brief Calculate Z-score for a value in a dataset
164 : */
165 : double calculate_zscore(double value, double mean, double std_dev) const;
166 : };
167 :
168 : /**
169 : * @brief RAII scope timer for automatic timing of code blocks
170 : *
171 : * This class automatically starts timing on construction and stops on destruction,
172 : * making it perfect for timing scoped operations.
173 : *
174 : * Example usage:
175 : * @code
176 : * {
177 : * ScopeTimer timer("Operation Name");
178 : * // ... code to time ...
179 : * } // timer automatically stops and optionally prints result
180 : * @endcode
181 : */
182 : class ScopeTimer {
183 : public:
184 : /**
185 : * @brief Construct scope timer with optional name and auto-print
186 : * @param name Optional name for the operation being timed
187 : * @param auto_print If true, print timing result on destruction
188 : */
189 : explicit ScopeTimer(const std::string& name = "", bool auto_print = true);
190 :
191 : /**
192 : * @brief Destructor - automatically stops timing
193 : */
194 : ~ScopeTimer();
195 :
196 : /**
197 : * @brief Get the elapsed time so far (without stopping)
198 : * @return Current elapsed time
199 : */
200 : Timer::duration elapsed() const;
201 :
202 : /**
203 : * @brief Manually stop the timer and get final duration
204 : * @return Total elapsed time
205 : */
206 : Timer::duration stop();
207 :
208 : private:
209 : Timer::time_point start_time_;
210 : std::string operation_name_;
211 : bool auto_print_;
212 : bool stopped_;
213 : };
214 :
215 : /**
216 : * @brief Benchmark runner for comparative performance analysis
217 : *
218 : * This class facilitates running multiple benchmark tests and comparing
219 : * their performance characteristics.
220 : */
221 : class BenchmarkRunner {
222 : public:
223 : struct BenchmarkResult {
224 : std::string name;
225 : Timer::Statistics stats;
226 : double operations_per_second;
227 : size_t data_size;
228 : };
229 :
230 : /**
231 : * @brief Add a benchmark test
232 : * @param name Name identifier for the benchmark
233 : * @param operation Function to benchmark
234 : * @param iterations Number of times to run the operation
235 : * @param data_size Size of data being processed (for throughput calculation)
236 : */
237 : template<typename Func>
238 2 : void add_benchmark(const std::string& name, Func&& operation,
239 : size_t iterations, size_t data_size = 0) {
240 2 : Timer timer(true, 2.0); // Remove outliers with 2-sigma threshold
241 2 : auto stats = timer.time_operation(std::forward<Func>(operation), iterations, 5);
242 :
243 2 : double ops_per_sec = 0.0;
244 2 : if (stats.mean_ns > 0) {
245 2 : ops_per_sec = 1e9 / stats.mean_ns; // Convert to operations per second
246 : }
247 :
248 4 : results_.push_back({name, stats, ops_per_sec, data_size});
249 6 : }
250 :
251 : /**
252 : * @brief Get all benchmark results
253 : */
254 : const std::vector<BenchmarkResult>& get_results() const { return results_; }
255 :
256 : /**
257 : * @brief Clear all results
258 : */
259 : void clear() { results_.clear(); }
260 :
261 : /**
262 : * @brief Print formatted benchmark comparison
263 : */
264 : void print_comparison() const;
265 :
266 : /**
267 : * @brief Export results to CSV format
268 : * @param filename Output CSV file path
269 : */
270 : void export_csv(const std::string& filename) const;
271 :
272 : private:
273 : std::vector<BenchmarkResult> results_;
274 : };
275 :
276 : /**
277 : * @brief Convenience macro for scope timing
278 : */
279 : #define SCOPE_TIMER(name) hashbrowns::ScopeTimer _scope_timer(name)
280 :
281 : /**
282 : * @brief Convenience macro for scope timing with custom auto-print setting
283 : */
284 : #define SCOPE_TIMER_PRINT(name, print) hashbrowns::ScopeTimer _scope_timer(name, print)
285 :
286 : } // namespace hashbrowns
287 :
288 : #endif // HASHBROWNS_TIMER_H
|