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