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 
10 namespace benchmark {
11 
12 template <typename VType, typename NumType>
13 class Stat1;
14 
15 template <typename VType, typename NumType>
16 class Stat1MinMax;
17 
18 typedef Stat1<float, int64_t> Stat1_f;
19 typedef Stat1<double, int64_t> Stat1_d;
20 typedef Stat1MinMax<float, int64_t> Stat1MinMax_f;
21 typedef Stat1MinMax<double, int64_t> Stat1MinMax_d;
22 
23 template <typename VType>
24 class Vector2;
25 template <typename VType>
26 class Vector3;
27 template <typename VType>
28 class Vector4;
29 
30 template <typename VType, typename NumType>
31 class Stat1 {
32  public:
33   typedef Stat1<VType, NumType> Self;
34 
Stat1()35   Stat1() { Clear(); }
36   // Create a sample of value dat and weight 1
Stat1(const VType & dat)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)
Stat1(const VType * begin,const VType * end)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
Stat1(const VType & dat,const NumType & 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
Stat1(const Self & stat)57   Stat1(const Self &stat) {
58     sum_ = stat.sum_;
59     sum_squares_ = stat.sum_squares_;
60     numsamples_ = stat.numsamples_;
61   }
62 
Clear()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
numSamples()106   NumType numSamples() const { return numsamples_; }
107 
108   // Return the sum of this sample set
Sum()109   VType Sum() const { return sum_; }
110 
111   // Return the mean of this sample set
Mean()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.
Mean(VType * stddev)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
StdDev()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>
Sqr(const SType & dat)151   static inline SType Sqr(const SType &dat) {
152     return dat * dat;
153   }
154 
155   template <typename SType>
Sqr(const Vector2<SType> & dat)156   static inline Vector2<SType> Sqr(const Vector2<SType> &dat) {
157     return dat.MulComponents(dat);
158   }
159 
160   template <typename SType>
Sqr(const Vector3<SType> & dat)161   static inline Vector3<SType> Sqr(const Vector3<SType> &dat) {
162     return dat.MulComponents(dat);
163   }
164 
165   template <typename SType>
Sqr(const Vector4<SType> & dat)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>
Sqrt(const SType & dat)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>
Sqrt(const Vector2<SType> & dat)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>
Sqrt(const Vector3<SType> & dat)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>
Sqrt(const Vector4<SType> & dat)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
199 template <typename VType, typename NumType>
200 std::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
210 template <typename VType, typename NumType>
211 class Stat1MinMax : public Stat1<VType, NumType> {
212  public:
213   typedef Stat1MinMax<VType, NumType> Self;
214 
Stat1MinMax()215   Stat1MinMax() { Clear(); }
216   // Create a sample of value dat and weight 1
Stat1MinMax(const VType & dat)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)
Stat1MinMax(const VType * begin,const VType * end)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
Stat1MinMax(const VType & dat,const NumType & 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
Stat1MinMax(const Self & stat)236   Stat1MinMax(const Self &stat) : Stat1<VType, NumType>(stat) {
237     max_ = stat.max_;
238     min_ = stat.min_;
239   }
240 
Clear()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
Max()276   VType Max() const { return max_; }
277   // Return the minimal value in this sample set
Min()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
297 template <typename VType, typename NumType>
298 std::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