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