benchmark/src/complexity.cc
Go to the documentation of this file.
1 // Copyright 2016 Ismael Jimenez Martinez. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 // Source project : https://github.com/ismaelJimenez/cpp.leastsq
16 // Adapted to be used with google benchmark
17 
18 #include "benchmark/benchmark.h"
19 
20 #include <algorithm>
21 #include <cmath>
22 #include "check.h"
23 #include "complexity.h"
24 
25 namespace benchmark {
26 
27 // Internal function to calculate the different scalability forms
28 BigOFunc* FittingCurve(BigO complexity) {
29  static const double kLog2E = 1.44269504088896340736;
30  switch (complexity) {
31  case oN:
32  return [](IterationCount n) -> double { return static_cast<double>(n); };
33  case oNSquared:
34  return [](IterationCount n) -> double { return std::pow(n, 2); };
35  case oNCubed:
36  return [](IterationCount n) -> double { return std::pow(n, 3); };
37  case oLogN:
38  /* Note: can't use log2 because Android's GNU STL lacks it */
39  return
40  [](IterationCount n) { return kLog2E * log(static_cast<double>(n)); };
41  case oNLogN:
42  /* Note: can't use log2 because Android's GNU STL lacks it */
43  return [](IterationCount n) {
44  return kLog2E * n * log(static_cast<double>(n));
45  };
46  case o1:
47  default:
48  return [](IterationCount) { return 1.0; };
49  }
50 }
51 
52 // Function to return an string for the calculated complexity
54  switch (complexity) {
55  case oN:
56  return "N";
57  case oNSquared:
58  return "N^2";
59  case oNCubed:
60  return "N^3";
61  case oLogN:
62  return "lgN";
63  case oNLogN:
64  return "NlgN";
65  case o1:
66  return "(1)";
67  default:
68  return "f(N)";
69  }
70 }
71 
72 // Find the coefficient for the high-order term in the running time, by
73 // minimizing the sum of squares of relative error, for the fitting curve
74 // given by the lambda expression.
75 // - n : Vector containing the size of the benchmark tests.
76 // - time : Vector containing the times for the benchmark tests.
77 // - fitting_curve : lambda expression (e.g. [](int64_t n) {return n; };).
78 
79 // For a deeper explanation on the algorithm logic, please refer to
80 // https://en.wikipedia.org/wiki/Least_squares#Least_squares,_regression_analysis_and_statistics
81 
82 LeastSq MinimalLeastSq(const std::vector<int64_t>& n,
83  const std::vector<double>& time,
84  BigOFunc* fitting_curve) {
85  double sigma_gn_squared = 0.0;
86  double sigma_time = 0.0;
87  double sigma_time_gn = 0.0;
88 
89  // Calculate least square fitting parameter
90  for (size_t i = 0; i < n.size(); ++i) {
91  double gn_i = fitting_curve(n[i]);
92  sigma_gn_squared += gn_i * gn_i;
93  sigma_time += time[i];
94  sigma_time_gn += time[i] * gn_i;
95  }
96 
98  result.complexity = oLambda;
99 
100  // Calculate complexity.
101  result.coef = sigma_time_gn / sigma_gn_squared;
102 
103  // Calculate RMS
104  double rms = 0.0;
105  for (size_t i = 0; i < n.size(); ++i) {
106  double fit = result.coef * fitting_curve(n[i]);
107  rms += pow((time[i] - fit), 2);
108  }
109 
110  // Normalized RMS by the mean of the observed values
111  double mean = sigma_time / n.size();
112  result.rms = sqrt(rms / n.size()) / mean;
113 
114  return result;
115 }
116 
117 // Find the coefficient for the high-order term in the running time, by
118 // minimizing the sum of squares of relative error.
119 // - n : Vector containing the size of the benchmark tests.
120 // - time : Vector containing the times for the benchmark tests.
121 // - complexity : If different than oAuto, the fitting curve will stick to
122 // this one. If it is oAuto, it will be calculated the best
123 // fitting curve.
124 LeastSq MinimalLeastSq(const std::vector<int64_t>& n,
125  const std::vector<double>& time, const BigO complexity) {
126  BM_CHECK_EQ(n.size(), time.size());
127  BM_CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two
128  // benchmark runs are given
129  BM_CHECK_NE(complexity, oNone);
130 
131  LeastSq best_fit;
132 
133  if (complexity == oAuto) {
134  std::vector<BigO> fit_curves = {oLogN, oN, oNLogN, oNSquared, oNCubed};
135 
136  // Take o1 as default best fitting curve
137  best_fit = MinimalLeastSq(n, time, FittingCurve(o1));
138  best_fit.complexity = o1;
139 
140  // Compute all possible fitting curves and stick to the best one
141  for (const auto& fit : fit_curves) {
142  LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit));
143  if (current_fit.rms < best_fit.rms) {
144  best_fit = current_fit;
145  best_fit.complexity = fit;
146  }
147  }
148  } else {
149  best_fit = MinimalLeastSq(n, time, FittingCurve(complexity));
150  best_fit.complexity = complexity;
151  }
152 
153  return best_fit;
154 }
155 
156 std::vector<BenchmarkReporter::Run> ComputeBigO(
157  const std::vector<BenchmarkReporter::Run>& reports) {
158  typedef BenchmarkReporter::Run Run;
159  std::vector<Run> results;
160 
161  if (reports.size() < 2) return results;
162 
163  // Accumulators.
164  std::vector<int64_t> n;
165  std::vector<double> real_time;
166  std::vector<double> cpu_time;
167 
168  // Populate the accumulators.
169  for (const Run& run : reports) {
170  BM_CHECK_GT(run.complexity_n, 0)
171  << "Did you forget to call SetComplexityN?";
172  n.push_back(run.complexity_n);
173  real_time.push_back(run.real_accumulated_time / run.iterations);
174  cpu_time.push_back(run.cpu_accumulated_time / run.iterations);
175  }
176 
177  LeastSq result_cpu;
178  LeastSq result_real;
179 
180  if (reports[0].complexity == oLambda) {
181  result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda);
182  result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda);
183  } else {
184  result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity);
185  result_real = MinimalLeastSq(n, real_time, result_cpu.complexity);
186  }
187 
188  // Drop the 'args' when reporting complexity.
189  auto run_name = reports[0].run_name;
190  run_name.args.clear();
191 
192  // Get the data from the accumulator to BenchmarkReporter::Run's.
193  Run big_o;
194  big_o.run_name = run_name;
195  big_o.family_index = reports[0].family_index;
196  big_o.per_family_instance_index = reports[0].per_family_instance_index;
197  big_o.run_type = BenchmarkReporter::Run::RT_Aggregate;
198  big_o.repetitions = reports[0].repetitions;
199  big_o.repetition_index = Run::no_repetition_index;
200  big_o.threads = reports[0].threads;
201  big_o.aggregate_name = "BigO";
202  big_o.aggregate_unit = StatisticUnit::kTime;
203  big_o.report_label = reports[0].report_label;
204  big_o.iterations = 0;
205  big_o.real_accumulated_time = result_real.coef;
206  big_o.cpu_accumulated_time = result_cpu.coef;
207  big_o.report_big_o = true;
208  big_o.complexity = result_cpu.complexity;
209 
210  // All the time results are reported after being multiplied by the
211  // time unit multiplier. But since RMS is a relative quantity it
212  // should not be multiplied at all. So, here, we _divide_ it by the
213  // multiplier so that when it is multiplied later the result is the
214  // correct one.
215  double multiplier = GetTimeUnitMultiplier(reports[0].time_unit);
216 
217  // Only add label to mean/stddev if it is same for all runs
218  Run rms;
219  rms.run_name = run_name;
220  rms.family_index = reports[0].family_index;
221  rms.per_family_instance_index = reports[0].per_family_instance_index;
223  rms.aggregate_name = "RMS";
224  rms.aggregate_unit = StatisticUnit::kPercentage;
225  rms.report_label = big_o.report_label;
226  rms.iterations = 0;
227  rms.repetition_index = Run::no_repetition_index;
228  rms.repetitions = reports[0].repetitions;
229  rms.threads = reports[0].threads;
230  rms.real_accumulated_time = result_real.rms / multiplier;
231  rms.cpu_accumulated_time = result_cpu.rms / multiplier;
232  rms.report_rms = true;
233  rms.complexity = result_cpu.complexity;
234  // don't forget to keep the time unit, or we won't be able to
235  // recover the correct value.
236  rms.time_unit = reports[0].time_unit;
237 
238  results.push_back(big_o);
239  results.push_back(rms);
240  return results;
241 }
242 
243 } // end namespace benchmark
benchmark::kTime
@ kTime
Definition: benchmark/include/benchmark/benchmark.h:453
_gevent_test_main.result
result
Definition: _gevent_test_main.py:96
benchmark::oNSquared
@ oNSquared
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::BenchmarkReporter::Run::RT_Aggregate
@ RT_Aggregate
Definition: benchmark/include/benchmark/benchmark.h:1425
check.h
benchmark::oNone
@ oNone
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::kPercentage
@ kPercentage
Definition: benchmark/include/benchmark/benchmark.h:453
benchmark
Definition: bm_alarm.cc:55
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
kLog2E
static const double kLog2E
Definition: benchmark/test/complexity_test.cc:166
benchmark::LeastSq
Definition: benchmark/src/complexity.h:42
benchmark::oLambda
@ oLambda
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::LeastSq::coef
double coef
Definition: benchmark/src/complexity.h:45
benchmark::IterationCount
uint64_t IterationCount
Definition: benchmark/include/benchmark/benchmark.h:451
benchmark::oAuto
@ oAuto
Definition: benchmark/include/benchmark/benchmark.h:449
benchmarks.python.py_benchmark.results
list results
Definition: bloaty/third_party/protobuf/benchmarks/python/py_benchmark.py:145
BM_CHECK_NE
#define BM_CHECK_NE(a, b)
Definition: benchmark/src/check.h:69
benchmark::ComputeBigO
std::vector< BenchmarkReporter::Run > ComputeBigO(const std::vector< BenchmarkReporter::Run > &reports)
Definition: benchmark/src/complexity.cc:156
benchmark::oN
@ oN
Definition: benchmark/include/benchmark/benchmark.h:449
BM_CHECK_GE
#define BM_CHECK_GE(a, b)
Definition: benchmark/src/check.h:70
benchmark::BenchmarkReporter::Run
Definition: benchmark/include/benchmark/benchmark.h:1423
BM_CHECK_GT
#define BM_CHECK_GT(a, b)
Definition: benchmark/src/check.h:72
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
benchmark::GetBigOString
std::string GetBigOString(BigO complexity)
Definition: benchmark/src/complexity.cc:53
BM_CHECK_EQ
#define BM_CHECK_EQ(a, b)
Definition: benchmark/src/check.h:68
client.run
def run()
Definition: examples/python/async_streaming/client.py:109
benchmark::oLogN
@ oLogN
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::MinimalLeastSq
LeastSq MinimalLeastSq(const std::vector< int64_t > &n, const std::vector< double > &time, BigOFunc *fitting_curve)
Definition: benchmark/src/complexity.cc:82
benchmark::BigO
BigO
Definition: benchmark/include/benchmark/benchmark.h:449
log
bool log
Definition: abseil-cpp/absl/synchronization/mutex.cc:310
benchmark::LeastSq::complexity
BigO complexity
Definition: benchmark/src/complexity.h:47
benchmark::oNCubed
@ oNCubed
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::FittingCurve
BigOFunc * FittingCurve(BigO complexity)
Definition: benchmark/src/complexity.cc:28
benchmark::oNLogN
@ oNLogN
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::GetTimeUnitMultiplier
double GetTimeUnitMultiplier(TimeUnit unit)
Definition: benchmark/include/benchmark/benchmark.h:1664
googletest-break-on-failure-unittest.Run
def Run(command)
Definition: bloaty/third_party/googletest/googletest/test/googletest-break-on-failure-unittest.py:76
complexity.h
benchmark::o1
@ o1
Definition: benchmark/include/benchmark/benchmark.h:449
benchmark::LeastSq::rms
double rms
Definition: benchmark/src/complexity.h:46
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230
benchmark::BigOFunc
double() BigOFunc(IterationCount)
Definition: benchmark/include/benchmark/benchmark.h:457


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:52