xref: /aosp_15_r20/external/google-benchmark/src/statistics.cc (revision dbb99499c3810fa1611fa2242a2fc446be01a57c)
1*dbb99499SAndroid Build Coastguard Worker // Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
2*dbb99499SAndroid Build Coastguard Worker // Copyright 2017 Roman Lebedev. All rights reserved.
3*dbb99499SAndroid Build Coastguard Worker //
4*dbb99499SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*dbb99499SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*dbb99499SAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*dbb99499SAndroid Build Coastguard Worker //
8*dbb99499SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
9*dbb99499SAndroid Build Coastguard Worker //
10*dbb99499SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*dbb99499SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*dbb99499SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*dbb99499SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*dbb99499SAndroid Build Coastguard Worker // limitations under the License.
15*dbb99499SAndroid Build Coastguard Worker 
16*dbb99499SAndroid Build Coastguard Worker #include "statistics.h"
17*dbb99499SAndroid Build Coastguard Worker 
18*dbb99499SAndroid Build Coastguard Worker #include <algorithm>
19*dbb99499SAndroid Build Coastguard Worker #include <cmath>
20*dbb99499SAndroid Build Coastguard Worker #include <numeric>
21*dbb99499SAndroid Build Coastguard Worker #include <string>
22*dbb99499SAndroid Build Coastguard Worker #include <vector>
23*dbb99499SAndroid Build Coastguard Worker 
24*dbb99499SAndroid Build Coastguard Worker #include "benchmark/benchmark.h"
25*dbb99499SAndroid Build Coastguard Worker #include "check.h"
26*dbb99499SAndroid Build Coastguard Worker 
27*dbb99499SAndroid Build Coastguard Worker namespace benchmark {
28*dbb99499SAndroid Build Coastguard Worker 
__anonc9bd6c810102(const std::vector<double>& v) 29*dbb99499SAndroid Build Coastguard Worker auto StatisticsSum = [](const std::vector<double>& v) {
30*dbb99499SAndroid Build Coastguard Worker   return std::accumulate(v.begin(), v.end(), 0.0);
31*dbb99499SAndroid Build Coastguard Worker };
32*dbb99499SAndroid Build Coastguard Worker 
StatisticsMean(const std::vector<double> & v)33*dbb99499SAndroid Build Coastguard Worker double StatisticsMean(const std::vector<double>& v) {
34*dbb99499SAndroid Build Coastguard Worker   if (v.empty()) return 0.0;
35*dbb99499SAndroid Build Coastguard Worker   return StatisticsSum(v) * (1.0 / static_cast<double>(v.size()));
36*dbb99499SAndroid Build Coastguard Worker }
37*dbb99499SAndroid Build Coastguard Worker 
StatisticsMedian(const std::vector<double> & v)38*dbb99499SAndroid Build Coastguard Worker double StatisticsMedian(const std::vector<double>& v) {
39*dbb99499SAndroid Build Coastguard Worker   if (v.size() < 3) return StatisticsMean(v);
40*dbb99499SAndroid Build Coastguard Worker   std::vector<double> copy(v);
41*dbb99499SAndroid Build Coastguard Worker 
42*dbb99499SAndroid Build Coastguard Worker   auto center = copy.begin() + v.size() / 2;
43*dbb99499SAndroid Build Coastguard Worker   std::nth_element(copy.begin(), center, copy.end());
44*dbb99499SAndroid Build Coastguard Worker 
45*dbb99499SAndroid Build Coastguard Worker   // Did we have an odd number of samples?  If yes, then center is the median.
46*dbb99499SAndroid Build Coastguard Worker   // If not, then we are looking for the average between center and the value
47*dbb99499SAndroid Build Coastguard Worker   // before.  Instead of resorting, we just look for the max value before it,
48*dbb99499SAndroid Build Coastguard Worker   // which is not necessarily the element immediately preceding `center` Since
49*dbb99499SAndroid Build Coastguard Worker   // `copy` is only partially sorted by `nth_element`.
50*dbb99499SAndroid Build Coastguard Worker   if (v.size() % 2 == 1) return *center;
51*dbb99499SAndroid Build Coastguard Worker   auto center2 = std::max_element(copy.begin(), center);
52*dbb99499SAndroid Build Coastguard Worker   return (*center + *center2) / 2.0;
53*dbb99499SAndroid Build Coastguard Worker }
54*dbb99499SAndroid Build Coastguard Worker 
55*dbb99499SAndroid Build Coastguard Worker // Return the sum of the squares of this sample set
__anonc9bd6c810202(const std::vector<double>& v) 56*dbb99499SAndroid Build Coastguard Worker auto SumSquares = [](const std::vector<double>& v) {
57*dbb99499SAndroid Build Coastguard Worker   return std::inner_product(v.begin(), v.end(), v.begin(), 0.0);
58*dbb99499SAndroid Build Coastguard Worker };
59*dbb99499SAndroid Build Coastguard Worker 
__anonc9bd6c810302(const double dat) 60*dbb99499SAndroid Build Coastguard Worker auto Sqr = [](const double dat) { return dat * dat; };
__anonc9bd6c810402(const double dat) 61*dbb99499SAndroid Build Coastguard Worker auto Sqrt = [](const double dat) {
62*dbb99499SAndroid Build Coastguard Worker   // Avoid NaN due to imprecision in the calculations
63*dbb99499SAndroid Build Coastguard Worker   if (dat < 0.0) return 0.0;
64*dbb99499SAndroid Build Coastguard Worker   return std::sqrt(dat);
65*dbb99499SAndroid Build Coastguard Worker };
66*dbb99499SAndroid Build Coastguard Worker 
StatisticsStdDev(const std::vector<double> & v)67*dbb99499SAndroid Build Coastguard Worker double StatisticsStdDev(const std::vector<double>& v) {
68*dbb99499SAndroid Build Coastguard Worker   const auto mean = StatisticsMean(v);
69*dbb99499SAndroid Build Coastguard Worker   if (v.empty()) return mean;
70*dbb99499SAndroid Build Coastguard Worker 
71*dbb99499SAndroid Build Coastguard Worker   // Sample standard deviation is undefined for n = 1
72*dbb99499SAndroid Build Coastguard Worker   if (v.size() == 1) return 0.0;
73*dbb99499SAndroid Build Coastguard Worker 
74*dbb99499SAndroid Build Coastguard Worker   const double avg_squares =
75*dbb99499SAndroid Build Coastguard Worker       SumSquares(v) * (1.0 / static_cast<double>(v.size()));
76*dbb99499SAndroid Build Coastguard Worker   return Sqrt(static_cast<double>(v.size()) /
77*dbb99499SAndroid Build Coastguard Worker               (static_cast<double>(v.size()) - 1.0) *
78*dbb99499SAndroid Build Coastguard Worker               (avg_squares - Sqr(mean)));
79*dbb99499SAndroid Build Coastguard Worker }
80*dbb99499SAndroid Build Coastguard Worker 
StatisticsCV(const std::vector<double> & v)81*dbb99499SAndroid Build Coastguard Worker double StatisticsCV(const std::vector<double>& v) {
82*dbb99499SAndroid Build Coastguard Worker   if (v.size() < 2) return 0.0;
83*dbb99499SAndroid Build Coastguard Worker 
84*dbb99499SAndroid Build Coastguard Worker   const auto stddev = StatisticsStdDev(v);
85*dbb99499SAndroid Build Coastguard Worker   const auto mean = StatisticsMean(v);
86*dbb99499SAndroid Build Coastguard Worker 
87*dbb99499SAndroid Build Coastguard Worker   if (std::fpclassify(mean) == FP_ZERO) return 0.0;
88*dbb99499SAndroid Build Coastguard Worker 
89*dbb99499SAndroid Build Coastguard Worker   return stddev / mean;
90*dbb99499SAndroid Build Coastguard Worker }
91*dbb99499SAndroid Build Coastguard Worker 
ComputeStats(const std::vector<BenchmarkReporter::Run> & reports)92*dbb99499SAndroid Build Coastguard Worker std::vector<BenchmarkReporter::Run> ComputeStats(
93*dbb99499SAndroid Build Coastguard Worker     const std::vector<BenchmarkReporter::Run>& reports) {
94*dbb99499SAndroid Build Coastguard Worker   typedef BenchmarkReporter::Run Run;
95*dbb99499SAndroid Build Coastguard Worker   std::vector<Run> results;
96*dbb99499SAndroid Build Coastguard Worker 
97*dbb99499SAndroid Build Coastguard Worker   auto error_count = std::count_if(reports.begin(), reports.end(),
98*dbb99499SAndroid Build Coastguard Worker                                    [](Run const& run) { return run.skipped; });
99*dbb99499SAndroid Build Coastguard Worker 
100*dbb99499SAndroid Build Coastguard Worker   if (reports.size() - static_cast<size_t>(error_count) < 2) {
101*dbb99499SAndroid Build Coastguard Worker     // We don't report aggregated data if there was a single run.
102*dbb99499SAndroid Build Coastguard Worker     return results;
103*dbb99499SAndroid Build Coastguard Worker   }
104*dbb99499SAndroid Build Coastguard Worker 
105*dbb99499SAndroid Build Coastguard Worker   // Accumulators.
106*dbb99499SAndroid Build Coastguard Worker   std::vector<double> real_accumulated_time_stat;
107*dbb99499SAndroid Build Coastguard Worker   std::vector<double> cpu_accumulated_time_stat;
108*dbb99499SAndroid Build Coastguard Worker 
109*dbb99499SAndroid Build Coastguard Worker   real_accumulated_time_stat.reserve(reports.size());
110*dbb99499SAndroid Build Coastguard Worker   cpu_accumulated_time_stat.reserve(reports.size());
111*dbb99499SAndroid Build Coastguard Worker 
112*dbb99499SAndroid Build Coastguard Worker   // All repetitions should be run with the same number of iterations so we
113*dbb99499SAndroid Build Coastguard Worker   // can take this information from the first benchmark.
114*dbb99499SAndroid Build Coastguard Worker   const IterationCount run_iterations = reports.front().iterations;
115*dbb99499SAndroid Build Coastguard Worker   // create stats for user counters
116*dbb99499SAndroid Build Coastguard Worker   struct CounterStat {
117*dbb99499SAndroid Build Coastguard Worker     Counter c;
118*dbb99499SAndroid Build Coastguard Worker     std::vector<double> s;
119*dbb99499SAndroid Build Coastguard Worker   };
120*dbb99499SAndroid Build Coastguard Worker   std::map<std::string, CounterStat> counter_stats;
121*dbb99499SAndroid Build Coastguard Worker   for (Run const& r : reports) {
122*dbb99499SAndroid Build Coastguard Worker     for (auto const& cnt : r.counters) {
123*dbb99499SAndroid Build Coastguard Worker       auto it = counter_stats.find(cnt.first);
124*dbb99499SAndroid Build Coastguard Worker       if (it == counter_stats.end()) {
125*dbb99499SAndroid Build Coastguard Worker         it = counter_stats
126*dbb99499SAndroid Build Coastguard Worker                  .emplace(cnt.first,
127*dbb99499SAndroid Build Coastguard Worker                           CounterStat{cnt.second, std::vector<double>{}})
128*dbb99499SAndroid Build Coastguard Worker                  .first;
129*dbb99499SAndroid Build Coastguard Worker         it->second.s.reserve(reports.size());
130*dbb99499SAndroid Build Coastguard Worker       } else {
131*dbb99499SAndroid Build Coastguard Worker         BM_CHECK_EQ(it->second.c.flags, cnt.second.flags);
132*dbb99499SAndroid Build Coastguard Worker       }
133*dbb99499SAndroid Build Coastguard Worker     }
134*dbb99499SAndroid Build Coastguard Worker   }
135*dbb99499SAndroid Build Coastguard Worker 
136*dbb99499SAndroid Build Coastguard Worker   // Populate the accumulators.
137*dbb99499SAndroid Build Coastguard Worker   for (Run const& run : reports) {
138*dbb99499SAndroid Build Coastguard Worker     BM_CHECK_EQ(reports[0].benchmark_name(), run.benchmark_name());
139*dbb99499SAndroid Build Coastguard Worker     BM_CHECK_EQ(run_iterations, run.iterations);
140*dbb99499SAndroid Build Coastguard Worker     if (run.skipped) continue;
141*dbb99499SAndroid Build Coastguard Worker     real_accumulated_time_stat.emplace_back(run.real_accumulated_time);
142*dbb99499SAndroid Build Coastguard Worker     cpu_accumulated_time_stat.emplace_back(run.cpu_accumulated_time);
143*dbb99499SAndroid Build Coastguard Worker     // user counters
144*dbb99499SAndroid Build Coastguard Worker     for (auto const& cnt : run.counters) {
145*dbb99499SAndroid Build Coastguard Worker       auto it = counter_stats.find(cnt.first);
146*dbb99499SAndroid Build Coastguard Worker       BM_CHECK_NE(it, counter_stats.end());
147*dbb99499SAndroid Build Coastguard Worker       it->second.s.emplace_back(cnt.second);
148*dbb99499SAndroid Build Coastguard Worker     }
149*dbb99499SAndroid Build Coastguard Worker   }
150*dbb99499SAndroid Build Coastguard Worker 
151*dbb99499SAndroid Build Coastguard Worker   // Only add label if it is same for all runs
152*dbb99499SAndroid Build Coastguard Worker   std::string report_label = reports[0].report_label;
153*dbb99499SAndroid Build Coastguard Worker   for (std::size_t i = 1; i < reports.size(); i++) {
154*dbb99499SAndroid Build Coastguard Worker     if (reports[i].report_label != report_label) {
155*dbb99499SAndroid Build Coastguard Worker       report_label = "";
156*dbb99499SAndroid Build Coastguard Worker       break;
157*dbb99499SAndroid Build Coastguard Worker     }
158*dbb99499SAndroid Build Coastguard Worker   }
159*dbb99499SAndroid Build Coastguard Worker 
160*dbb99499SAndroid Build Coastguard Worker   const double iteration_rescale_factor =
161*dbb99499SAndroid Build Coastguard Worker       double(reports.size()) / double(run_iterations);
162*dbb99499SAndroid Build Coastguard Worker 
163*dbb99499SAndroid Build Coastguard Worker   for (const auto& Stat : *reports[0].statistics) {
164*dbb99499SAndroid Build Coastguard Worker     // Get the data from the accumulator to BenchmarkReporter::Run's.
165*dbb99499SAndroid Build Coastguard Worker     Run data;
166*dbb99499SAndroid Build Coastguard Worker     data.run_name = reports[0].run_name;
167*dbb99499SAndroid Build Coastguard Worker     data.family_index = reports[0].family_index;
168*dbb99499SAndroid Build Coastguard Worker     data.per_family_instance_index = reports[0].per_family_instance_index;
169*dbb99499SAndroid Build Coastguard Worker     data.run_type = BenchmarkReporter::Run::RT_Aggregate;
170*dbb99499SAndroid Build Coastguard Worker     data.threads = reports[0].threads;
171*dbb99499SAndroid Build Coastguard Worker     data.repetitions = reports[0].repetitions;
172*dbb99499SAndroid Build Coastguard Worker     data.repetition_index = Run::no_repetition_index;
173*dbb99499SAndroid Build Coastguard Worker     data.aggregate_name = Stat.name_;
174*dbb99499SAndroid Build Coastguard Worker     data.aggregate_unit = Stat.unit_;
175*dbb99499SAndroid Build Coastguard Worker     data.report_label = report_label;
176*dbb99499SAndroid Build Coastguard Worker 
177*dbb99499SAndroid Build Coastguard Worker     // It is incorrect to say that an aggregate is computed over
178*dbb99499SAndroid Build Coastguard Worker     // run's iterations, because those iterations already got averaged.
179*dbb99499SAndroid Build Coastguard Worker     // Similarly, if there are N repetitions with 1 iterations each,
180*dbb99499SAndroid Build Coastguard Worker     // an aggregate will be computed over N measurements, not 1.
181*dbb99499SAndroid Build Coastguard Worker     // Thus it is best to simply use the count of separate reports.
182*dbb99499SAndroid Build Coastguard Worker     data.iterations = static_cast<IterationCount>(reports.size());
183*dbb99499SAndroid Build Coastguard Worker 
184*dbb99499SAndroid Build Coastguard Worker     data.real_accumulated_time = Stat.compute_(real_accumulated_time_stat);
185*dbb99499SAndroid Build Coastguard Worker     data.cpu_accumulated_time = Stat.compute_(cpu_accumulated_time_stat);
186*dbb99499SAndroid Build Coastguard Worker 
187*dbb99499SAndroid Build Coastguard Worker     if (data.aggregate_unit == StatisticUnit::kTime) {
188*dbb99499SAndroid Build Coastguard Worker       // We will divide these times by data.iterations when reporting, but the
189*dbb99499SAndroid Build Coastguard Worker       // data.iterations is not necessarily the scale of these measurements,
190*dbb99499SAndroid Build Coastguard Worker       // because in each repetition, these timers are sum over all the iters.
191*dbb99499SAndroid Build Coastguard Worker       // And if we want to say that the stats are over N repetitions and not
192*dbb99499SAndroid Build Coastguard Worker       // M iterations, we need to multiply these by (N/M).
193*dbb99499SAndroid Build Coastguard Worker       data.real_accumulated_time *= iteration_rescale_factor;
194*dbb99499SAndroid Build Coastguard Worker       data.cpu_accumulated_time *= iteration_rescale_factor;
195*dbb99499SAndroid Build Coastguard Worker     }
196*dbb99499SAndroid Build Coastguard Worker 
197*dbb99499SAndroid Build Coastguard Worker     data.time_unit = reports[0].time_unit;
198*dbb99499SAndroid Build Coastguard Worker 
199*dbb99499SAndroid Build Coastguard Worker     // user counters
200*dbb99499SAndroid Build Coastguard Worker     for (auto const& kv : counter_stats) {
201*dbb99499SAndroid Build Coastguard Worker       // Do *NOT* rescale the custom counters. They are already properly scaled.
202*dbb99499SAndroid Build Coastguard Worker       const auto uc_stat = Stat.compute_(kv.second.s);
203*dbb99499SAndroid Build Coastguard Worker       auto c = Counter(uc_stat, counter_stats[kv.first].c.flags,
204*dbb99499SAndroid Build Coastguard Worker                        counter_stats[kv.first].c.oneK);
205*dbb99499SAndroid Build Coastguard Worker       data.counters[kv.first] = c;
206*dbb99499SAndroid Build Coastguard Worker     }
207*dbb99499SAndroid Build Coastguard Worker 
208*dbb99499SAndroid Build Coastguard Worker     results.push_back(data);
209*dbb99499SAndroid Build Coastguard Worker   }
210*dbb99499SAndroid Build Coastguard Worker 
211*dbb99499SAndroid Build Coastguard Worker   return results;
212*dbb99499SAndroid Build Coastguard Worker }
213*dbb99499SAndroid Build Coastguard Worker 
214*dbb99499SAndroid Build Coastguard Worker }  // end namespace benchmark
215