//===----------------------------------------------------------------------===// // // 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 #include #include #include #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 { static constexpr const char* Names[] = {"Null", "FuncPtr", "MemFuncPtr", "MemPtr", "SmallTrivialFunctor", "SmallNonTrivialFunctor", "LargeTrivialFunctor", "LargeNonTrivialFunctor"}; }; enum class Opacity { kOpaque, kTransparent }; struct AllOpacity : EnumValuesAsTuple { 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; 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 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 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 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 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 struct OperatorBool { static void run(benchmark::State& state) { auto f = MakeFunction(FunctionType()); for (auto _ : state) { benchmark::DoNotOptimize(f); benchmark::DoNotOptimize(static_cast(f)); } } static std::string name() { return "BM_OperatorBool" + FunctionType::name(); } }; template 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 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(); makeCartesianProductBenchmark(); makeCartesianProductBenchmark(); makeCartesianProductBenchmark(); makeCartesianProductBenchmark(); makeCartesianProductBenchmark(); makeCartesianProductBenchmark(); benchmark::RunSpecifiedBenchmarks(); }