xref: /llvm-project/libcxx/test/benchmarks/function.bench.cpp (revision b2d2494731976ab7aa9702f3134472db694b9332)
178b4b5ccSLouis Dionne //===----------------------------------------------------------------------===//
278b4b5ccSLouis Dionne //
378b4b5ccSLouis Dionne // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
478b4b5ccSLouis Dionne // See https://llvm.org/LICENSE.txt for license information.
578b4b5ccSLouis Dionne // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
678b4b5ccSLouis Dionne //
778b4b5ccSLouis Dionne //===----------------------------------------------------------------------===//
878b4b5ccSLouis Dionne 
9*b2d24947SLouis Dionne // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10*b2d24947SLouis Dionne 
1178b4b5ccSLouis Dionne #include <cstdint>
1278b4b5ccSLouis Dionne #include <functional>
1378b4b5ccSLouis Dionne #include <memory>
1478b4b5ccSLouis Dionne #include <string>
15*b2d24947SLouis Dionne #include <utility>
1678b4b5ccSLouis Dionne 
1778b4b5ccSLouis Dionne #include "CartesianBenchmarks.h"
1878b4b5ccSLouis Dionne #include "benchmark/benchmark.h"
1978b4b5ccSLouis Dionne #include "test_macros.h"
2078b4b5ccSLouis Dionne 
2178b4b5ccSLouis Dionne namespace {
2278b4b5ccSLouis Dionne 
2378b4b5ccSLouis Dionne enum class FunctionType {
2478b4b5ccSLouis Dionne   Null,
2578b4b5ccSLouis Dionne   FunctionPointer,
2678b4b5ccSLouis Dionne   MemberFunctionPointer,
2778b4b5ccSLouis Dionne   MemberPointer,
2878b4b5ccSLouis Dionne   SmallTrivialFunctor,
2978b4b5ccSLouis Dionne   SmallNonTrivialFunctor,
3078b4b5ccSLouis Dionne   LargeTrivialFunctor,
3178b4b5ccSLouis Dionne   LargeNonTrivialFunctor
3278b4b5ccSLouis Dionne };
3378b4b5ccSLouis Dionne 
3478b4b5ccSLouis Dionne struct AllFunctionTypes : EnumValuesAsTuple<AllFunctionTypes, FunctionType, 8> {
3578b4b5ccSLouis Dionne   static constexpr const char* Names[] = {
3678b4b5ccSLouis Dionne       "Null",
3778b4b5ccSLouis Dionne       "FuncPtr",
3878b4b5ccSLouis Dionne       "MemFuncPtr",
3978b4b5ccSLouis Dionne       "MemPtr",
4078b4b5ccSLouis Dionne       "SmallTrivialFunctor",
4178b4b5ccSLouis Dionne       "SmallNonTrivialFunctor",
4278b4b5ccSLouis Dionne       "LargeTrivialFunctor",
4378b4b5ccSLouis Dionne       "LargeNonTrivialFunctor"};
4478b4b5ccSLouis Dionne };
4578b4b5ccSLouis Dionne 
4678b4b5ccSLouis Dionne enum class Opacity { kOpaque, kTransparent };
4778b4b5ccSLouis Dionne 
4878b4b5ccSLouis Dionne struct AllOpacity : EnumValuesAsTuple<AllOpacity, Opacity, 2> {
4978b4b5ccSLouis Dionne   static constexpr const char* Names[] = {"Opaque", "Transparent"};
5078b4b5ccSLouis Dionne };
5178b4b5ccSLouis Dionne 
5278b4b5ccSLouis Dionne struct S {
5378b4b5ccSLouis Dionne   int function() const { return 0; }
5478b4b5ccSLouis Dionne   int field = 0;
5578b4b5ccSLouis Dionne };
5678b4b5ccSLouis Dionne 
5778b4b5ccSLouis Dionne int FunctionWithS(const S*) { return 0; }
5878b4b5ccSLouis Dionne 
5978b4b5ccSLouis Dionne struct SmallTrivialFunctor {
6078b4b5ccSLouis Dionne   int operator()(const S*) const { return 0; }
6178b4b5ccSLouis Dionne };
6278b4b5ccSLouis Dionne struct SmallNonTrivialFunctor {
6378b4b5ccSLouis Dionne   SmallNonTrivialFunctor() {}
6478b4b5ccSLouis Dionne   SmallNonTrivialFunctor(const SmallNonTrivialFunctor&) {}
6578b4b5ccSLouis Dionne   ~SmallNonTrivialFunctor() {}
6678b4b5ccSLouis Dionne   int operator()(const S*) const { return 0; }
6778b4b5ccSLouis Dionne };
6878b4b5ccSLouis Dionne struct LargeTrivialFunctor {
6978b4b5ccSLouis Dionne   LargeTrivialFunctor() {
7078b4b5ccSLouis Dionne     // Do not spend time initializing the padding.
7178b4b5ccSLouis Dionne   }
7278b4b5ccSLouis Dionne   int padding[16];
7378b4b5ccSLouis Dionne   int operator()(const S*) const { return 0; }
7478b4b5ccSLouis Dionne };
7578b4b5ccSLouis Dionne struct LargeNonTrivialFunctor {
7678b4b5ccSLouis Dionne   int padding[16];
7778b4b5ccSLouis Dionne   LargeNonTrivialFunctor() {
7878b4b5ccSLouis Dionne     // Do not spend time initializing the padding.
7978b4b5ccSLouis Dionne   }
8078b4b5ccSLouis Dionne   LargeNonTrivialFunctor(const LargeNonTrivialFunctor&) {}
8178b4b5ccSLouis Dionne   ~LargeNonTrivialFunctor() {}
8278b4b5ccSLouis Dionne   int operator()(const S*) const { return 0; }
8378b4b5ccSLouis Dionne };
8478b4b5ccSLouis Dionne 
8578b4b5ccSLouis Dionne using Function = std::function<int(const S*)>;
8678b4b5ccSLouis Dionne 
8778b4b5ccSLouis Dionne TEST_ALWAYS_INLINE
8878b4b5ccSLouis Dionne inline Function MakeFunction(FunctionType type, bool opaque = false) {
8978b4b5ccSLouis Dionne   switch (type) {
9078b4b5ccSLouis Dionne   case FunctionType::Null:
9178b4b5ccSLouis Dionne     return nullptr;
9278b4b5ccSLouis Dionne   case FunctionType::FunctionPointer:
9378b4b5ccSLouis Dionne     return maybeOpaque(FunctionWithS, opaque);
9478b4b5ccSLouis Dionne   case FunctionType::MemberFunctionPointer:
9578b4b5ccSLouis Dionne     return maybeOpaque(&S::function, opaque);
9678b4b5ccSLouis Dionne   case FunctionType::MemberPointer:
9778b4b5ccSLouis Dionne     return maybeOpaque(&S::field, opaque);
9878b4b5ccSLouis Dionne   case FunctionType::SmallTrivialFunctor:
9978b4b5ccSLouis Dionne     return maybeOpaque(SmallTrivialFunctor{}, opaque);
10078b4b5ccSLouis Dionne   case FunctionType::SmallNonTrivialFunctor:
10178b4b5ccSLouis Dionne     return maybeOpaque(SmallNonTrivialFunctor{}, opaque);
10278b4b5ccSLouis Dionne   case FunctionType::LargeTrivialFunctor:
10378b4b5ccSLouis Dionne     return maybeOpaque(LargeTrivialFunctor{}, opaque);
10478b4b5ccSLouis Dionne   case FunctionType::LargeNonTrivialFunctor:
10578b4b5ccSLouis Dionne     return maybeOpaque(LargeNonTrivialFunctor{}, opaque);
10678b4b5ccSLouis Dionne   }
107*b2d24947SLouis Dionne   std::unreachable();
10878b4b5ccSLouis Dionne }
10978b4b5ccSLouis Dionne 
11078b4b5ccSLouis Dionne template <class Opacity, class FunctionType>
11178b4b5ccSLouis Dionne struct ConstructAndDestroy {
11278b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
11378b4b5ccSLouis Dionne     for (auto _ : state) {
11478b4b5ccSLouis Dionne       if (Opacity() == ::Opacity::kOpaque) {
11578b4b5ccSLouis Dionne         benchmark::DoNotOptimize(MakeFunction(FunctionType(), true));
11678b4b5ccSLouis Dionne       } else {
11778b4b5ccSLouis Dionne         MakeFunction(FunctionType());
11878b4b5ccSLouis Dionne       }
11978b4b5ccSLouis Dionne     }
12078b4b5ccSLouis Dionne   }
12178b4b5ccSLouis Dionne 
12278b4b5ccSLouis Dionne   static std::string name() { return "BM_ConstructAndDestroy" + FunctionType::name() + Opacity::name(); }
12378b4b5ccSLouis Dionne };
12478b4b5ccSLouis Dionne 
12578b4b5ccSLouis Dionne template <class FunctionType>
12678b4b5ccSLouis Dionne struct Copy {
12778b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
12878b4b5ccSLouis Dionne     auto value = MakeFunction(FunctionType());
12978b4b5ccSLouis Dionne     for (auto _ : state) {
13078b4b5ccSLouis Dionne       benchmark::DoNotOptimize(value);
13178b4b5ccSLouis Dionne       auto copy = value; // NOLINT
13278b4b5ccSLouis Dionne       benchmark::DoNotOptimize(copy);
13378b4b5ccSLouis Dionne     }
13478b4b5ccSLouis Dionne   }
13578b4b5ccSLouis Dionne 
13678b4b5ccSLouis Dionne   static std::string name() { return "BM_Copy" + FunctionType::name(); }
13778b4b5ccSLouis Dionne };
13878b4b5ccSLouis Dionne 
13978b4b5ccSLouis Dionne template <class FunctionType>
14078b4b5ccSLouis Dionne struct Move {
14178b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
14278b4b5ccSLouis Dionne     Function values[2] = {MakeFunction(FunctionType())};
14378b4b5ccSLouis Dionne     int i              = 0;
14478b4b5ccSLouis Dionne     for (auto _ : state) {
14578b4b5ccSLouis Dionne       benchmark::DoNotOptimize(values);
14678b4b5ccSLouis Dionne       benchmark::DoNotOptimize(values[i ^ 1] = std::move(values[i]));
14778b4b5ccSLouis Dionne       i ^= 1;
14878b4b5ccSLouis Dionne     }
14978b4b5ccSLouis Dionne   }
15078b4b5ccSLouis Dionne 
15178b4b5ccSLouis Dionne   static std::string name() { return "BM_Move" + FunctionType::name(); }
15278b4b5ccSLouis Dionne };
15378b4b5ccSLouis Dionne 
15478b4b5ccSLouis Dionne template <class Function1, class Function2>
15578b4b5ccSLouis Dionne struct Swap {
15678b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
15778b4b5ccSLouis Dionne     Function values[2] = {MakeFunction(Function1()), MakeFunction(Function2())};
15878b4b5ccSLouis Dionne     for (auto _ : state) {
15978b4b5ccSLouis Dionne       benchmark::DoNotOptimize(values);
16078b4b5ccSLouis Dionne       values[0].swap(values[1]);
16178b4b5ccSLouis Dionne     }
16278b4b5ccSLouis Dionne   }
16378b4b5ccSLouis Dionne 
16478b4b5ccSLouis Dionne   static bool skip() { return Function1() > Function2(); }
16578b4b5ccSLouis Dionne 
16678b4b5ccSLouis Dionne   static std::string name() { return "BM_Swap" + Function1::name() + Function2::name(); }
16778b4b5ccSLouis Dionne };
16878b4b5ccSLouis Dionne 
16978b4b5ccSLouis Dionne template <class FunctionType>
17078b4b5ccSLouis Dionne struct OperatorBool {
17178b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
17278b4b5ccSLouis Dionne     auto f = MakeFunction(FunctionType());
17378b4b5ccSLouis Dionne     for (auto _ : state) {
17478b4b5ccSLouis Dionne       benchmark::DoNotOptimize(f);
17578b4b5ccSLouis Dionne       benchmark::DoNotOptimize(static_cast<bool>(f));
17678b4b5ccSLouis Dionne     }
17778b4b5ccSLouis Dionne   }
17878b4b5ccSLouis Dionne 
17978b4b5ccSLouis Dionne   static std::string name() { return "BM_OperatorBool" + FunctionType::name(); }
18078b4b5ccSLouis Dionne };
18178b4b5ccSLouis Dionne 
18278b4b5ccSLouis Dionne template <class FunctionType>
18378b4b5ccSLouis Dionne struct Invoke {
18478b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
18578b4b5ccSLouis Dionne     S s;
186*b2d24947SLouis Dionne     auto value = MakeFunction(FunctionType());
18778b4b5ccSLouis Dionne     for (auto _ : state) {
18878b4b5ccSLouis Dionne       benchmark::DoNotOptimize(value);
18978b4b5ccSLouis Dionne       benchmark::DoNotOptimize(value(&s));
19078b4b5ccSLouis Dionne     }
19178b4b5ccSLouis Dionne   }
19278b4b5ccSLouis Dionne 
19378b4b5ccSLouis Dionne   static bool skip() { return FunctionType() == ::FunctionType::Null; }
19478b4b5ccSLouis Dionne 
19578b4b5ccSLouis Dionne   static std::string name() { return "BM_Invoke" + FunctionType::name(); }
19678b4b5ccSLouis Dionne };
19778b4b5ccSLouis Dionne 
19878b4b5ccSLouis Dionne template <class FunctionType>
19978b4b5ccSLouis Dionne struct InvokeInlined {
20078b4b5ccSLouis Dionne   static void run(benchmark::State& state) {
20178b4b5ccSLouis Dionne     S s;
20278b4b5ccSLouis Dionne     for (auto _ : state) {
20378b4b5ccSLouis Dionne       MakeFunction(FunctionType())(&s);
20478b4b5ccSLouis Dionne     }
20578b4b5ccSLouis Dionne   }
20678b4b5ccSLouis Dionne 
20778b4b5ccSLouis Dionne   static bool skip() { return FunctionType() == ::FunctionType::Null; }
20878b4b5ccSLouis Dionne 
20978b4b5ccSLouis Dionne   static std::string name() { return "BM_InvokeInlined" + FunctionType::name(); }
21078b4b5ccSLouis Dionne };
21178b4b5ccSLouis Dionne 
21278b4b5ccSLouis Dionne } // namespace
21378b4b5ccSLouis Dionne 
21478b4b5ccSLouis Dionne int main(int argc, char** argv) {
21578b4b5ccSLouis Dionne   benchmark::Initialize(&argc, argv);
21678b4b5ccSLouis Dionne   if (benchmark::ReportUnrecognizedArguments(argc, argv))
21778b4b5ccSLouis Dionne     return 1;
21878b4b5ccSLouis Dionne 
21978b4b5ccSLouis Dionne   makeCartesianProductBenchmark<ConstructAndDestroy, AllOpacity, AllFunctionTypes>();
22078b4b5ccSLouis Dionne   makeCartesianProductBenchmark<Copy, AllFunctionTypes>();
22178b4b5ccSLouis Dionne   makeCartesianProductBenchmark<Move, AllFunctionTypes>();
22278b4b5ccSLouis Dionne   makeCartesianProductBenchmark<Swap, AllFunctionTypes, AllFunctionTypes>();
22378b4b5ccSLouis Dionne   makeCartesianProductBenchmark<OperatorBool, AllFunctionTypes>();
22478b4b5ccSLouis Dionne   makeCartesianProductBenchmark<Invoke, AllFunctionTypes>();
22578b4b5ccSLouis Dionne   makeCartesianProductBenchmark<InvokeInlined, AllFunctionTypes>();
22678b4b5ccSLouis Dionne   benchmark::RunSpecifiedBenchmarks();
22778b4b5ccSLouis Dionne }
228