|
@@ -0,0 +1,232 @@
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+//
|
|
|
+// The LLVM Compiler Infrastructure
|
|
|
+//
|
|
|
+// This file is dual licensed under the MIT and the University of Illinois Open
|
|
|
+// Source Licenses. See LICENSE.TXT for details.
|
|
|
+//
|
|
|
+//===----------------------------------------------------------------------===//
|
|
|
+
|
|
|
+#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; }
|
|
|
+ int field;
|
|
|
+};
|
|
|
+
|
|
|
+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();
|
|
|
+}
|