| #ifndef BENCHMARK_STAT_H_ |
| #define BENCHMARK_STAT_H_ |
| |
| #include <cmath> |
| #include <limits> |
| #include <ostream> |
| #include <type_traits> |
| |
| namespace benchmark { |
| |
| template <typename VType, typename NumType> |
| class Stat1; |
| |
| template <typename VType, typename NumType> |
| class Stat1MinMax; |
| |
| typedef Stat1<float, int64_t> Stat1_f; |
| typedef Stat1<double, int64_t> Stat1_d; |
| typedef Stat1MinMax<float, int64_t> Stat1MinMax_f; |
| typedef Stat1MinMax<double, int64_t> Stat1MinMax_d; |
| |
| template <typename VType> |
| class Vector2; |
| template <typename VType> |
| class Vector3; |
| template <typename VType> |
| class Vector4; |
| |
| template <typename VType, typename NumType> |
| class Stat1 { |
| public: |
| typedef Stat1<VType, NumType> 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) { |
| VType avg_squares = sum_squares_ * (1.0 / numsamples_); |
| *stddev = Sqrt(avg_squares - Sqr(mean)); |
| } |
| return mean; |
| } |
| |
| // Return the standard deviation of the sample set |
| VType StdDev() const { |
| if (numsamples_ == 0) return VType(); |
| VType mean = Mean(); |
| VType avg_squares = sum_squares_ * (1.0 / numsamples_); |
| return Sqrt(avg_squares - Sqr(mean)); |
| } |
| |
| private: |
| static_assert(std::is_integral<NumType>::value && |
| !std::is_same<NumType, bool>::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 <typename SType> |
| static inline SType Sqr(const SType &dat) { |
| return dat * dat; |
| } |
| |
| template <typename SType> |
| static inline Vector2<SType> Sqr(const Vector2<SType> &dat) { |
| return dat.MulComponents(dat); |
| } |
| |
| template <typename SType> |
| static inline Vector3<SType> Sqr(const Vector3<SType> &dat) { |
| return dat.MulComponents(dat); |
| } |
| |
| template <typename SType> |
| static inline Vector4<SType> Sqr(const Vector4<SType> &dat) { |
| return dat.MulComponents(dat); |
| } |
| |
| // Template function used to take the square root of a number. |
| // For a vector we square all components |
| template <typename SType> |
| static inline SType Sqrt(const SType &dat) { |
| // Avoid NaN due to imprecision in the calculations |
| if (dat < 0) return 0; |
| return sqrt(dat); |
| } |
| |
| template <typename SType> |
| static inline Vector2<SType> Sqrt(const Vector2<SType> &dat) { |
| // Avoid NaN due to imprecision in the calculations |
| return Max(dat, Vector2<SType>()).Sqrt(); |
| } |
| |
| template <typename SType> |
| static inline Vector3<SType> Sqrt(const Vector3<SType> &dat) { |
| // Avoid NaN due to imprecision in the calculations |
| return Max(dat, Vector3<SType>()).Sqrt(); |
| } |
| |
| template <typename SType> |
| static inline Vector4<SType> Sqrt(const Vector4<SType> &dat) { |
| // Avoid NaN due to imprecision in the calculations |
| return Max(dat, Vector4<SType>()).Sqrt(); |
| } |
| }; |
| |
| // Useful printing function |
| template <typename VType, typename NumType> |
| std::ostream &operator<<(std::ostream &out, const Stat1<VType, NumType> &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 <typename VType, typename NumType> |
| class Stat1MinMax : public Stat1<VType, NumType> { |
| public: |
| typedef Stat1MinMax<VType, NumType> Self; |
| |
| Stat1MinMax() { Clear(); } |
| // Create a sample of value dat and weight 1 |
| explicit Stat1MinMax(const VType &dat) : Stat1<VType, NumType>(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<VType, NumType>(dat, w) { |
| max_ = dat; |
| min_ = dat; |
| } |
| // Copy operator |
| Stat1MinMax(const Self &stat) : Stat1<VType, NumType>(stat) { |
| max_ = stat.max_; |
| min_ = stat.min_; |
| } |
| |
| void Clear() { |
| Stat1<VType, NumType>::Clear(); |
| if (std::numeric_limits<VType>::has_infinity) { |
| min_ = std::numeric_limits<VType>::infinity(); |
| max_ = -std::numeric_limits<VType>::infinity(); |
| } else { |
| min_ = std::numeric_limits<VType>::max(); |
| max_ = std::numeric_limits<VType>::min(); |
| } |
| } |
| |
| Self &operator=(const Self &stat) { |
| this->Stat1<VType, NumType>::operator=(stat); |
| max_ = stat.max_; |
| min_ = stat.min_; |
| return (*this); |
| } |
| // Merge statistics from two sample sets. |
| Self &operator+=(const Self &stat) { |
| this->Stat1<VType, NumType>::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<VType, NumType>::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 <typename VType, typename NumType> |
| std::ostream &operator<<(std::ostream &out, |
| const Stat1MinMax<VType, NumType> &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_ |