| //===----------------------------------------------------------------------===// | 
 | // | 
 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
 | // See https://llvm.org/LICENSE.txt for license information. | 
 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | #include <cstdint> | 
 | #include <functional> | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "CartesianBenchmarks.h" | 
 | #include "benchmark/benchmark.h" | 
 | #include "test_macros.h" | 
 |  | 
 | namespace { | 
 |  | 
 | enum class FunctionType { | 
 |   Null, | 
 |   FunctionPointer, | 
 |   MemberFunctionPointer, | 
 |   MemberPointer, | 
 |   SmallTrivialFunctor, | 
 |   SmallNonTrivialFunctor, | 
 |   LargeTrivialFunctor, | 
 |   LargeNonTrivialFunctor | 
 | }; | 
 |  | 
 | struct AllFunctionTypes : EnumValuesAsTuple<AllFunctionTypes, FunctionType, 8> { | 
 |   static constexpr const char* Names[] = {"Null", | 
 |                                           "FuncPtr", | 
 |                                           "MemFuncPtr", | 
 |                                           "MemPtr", | 
 |                                           "SmallTrivialFunctor", | 
 |                                           "SmallNonTrivialFunctor", | 
 |                                           "LargeTrivialFunctor", | 
 |                                           "LargeNonTrivialFunctor"}; | 
 | }; | 
 |  | 
 | enum class Opacity { kOpaque, kTransparent }; | 
 |  | 
 | struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> { | 
 |   static constexpr const char* Names[] = {"Opaque", "Transparent"}; | 
 | }; | 
 |  | 
 | struct S { | 
 |   int function() const { return 0; } | 
 |   int field = 0; | 
 | }; | 
 |  | 
 | int FunctionWithS(const S*) { return 0; } | 
 |  | 
 | struct SmallTrivialFunctor { | 
 |   int operator()(const S*) const { return 0; } | 
 | }; | 
 | struct SmallNonTrivialFunctor { | 
 |   SmallNonTrivialFunctor() {} | 
 |   SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {} | 
 |   ~SmallNonTrivialFunctor() {} | 
 |   int operator()(const S*) const { return 0; } | 
 | }; | 
 | struct LargeTrivialFunctor { | 
 |   LargeTrivialFunctor() { | 
 |       // Do not spend time initializing the padding. | 
 |   } | 
 |   int padding[16]; | 
 |   int operator()(const S*) const { return 0; } | 
 | }; | 
 | struct LargeNonTrivialFunctor { | 
 |   int padding[16]; | 
 |   LargeNonTrivialFunctor() { | 
 |       // Do not spend time initializing the padding. | 
 |   } | 
 |   LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {} | 
 |   ~LargeNonTrivialFunctor() {} | 
 |   int operator()(const S*) const { return 0; } | 
 | }; | 
 |  | 
 | using Function = std::function<int(const S*)>; | 
 |  | 
 | TEST_ALWAYS_INLINE | 
 | inline Function MakeFunction(FunctionType type, bool opaque = false) { | 
 |   switch (type) { | 
 |     case FunctionType::Null: | 
 |       return nullptr; | 
 |     case FunctionType::FunctionPointer: | 
 |       return maybeOpaque(FunctionWithS, opaque); | 
 |     case FunctionType::MemberFunctionPointer: | 
 |       return maybeOpaque(&S::function, opaque); | 
 |     case FunctionType::MemberPointer: | 
 |       return maybeOpaque(&S::field, opaque); | 
 |     case FunctionType::SmallTrivialFunctor: | 
 |       return maybeOpaque(SmallTrivialFunctor{}, opaque); | 
 |     case FunctionType::SmallNonTrivialFunctor: | 
 |       return maybeOpaque(SmallNonTrivialFunctor{}, opaque); | 
 |     case FunctionType::LargeTrivialFunctor: | 
 |       return maybeOpaque(LargeTrivialFunctor{}, opaque); | 
 |     case FunctionType::LargeNonTrivialFunctor: | 
 |       return maybeOpaque(LargeNonTrivialFunctor{}, opaque); | 
 |   } | 
 | } | 
 |  | 
 | template <class Opacity, class FunctionType> | 
 | struct ConstructAndDestroy { | 
 |   static void run(benchmark::State& state) { | 
 |     for (auto _ : state) { | 
 |       if (Opacity() == ::Opacity::kOpaque) { | 
 |         benchmark::DoNotOptimize(MakeFunction(FunctionType(), true)); | 
 |       } else { | 
 |         MakeFunction(FunctionType()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   static std::string name() { | 
 |     return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class FunctionType> | 
 | struct Copy { | 
 |   static void run(benchmark::State& state) { | 
 |     auto value = MakeFunction(FunctionType()); | 
 |     for (auto _ : state) { | 
 |       benchmark::DoNotOptimize(value); | 
 |       auto copy = value;  // NOLINT | 
 |       benchmark::DoNotOptimize(copy); | 
 |     } | 
 |   } | 
 |  | 
 |   static std::string name() { return "BM_Copy" + FunctionType::name(); } | 
 | }; | 
 |  | 
 | template <class FunctionType> | 
 | struct Move { | 
 |   static void run(benchmark::State& state) { | 
 |     Function values[2] = {MakeFunction(FunctionType())}; | 
 |     int i = 0; | 
 |     for (auto _ : state) { | 
 |       benchmark::DoNotOptimize(values); | 
 |       benchmark::DoNotOptimize(values[i ^ 1] = std::move(values[i])); | 
 |       i ^= 1; | 
 |     } | 
 |   } | 
 |  | 
 |   static std::string name() { | 
 |     return "BM_Move" + FunctionType::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class Function1, class Function2> | 
 | struct Swap { | 
 |   static void run(benchmark::State& state) { | 
 |     Function values[2] = {MakeFunction(Function1()), MakeFunction(Function2())}; | 
 |     for (auto _ : state) { | 
 |       benchmark::DoNotOptimize(values); | 
 |       values[0].swap(values[1]); | 
 |     } | 
 |   } | 
 |  | 
 |   static bool skip() { return Function1() > Function2(); } | 
 |  | 
 |   static std::string name() { | 
 |     return "BM_Swap" + Function1::name() + Function2::name(); | 
 |   } | 
 | }; | 
 |  | 
 | template <class FunctionType> | 
 | struct OperatorBool { | 
 |   static void run(benchmark::State& state) { | 
 |     auto f = MakeFunction(FunctionType()); | 
 |     for (auto _ : state) { | 
 |       benchmark::DoNotOptimize(f); | 
 |       benchmark::DoNotOptimize(static_cast<bool>(f)); | 
 |     } | 
 |   } | 
 |  | 
 |   static std::string name() { return "BM_OperatorBool" + FunctionType::name(); } | 
 | }; | 
 |  | 
 | template <class FunctionType> | 
 | struct Invoke { | 
 |   static void run(benchmark::State& state) { | 
 |     S s; | 
 |     const auto value = MakeFunction(FunctionType()); | 
 |     for (auto _ : state) { | 
 |       benchmark::DoNotOptimize(value); | 
 |       benchmark::DoNotOptimize(value(&s)); | 
 |     } | 
 |   } | 
 |  | 
 |   static bool skip() { return FunctionType() == ::FunctionType::Null; } | 
 |  | 
 |   static std::string name() { return "BM_Invoke" + FunctionType::name(); } | 
 | }; | 
 |  | 
 | template <class FunctionType> | 
 | struct InvokeInlined { | 
 |   static void run(benchmark::State& state) { | 
 |     S s; | 
 |     for (auto _ : state) { | 
 |       MakeFunction(FunctionType())(&s); | 
 |     } | 
 |   } | 
 |  | 
 |   static bool skip() { return FunctionType() == ::FunctionType::Null; } | 
 |  | 
 |   static std::string name() { | 
 |     return "BM_InvokeInlined" + FunctionType::name(); | 
 |   } | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | int main(int argc, char** argv) { | 
 |   benchmark::Initialize(&argc, argv); | 
 |   if (benchmark::ReportUnrecognizedArguments(argc, argv)) | 
 |     return 1; | 
 |  | 
 |   makeCartesianProductBenchmark<ConstructAndDestroy, AllOpacity, | 
 |                                 AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<Copy, AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<Move, AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<Swap, AllFunctionTypes, AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<OperatorBool, AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<Invoke, AllFunctionTypes>(); | 
 |   makeCartesianProductBenchmark<InvokeInlined, AllFunctionTypes>(); | 
 |   benchmark::RunSpecifiedBenchmarks(); | 
 | } |