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