| //===----------------------------------------------------------------------===// | |
| // | |
| // 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(); | |
| } |