HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_time_it.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vector>
4#include <chrono>
5
6#include "metrics/hart_metrics_common.hpp" // ReducerResultType
7
8namespace hart
9{
10
11/// @brief Measures execution time of a code block over multiple iterations.
12///
13/// This macro runs the provided code block @p numRuns times and records the duration
14/// of each iteration into a local `hart::TimeIt` instance.
15///
16/// The recorder object is declared with the name provided in @p timeItInstanceName and can
17/// be queried after the block using reducers such as `hart::mean()`, `hart::max()`,
18/// `hart::percentile()` etc (see @ref Reducers).
19///
20/// To query the result, call `hart::TimeIt::result()` on the created `TimeIt` instance.
21///
22/// @par Example
23/// @code
24/// HART_TIME_IT (myTimeIt, 100)
25/// {
26/// doSomething();
27/// };
28///
29/// const double t = myTimeIt.result (hart::mean());
30/// @endcode
31///
32/// @param timeItInstanceName Name of the `hart::TimeIt` instance to create.
33/// Pick any valid name, and the object with such name will be created by the macro.
34/// @param numRuns Number of iterations to run the block.
35///
36/// @warning This macro expands to multiple statements and behaves like a control
37/// statement. It must always be followed by a block. If nested, it must always
38/// be surrounded by braces. In particular, do NOT use it like this:
39/// @code
40/// if (someCondition)
41/// HART_TIME_IT (myTimeIt, 100) { doSomething(); } // Incorrect! It's not a single statement.
42/// else
43/// doSomethingElse();
44/// @endcode
45/// Instead, always use braces:
46/// @code
47/// if (cond)
48/// {
49/// HART_TIME_IT (myTimeIt, 100)
50/// {
51/// doSomething();
52/// };
53/// }
54/// else
55/// {
56/// doSomethingElse();
57/// }
58/// @endcode
59///
60/// @note If the code inside the block throws an exception, the current iteration
61/// will still be recorded (via RAII), but may include stack unwinding time.
62/// Subsequent iterations will not run.
63/// @ingroup Utilities
64#define HART_TIME_IT(timeItInstanceName, numRuns)
65 ::hart::TimeIt timeItInstanceName (numRuns);
66 for (size_t HART_timeItCurrentRun = 0; HART_timeItCurrentRun < (numRuns); ++HART_timeItCurrentRun)
67 for (bool HART_timeItRunOnce = true; HART_timeItRunOnce; HART_timeItRunOnce = false)
68 for (::hart::TimeItSample HART_sample (timeItInstanceName); HART_timeItRunOnce; HART_timeItRunOnce = false)
69
70/// @brief Helper class for measuring duration of some block of code over multiple runs
71/// @details Don't instantiate this class directly, it's intended to be created via `HART_TIME_IT()` macro
72/// @ingroup Utilities
73class TimeIt
74{
75public:
76 /// @brief Creates a new TimeIt instance
77 /// @param numRuns Number of times to run the measured block of code
78 TimeIt (size_t numRuns)
79 {
80 m_observedDurationsSeconds.reserve (numRuns);
81 }
82
83 /// @brief Record an observed duration
84 /// @param duration Observed code block duration in seconds
85 /// @private
86 void add (double durationSeconds)
87 {
88 m_observedDurationsSeconds.push_back (durationSeconds);
89 }
90
91 /// @brief Get the result of the timing measurement
92 /// @details Internally, it stores a list of durations for each run. You're expected to choose
93 /// what to do with this value by providing a reducer instance like `hart::mean()`, `hart::max()`.
94 /// If you want to get the raw list of values, you can use `hart::collect()`.
95 /// @param reducer Callable object that accepts a pair of std::vector iterators (begin and end).
96 /// You'll probably want to use one of the HART's built-in reducers, see @ref Reducers.
97 template <typename ReducerType>
98 auto result (ReducerType&& reducer) const
99 -> ReducerResultType<ReducerType, std::vector<double>::const_iterator>
100 {
101 return reducer (m_observedDurationsSeconds.begin(), m_observedDurationsSeconds.end());
102 }
103
104private:
105 std::vector<double> m_observedDurationsSeconds;
106};
107
108/// @brief A helper class to measure time duration of some block of code
109/// @private
110struct TimeItSample
111{
112 using clock = std::chrono::steady_clock;
113
114 TimeIt& m_timeIt;
115 clock::time_point m_start;
116
117 explicit TimeItSample (TimeIt& timeIt) :
118 m_timeIt (timeIt),
119 m_start (clock::now())
120 {
121 }
122
123 TimeItSample (const TimeItSample&) = delete;
124 TimeItSample (TimeItSample&&) = delete;
125 TimeItSample& operator= (const TimeItSample&) = delete;
126 TimeItSample& operator= (TimeItSample&&) = delete;
127
128 ~TimeItSample()
129 {
130 const clock::time_point end = clock::now();
131
132 using std::chrono::duration_cast;
133 using DurationDoubleType = std::chrono::duration<double>;
134 const double durationSeconds = duration_cast<DurationDoubleType> (end - m_start).count();
135
136 m_timeIt.add (durationSeconds);
137 }
138};
139
140} // namespace hart
Helper class for measuring duration of some block of code over multiple runs.
TimeIt(size_t numRuns)
Creates a new TimeIt instance.
auto result(ReducerType &&reducer) const -> ReducerResultType< ReducerType, std::vector< double >::const_iterator >
Get the result of the timing measurement.