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