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