• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1     /*
2  *  Created by Joachim on 16/04/2019.
3  *  Adapted from donated nonius code.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 
9  // Dumb std::function implementation for consistent call overhead
10 
11 #ifndef TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
12 #define TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
13 
14 #include "../catch_chronometer.hpp"
15 #include "catch_complete_invoke.hpp"
16 #include "../../catch_meta.hpp"
17 
18 #include <cassert>
19 #include <type_traits>
20 #include <utility>
21 #include <memory>
22 
23 namespace Catch {
24     namespace Benchmark {
25         namespace Detail {
26             template <typename T>
27             using Decay = typename std::decay<T>::type;
28             template <typename T, typename U>
29             struct is_related
30                 : std::is_same<Decay<T>, Decay<U>> {};
31 
32             /// We need to reinvent std::function because every piece of code that might add overhead
33             /// in a measurement context needs to have consistent performance characteristics so that we
34             /// can account for it in the measurement.
35             /// Implementations of std::function with optimizations that aren't always applicable, like
36             /// small buffer optimizations, are not uncommon.
37             /// This is effectively an implementation of std::function without any such optimizations;
38             /// it may be slow, but it is consistently slow.
39             struct BenchmarkFunction {
40             private:
41                 struct callable {
42                     virtual void call(Chronometer meter) const = 0;
43                     virtual callable* clone() const = 0;
44                     virtual ~callable() = default;
45                 };
46                 template <typename Fun>
47                 struct model : public callable {
modelCatch::Benchmark::Detail::BenchmarkFunction::model48                     model(Fun&& fun) : fun(std::move(fun)) {}
modelCatch::Benchmark::Detail::BenchmarkFunction::model49                     model(Fun const& fun) : fun(fun) {}
50 
cloneCatch::Benchmark::Detail::BenchmarkFunction::model51                     model<Fun>* clone() const override { return new model<Fun>(*this); }
52 
callCatch::Benchmark::Detail::BenchmarkFunction::model53                     void call(Chronometer meter) const override {
54                         call(meter, is_callable<Fun(Chronometer)>());
55                     }
callCatch::Benchmark::Detail::BenchmarkFunction::model56                     void call(Chronometer meter, std::true_type) const {
57                         fun(meter);
58                     }
callCatch::Benchmark::Detail::BenchmarkFunction::model59                     void call(Chronometer meter, std::false_type) const {
60                         meter.measure(fun);
61                     }
62 
63                     Fun fun;
64                 };
65 
operator ()Catch::Benchmark::Detail::BenchmarkFunction::do_nothing66                 struct do_nothing { void operator()() const {} };
67 
68                 template <typename T>
BenchmarkFunctionCatch::Benchmark::Detail::BenchmarkFunction69                 BenchmarkFunction(model<T>* c) : f(c) {}
70 
71             public:
BenchmarkFunctionCatch::Benchmark::Detail::BenchmarkFunction72                 BenchmarkFunction()
73                     : f(new model<do_nothing>{ {} }) {}
74 
75                 template <typename Fun,
76                     typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
BenchmarkFunctionCatch::Benchmark::Detail::BenchmarkFunction77                     BenchmarkFunction(Fun&& fun)
78                     : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
79 
BenchmarkFunctionCatch::Benchmark::Detail::BenchmarkFunction80                 BenchmarkFunction(BenchmarkFunction&& that)
81                     : f(std::move(that.f)) {}
82 
BenchmarkFunctionCatch::Benchmark::Detail::BenchmarkFunction83                 BenchmarkFunction(BenchmarkFunction const& that)
84                     : f(that.f->clone()) {}
85 
operator =Catch::Benchmark::Detail::BenchmarkFunction86                 BenchmarkFunction& operator=(BenchmarkFunction&& that) {
87                     f = std::move(that.f);
88                     return *this;
89                 }
90 
operator =Catch::Benchmark::Detail::BenchmarkFunction91                 BenchmarkFunction& operator=(BenchmarkFunction const& that) {
92                     f.reset(that.f->clone());
93                     return *this;
94                 }
95 
operator ()Catch::Benchmark::Detail::BenchmarkFunction96                 void operator()(Chronometer meter) const { f->call(meter); }
97 
98             private:
99                 std::unique_ptr<callable> f;
100             };
101         } // namespace Detail
102     } // namespace Benchmark
103 } // namespace Catch
104 
105 #endif // TWOBLUECUBES_CATCH_DETAIL_BENCHMARK_FUNCTION_HPP_INCLUDED
106