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