benchmark.hpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2014, 2015 Ableton AG, Berlin. All rights reserved.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21 //
22 
27 #pragma once
28 
30 
31 #include <ableton/build_system/Warnings.hpp>
32 ABL_DISABLE_WARNINGS
33 #include <boost/range/irange.hpp>
34 #include <boost/range/numeric.hpp>
35 #include <boost/range/algorithm/for_each.hpp>
36 ABL_RESTORE_WARNINGS
37 
38 #include <chrono>
39 #include <set>
40 #include <string>
41 #include <utility>
42 #include <tuple>
43 #include <iostream>
44 #include <functional>
45 
46 namespace atria {
47 namespace testing {
48 
56 void unoptimize(const void*);
57 
58 template <typename T>
59 void unoptimize(const T& x)
60 {
61  unoptimize(reinterpret_cast<const void*>(&x));
62 }
63 
68 template <typename FnT>
69 auto timeit(FnT&& fn) ->
70  std::chrono::duration<double, std::milli>
71 {
72  auto t0 = std::chrono::steady_clock::now();
73 
74  std::forward<FnT>(fn)();
75 
76  auto t1 = std::chrono::steady_clock::now();
77  return std::chrono::duration<double, std::milli>{t1 - t0};
78 }
79 
84 {
90  unsigned iterations = 100;
91 
96  unsigned measurements = 100;
97 
102  std::size_t size = 100;
103 
108  bool verbose = false;
109 };
110 
111 namespace detail {
112 
113 class benchmark_suite_base
114 {
115 public:
116  ~benchmark_suite_base();
117  benchmark_suite_base(const benchmark_suite_base&) = default;
118  benchmark_suite_base(std::string name, benchmark_settings settings)
119  : name_(std::move(name))
120  , settings_(std::move(settings))
121  {
122  if (settings_.verbose)
123  std::cout << std::endl << "running: " << name_ << " ..." << std::endl;
124  }
125 
126 protected:
127  const benchmark_settings& settings() { return settings_; }
128 
129  template <typename TestFnT>
130  void run(std::string name, TestFnT test_fn)
131  {
132  if (settings_.verbose)
133  std::cout << " ... " << name << std::endl;
134 
135  auto iteration = [&] (unsigned) { unoptimize(test_fn()); };
136  auto measure = [&] (double fastest, unsigned) {
137  const auto measured_time = timeit([&] {
138  boost::for_each(boost::irange(0u, settings_.iterations), iteration);
139  }).count();
140  return std::min(fastest, measured_time);
141  };
142 
143  results_.insert(
144  std::make_tuple(
145  boost::accumulate(
146  boost::irange(0u, settings_.measurements),
147  std::numeric_limits<double>::max(),
148  measure),
149  std::move(name)));
150  }
151 
152 private:
153  std::string name_;
154  benchmark_settings settings_;
155  std::set<std::tuple<double, std::string> > results_;
156 };
157 
158 } // namespace detail
159 
167 template <typename InitFnT=void>
168 class benchmark_suite : detail::benchmark_suite_base
169 {
170 public:
171  benchmark_suite(std::string name, InitFnT init_fn,
172  benchmark_settings settings)
173  : detail::benchmark_suite_base(std::move(name), std::move(settings))
174  , init_fn_(std::move(init_fn))
175  {}
176 
177  template <typename TestT>
178  benchmark_suite& operator() (std::string name, TestT test)
179  {
180  const auto data = init_fn_(settings());
181  run(std::move(name), std::bind(test, data));
182  return *this;
183  }
184 
185 private:
186  InitFnT init_fn_;
187 };
188 
189 template <>
190 class benchmark_suite<void> : detail::benchmark_suite_base
191 {
192 public:
193  using detail::benchmark_suite_base::benchmark_suite_base;
194 
195  template <typename TestT>
196  benchmark_suite& operator() (std::string name, TestT&& test)
197  {
198  run(std::move(name), std::forward<TestT>(test));
199  return *this;
200  }
201 };
202 
207 struct benchmark_runner_error : std::runtime_error
208 {
209  using std::runtime_error::runtime_error;
210 };
211 
257 {
258 public:
259  benchmark_runner(int argc, char const*const* argv,
260  benchmark_settings settings = {});
261 
262  template <typename InitFnT>
263  auto suite(std::string name, InitFnT&& init_fn)
265  {
267  std::move(name),
268  std::forward<InitFnT>(init_fn),
269  settings_
270  };
271  }
272 
273  auto suite(std::string name) -> benchmark_suite<>
274  {
275  return benchmark_suite<> {
276  std::move(name),
277  settings_
278  };
279  }
280 
281 private:
282  benchmark_settings settings_;
283 };
284 
290 template <typename FnT>
291 int benchmark_main(int argc, char const*const* argv, FnT&& fn)
292 {
293  try {
294  std::forward<FnT>(fn)(benchmark_runner(argc, argv));
295  } catch (const benchmark_runner_error& ex) {
296  std::cerr << ex.what() << std::endl;
297  return 1;
298  }
299  return 0;
300 }
301 
305 #define ABL_BENCHMARK_MAIN(fn) \
306  int main(int argc, char const*const* argv) \
307  { \
308  return ::atria::testing::benchmark_main(argc, argv, fn); \
309  } \
310 
311 
312 } // namespace testong
313 } // namespace atria
void unoptimize(const void *)
Utility to prevent calls to code the could be inlineable by the compiler being removed completely if ...
int benchmark_main(int argc, char const *const *argv, FnT &&fn)
Creates a benchmark_runner and passes it to fn, returning a zero (success) code.
Definition: benchmark.hpp:291
constexpr auto count(InitT init=InitT{0}, StepT step=StepT{1}) -> count_t< InitT, StepT >
Generator transducer produces a sequence:
Definition: count.hpp:78
Settings to configure a benchmark run.
Definition: benchmark.hpp:83
bool verbose
Amount of data to use in the benchmark.
Definition: benchmark.hpp:108
unsigned measurements
Number of measurements or attempts to make per benchmark.
Definition: benchmark.hpp:96
Error thrown by the benchmark_runner when it should not execute.
Definition: benchmark.hpp:207
auto timeit(FnT &&fn) -> std::chrono::duration< double, std::milli >
In the spirit of Python's timeit(), takes a nullary function and evaluates it, calculating how long i...
Definition: benchmark.hpp:69
std::size_t size
Amount of data to use in the benchmark.
Definition: benchmark.hpp:102
A suite of benchmarks.
Definition: benchmark.hpp:168
void run(XformT &&xform, InputRangeTs &&...ranges)
Runs a transducer composed with no significant reduction.
Definition: run.hpp:48
C++ amazing templates and reusable implementations awesomeness.
Definition: _doc.hpp:35
unsigned iterations
How many times the benchmark is run per measurement.
Definition: benchmark.hpp:90
A class to run multiple suites of benchmarks.
Definition: benchmark.hpp:256
Fork me on GitHub