benchmarks/once_function_benchmark.cpp (119 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE.md file in the root directory of this source tree. #include <cstring> #include <deque> #include <functional> #include <dispenso/once_function.h> #include "benchmark_common.h" constexpr size_t kSmallSize = 24; constexpr size_t kMediumSize = 120; constexpr size_t kLargeSize = 248; // 1000 is larger than our largest optimized chunk size, so we may expect to see performance falloff // here. constexpr size_t kExtraLargeSize = 1000; template <typename ExeType, typename Func> void runMoveLoop(benchmark::State& state, Func f) { for (auto UNUSED_VAR : state) { ExeType t(f); ExeType o; for (int i = 0; i < 10; ++i) { o = std::move(t); t = std::move(o); } t(); } } template <typename Func> class FuncConsumer { public: void add(Func&& f) { funcs_.emplace_back(std::move(f)); } void consumeAll() { while (!funcs_.empty()) { Func f = std::move(funcs_.front()); funcs_.pop_front(); f(); } } private: std::deque<Func> funcs_; }; template <size_t kSize> struct Foo { Foo() { buf[0] = 0; benchmark::ClobberMemory(); } Foo(Foo<kSize>&& f) { std::memcpy(buf, f.buf, kSize); } Foo(const Foo<kSize>& f) { std::memcpy(buf, f.buf, kSize); } void operator()() { benchmark::DoNotOptimize(++buf[0]); } uint32_t buf[kSize / 4]; }; template <typename F> void onceCall(F&& f) { F lf = std::move(f); lf(); } template <size_t kSize> void BM_move_std_function(benchmark::State& state) { runMoveLoop<std::function<void()>>(state, Foo<kSize>()); } template <size_t kSize> void BM_move_once_function(benchmark::State& state) { runMoveLoop<dispenso::OnceFunction>(state, Foo<kSize>()); } constexpr int kMediumLoopLen = 200; template <size_t kSize> void BM_queue_inline_function(benchmark::State& state) { FuncConsumer<Foo<kSize>> consumer; for (auto UNUSED_VAR : state) { for (int i = 0; i < kMediumLoopLen; ++i) { consumer.add(Foo<kSize>()); } consumer.consumeAll(); } } template <size_t kSize> void BM_queue_std_function(benchmark::State& state) { FuncConsumer<std::function<void()>> consumer; for (auto UNUSED_VAR : state) { for (int i = 0; i < kMediumLoopLen; ++i) { consumer.add(Foo<kSize>()); } consumer.consumeAll(); } } template <size_t kSize> void BM_queue_once_function(benchmark::State& state) { FuncConsumer<dispenso::OnceFunction> consumer; for (auto UNUSED_VAR : state) { for (int i = 0; i < kMediumLoopLen; ++i) { consumer.add(Foo<kSize>()); } consumer.consumeAll(); } } BENCHMARK_TEMPLATE(BM_move_std_function, kSmallSize); BENCHMARK_TEMPLATE(BM_move_once_function, kSmallSize); BENCHMARK_TEMPLATE(BM_move_std_function, kMediumSize); BENCHMARK_TEMPLATE(BM_move_once_function, kMediumSize); BENCHMARK_TEMPLATE(BM_move_std_function, kLargeSize); BENCHMARK_TEMPLATE(BM_move_once_function, kLargeSize); BENCHMARK_TEMPLATE(BM_move_std_function, kExtraLargeSize); BENCHMARK_TEMPLATE(BM_move_once_function, kExtraLargeSize); BENCHMARK_TEMPLATE(BM_queue_inline_function, kSmallSize); BENCHMARK_TEMPLATE(BM_queue_std_function, kSmallSize); BENCHMARK_TEMPLATE(BM_queue_once_function, kSmallSize); BENCHMARK_TEMPLATE(BM_queue_inline_function, kMediumSize); BENCHMARK_TEMPLATE(BM_queue_std_function, kMediumSize); BENCHMARK_TEMPLATE(BM_queue_once_function, kMediumSize); BENCHMARK_TEMPLATE(BM_queue_inline_function, kLargeSize); BENCHMARK_TEMPLATE(BM_queue_std_function, kLargeSize); BENCHMARK_TEMPLATE(BM_queue_once_function, kLargeSize); BENCHMARK_TEMPLATE(BM_queue_inline_function, kExtraLargeSize); BENCHMARK_TEMPLATE(BM_queue_std_function, kExtraLargeSize); BENCHMARK_TEMPLATE(BM_queue_once_function, kExtraLargeSize); BENCHMARK_MAIN();