-
Notifications
You must be signed in to change notification settings - Fork 14
/
microbench.h
129 lines (112 loc) · 3.98 KB
/
microbench.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include "systemtime.h"
#include <cstdint>
#include <cassert>
#include <utility>
#include <algorithm>
#include <numeric>
#include <cmath>
namespace moodycamel
{
struct stats_t
{
stats_t(double* results, std::size_t count)
{
std::sort(results, results + count);
_min = results[0];
_max = results[count - 1];
if (count == 1) {
_q[0] = _q[1] = _q[2] = results[0];
_avg = results[0];
_variance = 0;
return;
}
// Kahan summation to reduce error (see http://en.wikipedia.org/wiki/Kahan_summation_algorithm)
double sum = 0;
double c = 0; // Error last time around
for (std::size_t i = 0; i != count; ++i) {
auto y = results[i] - c;
auto t = sum + y;
c = (t - sum) - y;
sum = t;
}
_avg = sum / count;
// Calculate unbiased (corrected) sample variance
sum = 0, c = 0;
for (std::size_t i = 0; i != count; ++i) {
auto y = (results[i] - _avg) * (results[i] - _avg) - c;
auto t = sum + y;
c = (t - sum) - y;
sum = t;
}
_variance = sum / (count - 1);
// See Method 3 here: http://en.wikipedia.org/wiki/Quartile
_q[1] = (count & 1) == 0 ? (results[count / 2 - 1] + results[count / 2]) * 0.5 : results[count / 2];
if ((count & 1) == 0) {
_q[0] = (count & 3) == 0 ? (results[count / 4 - 1] + results[count / 4]) * 0.5 : results[count / 4];
_q[2] = (count & 3) == 0 ? (results[count / 2 + count / 4 - 1] + results[count / 2 + count / 4]) * 0.5 : results[count / 2 + count / 4];
}
else if ((count & 3) == 1) {
_q[0] = results[count / 4 - 1] * 0.25 + results[count / 4] * 0.75;
_q[2] = results[count / 4 * 3] * 0.75 + results[count / 4 * 3 + 1] * 0.25;
}
else { // (count & 3) == 3
_q[0] = results[count / 4] * 0.75 + results[count / 4 + 1] * 0.25;
_q[2] = results[count / 4 * 3 + 1] * 0.25 + results[count / 4 * 3 + 2] * 0.75;
}
}
inline double min() const { return _min; }
inline double max() const { return _max; }
inline double range() const { return _max - _min; }
inline double avg() const { return _avg; }
inline double variance() const { return _variance; }
inline double stddev() const { return std::sqrt(_variance); }
inline double median() const { return _q[1]; }
inline double q1() const { return _q[0]; }
inline double q2() const { return _q[1]; }
inline double q3() const { return _q[2]; }
inline double q(std::size_t which) const { assert(which < 4 && which > 0); return _q[which - 1]; }
private:
double _min;
double _max;
double _q[3];
double _avg;
double _variance;
};
// Times how long it takes to run a given function for a given number of iterations; this
// timing process is repeated for a given number of test runs; various statistics of the
// of timing results are returned in a `stats_t` object.
template<typename TFunc>
stats_t microbench_stats(TFunc&& func, std::uint64_t iterations = 1, std::uint32_t testRuns = 100, bool returnTimePerIteration = true)
{
assert(testRuns >= 1);
assert(iterations >= 1);
auto results = new double[testRuns];
for (std::uint32_t i = 0; i < testRuns; ++i) {
auto start = getSystemTime();
for (std::uint64_t j = 0; j < iterations; ++j) {
func();
}
results[i] = getTimeDelta(start);
if (returnTimePerIteration) {
results[i] /= iterations;
}
}
double fastest = results[0];
for (std::uint32_t i = 1; i < testRuns; ++i) {
if (results[i] < fastest) {
fastest = results[i];
}
}
stats_t stats(results, testRuns);
delete[] results;
return stats;
}
// Times how long it takes to run a given function for a given number of iterations; this
// timing process is repeated for a given number of test runs; the fastest of the runs is
// selected, and its time returned (in milliseconds).
template<typename TFunc>
double microbench(TFunc&& func, std::uint64_t iterations = 1, std::uint32_t testRuns = 100, bool returnTimePerIteration = true)
{
return microbench_stats(std::forward<TFunc>(func), iterations, testRuns, returnTimePerIteration).min();
}
}