2018-10-13 05:01:15 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2019-01-19 18:56:40 +08:00
|
|
|
// 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
|
2018-10-13 05:01:15 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
#include <functional>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include "CartesianBenchmarks.hpp"
|
|
|
|
#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; }
|
2018-11-14 07:00:55 +08:00
|
|
|
int field = 0;
|
2018-10-13 05:01:15 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|