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