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