1#ifndef BENCHMARK_STAT_H_
2#define BENCHMARK_STAT_H_
3
4#include <cmath>
5#include <limits>
6#include <ostream>
7#include <type_traits>
8
9namespace benchmark {
10
11template <typename VType, typename NumType>
12class Stat1;
13
14template <typename VType, typename NumType>
15class Stat1MinMax;
16
17typedef Stat1<float, int64_t> Stat1_f;
18typedef Stat1<double, int64_t> Stat1_d;
19typedef Stat1MinMax<float, int64_t> Stat1MinMax_f;
20typedef Stat1MinMax<double, int64_t> Stat1MinMax_d;
21
22template <typename VType>
23class Vector2;
24template <typename VType>
25class Vector3;
26template <typename VType>
27class Vector4;
28
29template <typename VType, typename NumType>
30class Stat1 {
31 public:
32 typedef Stat1<VType, NumType> Self;
33
34 Stat1() { Clear(); }
35 // Create a sample of value dat and weight 1
36 explicit Stat1(const VType &dat) {
37 sum_ = dat;
38 sum_squares_ = Sqr(dat);
39 numsamples_ = 1;
40 }
41 // Create statistics for all the samples between begin (included)
42 // and end(excluded)
43 explicit Stat1(const VType *begin, const VType *end) {
44 Clear();
45 for (const VType *item = begin; item < end; ++item) {
46 (*this) += Stat1(*item);
47 }
48 }
49 // Create a sample of value dat and weight w
50 Stat1(const VType &dat, const NumType &w) {
51 sum_ = w * dat;
52 sum_squares_ = w * Sqr(dat);
53 numsamples_ = w;
54 }
55 // Copy operator
56 Stat1(const Self &stat) {
57 sum_ = stat.sum_;
58 sum_squares_ = stat.sum_squares_;
59 numsamples_ = stat.numsamples_;
60 }
61
62 void Clear() {
63 numsamples_ = NumType();
64 sum_squares_ = sum_ = VType();
65 }
66
67 Self &operator=(const Self &stat) {
68 sum_ = stat.sum_;
69 sum_squares_ = stat.sum_squares_;
70 numsamples_ = stat.numsamples_;
71 return (*this);
72 }
73 // Merge statistics from two sample sets.
74 Self &operator+=(const Self &stat) {
75 sum_ += stat.sum_;
76 sum_squares_ += stat.sum_squares_;
77 numsamples_ += stat.numsamples_;
78 return (*this);
79 }
80 // The operation opposite to +=
81 Self &operator-=(const Self &stat) {
82 sum_ -= stat.sum_;
83 sum_squares_ -= stat.sum_squares_;
84 numsamples_ -= stat.numsamples_;
85 return (*this);
86 }
87 // Multiply the weight of the set of samples by a factor k
88 Self &operator*=(const VType &k) {
89 sum_ *= k;
90 sum_squares_ *= k;
91 numsamples_ *= k;
92 return (*this);
93 }
94
95 // Merge statistics from two sample sets.
96 Self operator+(const Self &stat) const { return Self(*this) += stat; }
97
98 // The operation opposite to +
99 Self operator-(const Self &stat) const { return Self(*this) -= stat; }
100
101 // Multiply the weight of the set of samples by a factor k
102 Self operator*(const VType &k) const { return Self(*this) *= k; }
103
104 // Return the total weight of this sample set
105 NumType numSamples() const { return numsamples_; }
106
107 // Return the sum of this sample set
108 VType Sum() const { return sum_; }
109
110 // Return the mean of this sample set
111 VType Mean() const {
112 if (numsamples_ == 0) return VType();
113 return sum_ * (1.0 / numsamples_);
114 }
115
116 // Return the mean of this sample set and compute the standard deviation at
117 // the same time.
118 VType Mean(VType *stddev) const {
119 if (numsamples_ == 0) return VType();
120 VType mean = sum_ * (1.0 / numsamples_);
121 if (stddev) {
122 VType avg_squares = sum_squares_ * (1.0 / numsamples_);
123 *stddev = Sqrt(avg_squares - Sqr(mean));
124 }
125 return mean;
126 }
127
128 // Return the standard deviation of the sample set
129 VType StdDev() const {
130 if (numsamples_ == 0) return VType();
131 VType mean = Mean();
132 VType avg_squares = sum_squares_ * (1.0 / numsamples_);
133 return Sqrt(avg_squares - Sqr(mean));
134 }
135
136 private:
137 static_assert(std::is_integral<NumType>::value &&
138 !std::is_same<NumType, bool>::value,
139 "NumType must be an integral type that is not bool.");
140 // Let i be the index of the samples provided (using +=)
141 // and weight[i],value[i] be the data of sample #i
142 // then the variables have the following meaning:
143 NumType numsamples_; // sum of weight[i];
144 VType sum_; // sum of weight[i]*value[i];
145 VType sum_squares_; // sum of weight[i]*value[i]^2;
146
147 // Template function used to square a number.
148 // For a vector we square all components
149 template <typename SType>
150 static inline SType Sqr(const SType &dat) {
151 return dat * dat;
152 }
153
154 template <typename SType>
155 static inline Vector2<SType> Sqr(const Vector2<SType> &dat) {
156 return dat.MulComponents(dat);
157 }
158
159 template <typename SType>
160 static inline Vector3<SType> Sqr(const Vector3<SType> &dat) {
161 return dat.MulComponents(dat);
162 }
163
164 template <typename SType>
165 static inline Vector4<SType> Sqr(const Vector4<SType> &dat) {
166 return dat.MulComponents(dat);
167 }
168
169 // Template function used to take the square root of a number.
170 // For a vector we square all components
171 template <typename SType>
172 static inline SType Sqrt(const SType &dat) {
173 // Avoid NaN due to imprecision in the calculations
174 if (dat < 0) return 0;
175 return sqrt(dat);
176 }
177
178 template <typename SType>
179 static inline Vector2<SType> Sqrt(const Vector2<SType> &dat) {
180 // Avoid NaN due to imprecision in the calculations
181 return Max(dat, Vector2<SType>()).Sqrt();
182 }
183
184 template <typename SType>
185 static inline Vector3<SType> Sqrt(const Vector3<SType> &dat) {
186 // Avoid NaN due to imprecision in the calculations
187 return Max(dat, Vector3<SType>()).Sqrt();
188 }
189
190 template <typename SType>
191 static inline Vector4<SType> Sqrt(const Vector4<SType> &dat) {
192 // Avoid NaN due to imprecision in the calculations
193 return Max(dat, Vector4<SType>()).Sqrt();
194 }
195};
196
197// Useful printing function
198template <typename VType, typename NumType>
199std::ostream &operator<<(std::ostream &out, const Stat1<VType, NumType> &s) {
200 out << "{ avg = " << s.Mean() << " std = " << s.StdDev()
201 << " nsamples = " << s.NumSamples() << "}";
202 return out;
203}
204
205// Stat1MinMax: same as Stat1, but it also
206// keeps the Min and Max values; the "-"
207// operator is disabled because it cannot be implemented
208// efficiently
209template <typename VType, typename NumType>
210class Stat1MinMax : public Stat1<VType, NumType> {
211 public:
212 typedef Stat1MinMax<VType, NumType> Self;
213
214 Stat1MinMax() { Clear(); }
215 // Create a sample of value dat and weight 1
216 explicit Stat1MinMax(const VType &dat) : Stat1<VType, NumType>(dat) {
217 max_ = dat;
218 min_ = dat;
219 }
220 // Create statistics for all the samples between begin (included)
221 // and end(excluded)
222 explicit Stat1MinMax(const VType *begin, const VType *end) {
223 Clear();
224 for (const VType *item = begin; item < end; ++item) {
225 (*this) += Stat1MinMax(*item);
226 }
227 }
228 // Create a sample of value dat and weight w
229 Stat1MinMax(const VType &dat, const NumType &w)
230 : Stat1<VType, NumType>(dat, w) {
231 max_ = dat;
232 min_ = dat;
233 }
234 // Copy operator
235 Stat1MinMax(const Self &stat) : Stat1<VType, NumType>(stat) {
236 max_ = stat.max_;
237 min_ = stat.min_;
238 }
239
240 void Clear() {
241 Stat1<VType, NumType>::Clear();
242 if (std::numeric_limits<VType>::has_infinity) {
243 min_ = std::numeric_limits<VType>::infinity();
244 max_ = -std::numeric_limits<VType>::infinity();
245 } else {
246 min_ = std::numeric_limits<VType>::max();
247 max_ = std::numeric_limits<VType>::min();
248 }
249 }
250
251 Self &operator=(const Self &stat) {
252 this->Stat1<VType, NumType>::operator=(stat);
253 max_ = stat.max_;
254 min_ = stat.min_;
255 return (*this);
256 }
257 // Merge statistics from two sample sets.
258 Self &operator+=(const Self &stat) {
259 this->Stat1<VType, NumType>::operator+=(stat);
260 if (stat.max_ > max_) max_ = stat.max_;
261 if (stat.min_ < min_) min_ = stat.min_;
262 return (*this);
263 }
264 // Multiply the weight of the set of samples by a factor k
265 Self &operator*=(const VType &stat) {
266 this->Stat1<VType, NumType>::operator*=(stat);
267 return (*this);
268 }
269 // Merge statistics from two sample sets.
270 Self operator+(const Self &stat) const { return Self(*this) += stat; }
271 // Multiply the weight of the set of samples by a factor k
272 Self operator*(const VType &k) const { return Self(*this) *= k; }
273
274 // Return the maximal value in this sample set
275 VType Max() const { return max_; }
276 // Return the minimal value in this sample set
277 VType Min() const { return min_; }
278
279 private:
280 // The - operation makes no sense with Min/Max
281 // unless we keep the full list of values (but we don't)
282 // make it private, and let it undefined so nobody can call it
283 Self &operator-=(const Self &stat); // senseless. let it undefined.
284
285 // The operation opposite to -
286 Self operator-(const Self &stat) const; // senseless. let it undefined.
287
288 // Let i be the index of the samples provided (using +=)
289 // and weight[i],value[i] be the data of sample #i
290 // then the variables have the following meaning:
291 VType max_; // max of value[i]
292 VType min_; // min of value[i]
293};
294
295// Useful printing function
296template <typename VType, typename NumType>
297std::ostream &operator<<(std::ostream &out,
298 const Stat1MinMax<VType, NumType> &s) {
299 out << "{ avg = " << s.Mean() << " std = " << s.StdDev()
300 << " nsamples = " << s.NumSamples() << " min = " << s.Min()
301 << " max = " << s.Max() << "}";
302 return out;
303}
304} // end namespace benchmark
305
306#endif // BENCHMARK_STAT_H_
307