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 | |