#ifndef BENCHMARK_STAT_H_ #define BENCHMARK_STAT_H_ #include #include #include #include namespace benchmark { template class Stat1; template class Stat1MinMax; typedef Stat1 Stat1_f; typedef Stat1 Stat1_d; typedef Stat1MinMax Stat1MinMax_f; typedef Stat1MinMax Stat1MinMax_d; template class Vector2; template class Vector3; template class Vector4; template class Stat1 { public: typedef Stat1 Self; Stat1() { Clear(); } // Create a sample of value dat and weight 1 explicit Stat1(const VType &dat) { sum_ = dat; sum_squares_ = Sqr(dat); numsamples_ = 1; } // Create statistics for all the samples between begin (included) // and end(excluded) explicit Stat1(const VType *begin, const VType *end) { Clear(); for (const VType *item = begin; item < end; ++item) { (*this) += Stat1(*item); } } // Create a sample of value dat and weight w Stat1(const VType &dat, const NumType &w) { sum_ = w * dat; sum_squares_ = w * Sqr(dat); numsamples_ = w; } // Copy operator Stat1(const Self &stat) { sum_ = stat.sum_; sum_squares_ = stat.sum_squares_; numsamples_ = stat.numsamples_; } void Clear() { numsamples_ = NumType(); sum_squares_ = sum_ = VType(); } Self &operator=(const Self &stat) { sum_ = stat.sum_; sum_squares_ = stat.sum_squares_; numsamples_ = stat.numsamples_; return (*this); } // Merge statistics from two sample sets. Self &operator+=(const Self &stat) { sum_ += stat.sum_; sum_squares_ += stat.sum_squares_; numsamples_ += stat.numsamples_; return (*this); } // The operation opposite to += Self &operator-=(const Self &stat) { sum_ -= stat.sum_; sum_squares_ -= stat.sum_squares_; numsamples_ -= stat.numsamples_; return (*this); } // Multiply the weight of the set of samples by a factor k Self &operator*=(const VType &k) { sum_ *= k; sum_squares_ *= k; numsamples_ *= k; return (*this); } // Merge statistics from two sample sets. Self operator+(const Self &stat) const { return Self(*this) += stat; } // The operation opposite to + Self operator-(const Self &stat) const { return Self(*this) -= stat; } // Multiply the weight of the set of samples by a factor k Self operator*(const VType &k) const { return Self(*this) *= k; } // Return the total weight of this sample set NumType numSamples() const { return numsamples_; } // Return the sum of this sample set VType Sum() const { return sum_; } // Return the mean of this sample set VType Mean() const { if (numsamples_ == 0) return VType(); return sum_ * (1.0 / numsamples_); } // Return the mean of this sample set and compute the standard deviation at // the same time. VType Mean(VType *stddev) const { if (numsamples_ == 0) return VType(); VType mean = sum_ * (1.0 / numsamples_); if (stddev) { // Sample standard deviation is undefined for n = 1 if (numsamples_ == 1) { *stddev = VType(); } else { VType avg_squares = sum_squares_ * (1.0 / numsamples_); *stddev = Sqrt(numsamples_ / (numsamples_ - 1.0) * (avg_squares - Sqr(mean))); } } return mean; } // Return the standard deviation of the sample set VType StdDev() const { VType stddev = VType(); Mean(&stddev); return stddev; } private: static_assert(std::is_integral::value && !std::is_same::value, "NumType must be an integral type that is not bool."); // Let i be the index of the samples provided (using +=) // and weight[i],value[i] be the data of sample #i // then the variables have the following meaning: NumType numsamples_; // sum of weight[i]; VType sum_; // sum of weight[i]*value[i]; VType sum_squares_; // sum of weight[i]*value[i]^2; // Template function used to square a number. // For a vector we square all components template static inline SType Sqr(const SType &dat) { return dat * dat; } template static inline Vector2 Sqr(const Vector2 &dat) { return dat.MulComponents(dat); } template static inline Vector3 Sqr(const Vector3 &dat) { return dat.MulComponents(dat); } template static inline Vector4 Sqr(const Vector4 &dat) { return dat.MulComponents(dat); } // Template function used to take the square root of a number. // For a vector we square all components template static inline SType Sqrt(const SType &dat) { // Avoid NaN due to imprecision in the calculations if (dat < 0) return 0; return sqrt(dat); } template static inline Vector2 Sqrt(const Vector2 &dat) { // Avoid NaN due to imprecision in the calculations return Max(dat, Vector2()).Sqrt(); } template static inline Vector3 Sqrt(const Vector3 &dat) { // Avoid NaN due to imprecision in the calculations return Max(dat, Vector3()).Sqrt(); } template static inline Vector4 Sqrt(const Vector4 &dat) { // Avoid NaN due to imprecision in the calculations return Max(dat, Vector4()).Sqrt(); } }; // Useful printing function template std::ostream &operator<<(std::ostream &out, const Stat1 &s) { out << "{ avg = " << s.Mean() << " std = " << s.StdDev() << " nsamples = " << s.NumSamples() << "}"; return out; } // Stat1MinMax: same as Stat1, but it also // keeps the Min and Max values; the "-" // operator is disabled because it cannot be implemented // efficiently template class Stat1MinMax : public Stat1 { public: typedef Stat1MinMax Self; Stat1MinMax() { Clear(); } // Create a sample of value dat and weight 1 explicit Stat1MinMax(const VType &dat) : Stat1(dat) { max_ = dat; min_ = dat; } // Create statistics for all the samples between begin (included) // and end(excluded) explicit Stat1MinMax(const VType *begin, const VType *end) { Clear(); for (const VType *item = begin; item < end; ++item) { (*this) += Stat1MinMax(*item); } } // Create a sample of value dat and weight w Stat1MinMax(const VType &dat, const NumType &w) : Stat1(dat, w) { max_ = dat; min_ = dat; } // Copy operator Stat1MinMax(const Self &stat) : Stat1(stat) { max_ = stat.max_; min_ = stat.min_; } void Clear() { Stat1::Clear(); if (std::numeric_limits::has_infinity) { min_ = std::numeric_limits::infinity(); max_ = -std::numeric_limits::infinity(); } else { min_ = std::numeric_limits::max(); max_ = std::numeric_limits::min(); } } Self &operator=(const Self &stat) { this->Stat1::operator=(stat); max_ = stat.max_; min_ = stat.min_; return (*this); } // Merge statistics from two sample sets. Self &operator+=(const Self &stat) { this->Stat1::operator+=(stat); if (stat.max_ > max_) max_ = stat.max_; if (stat.min_ < min_) min_ = stat.min_; return (*this); } // Multiply the weight of the set of samples by a factor k Self &operator*=(const VType &stat) { this->Stat1::operator*=(stat); return (*this); } // Merge statistics from two sample sets. Self operator+(const Self &stat) const { return Self(*this) += stat; } // Multiply the weight of the set of samples by a factor k Self operator*(const VType &k) const { return Self(*this) *= k; } // Return the maximal value in this sample set VType Max() const { return max_; } // Return the minimal value in this sample set VType Min() const { return min_; } private: // The - operation makes no sense with Min/Max // unless we keep the full list of values (but we don't) // make it private, and let it undefined so nobody can call it Self &operator-=(const Self &stat); // senseless. let it undefined. // The operation opposite to - Self operator-(const Self &stat) const; // senseless. let it undefined. // Let i be the index of the samples provided (using +=) // and weight[i],value[i] be the data of sample #i // then the variables have the following meaning: VType max_; // max of value[i] VType min_; // min of value[i] }; // Useful printing function template std::ostream &operator<<(std::ostream &out, const Stat1MinMax &s) { out << "{ avg = " << s.Mean() << " std = " << s.StdDev() << " nsamples = " << s.NumSamples() << " min = " << s.Min() << " max = " << s.Max() << "}"; return out; } } // end namespace benchmark #endif // BENCHMARK_STAT_H_