diff --git a/benchmarks/bench_reference.cpp b/benchmarks/bench_reference.cpp new file mode 100644 index 0000000..d3005fb --- /dev/null +++ b/benchmarks/bench_reference.cpp @@ -0,0 +1,406 @@ +#include +#include +#include + +#include + +#include "reference.hpp" + +namespace { + +struct TestObject { + int64_t data = 42; + + TestObject() = default; + explicit TestObject(int64_t value) : data(value) {} +}; + +// Trait helpers for templated benchmarks +template struct PointerTraits; + +template struct PointerTraits> { + using pointer_type = std::shared_ptr; + using weak_type = std::weak_ptr; + + template static pointer_type make(Args &&...args) { + return std::make_shared(std::forward(args)...); + } + + static const char *name() { return "std::shared_ptr"; } + static const char *weak_name() { return "std::weak_ptr"; } +}; + +template struct PointerTraits> { + using pointer_type = Ref; + using weak_type = WeakRef; + + template static pointer_type make(Args &&...args) { + return make_ref(std::forward(args)...); + } + + static const char *name() { return "Ref"; } + static const char *weak_name() { return "WeakRef"; } +}; + +template +void benchmark_creation(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + bench.run(std::string(Traits::name()) + " creation", [&] { + auto ptr = Traits::make(TestObject{123}); + ankerl::nanobench::doNotOptimizeAway(ptr); + }); +} + +template +void benchmark_copy(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto original = Traits::make(TestObject{123}); + bench.run(std::string(Traits::name()) + " copy", [&] { + auto copy = original; + ankerl::nanobench::doNotOptimizeAway(copy); + }); +} + +template +void benchmark_move(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto original = Traits::make(TestObject{123}); + bench.run(std::string(Traits::name()) + " move", [&] { + auto moved = std::move(original); + ankerl::nanobench::doNotOptimizeAway(moved); + original = std::move(moved); + ankerl::nanobench::doNotOptimizeAway(original); + }); +} + +template +void benchmark_weak_copy(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto strong_ptr = Traits::make(TestObject{123}); + typename Traits::weak_type weak_original = strong_ptr; + bench.run(std::string(Traits::weak_name()) + " copy", [&] { + auto weak_copy = weak_original; + ankerl::nanobench::doNotOptimizeAway(weak_copy); + }); +} + +template +void benchmark_weak_move(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto strong_ptr = Traits::make(TestObject{123}); + typename Traits::weak_type weak_original = strong_ptr; + bench.run(std::string(Traits::weak_name()) + " move", [&] { + auto weak_moved = std::move(weak_original); + ankerl::nanobench::doNotOptimizeAway(weak_moved); + weak_original = std::move(weak_moved); + ankerl::nanobench::doNotOptimizeAway(weak_original); + }); +} + +template +void benchmark_dereference(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto ptr = Traits::make(TestObject{456}); + bench.run(std::string(Traits::name()) + " dereference", + [&] { ankerl::nanobench::doNotOptimizeAway(ptr->data); }); +} + +template +void benchmark_weak_lock_success(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + auto strong_ptr = Traits::make(TestObject{789}); + typename Traits::weak_type weak_ptr = strong_ptr; + bench.run(std::string(Traits::weak_name()) + " lock success", [&] { + auto locked = weak_ptr.lock(); + ankerl::nanobench::doNotOptimizeAway(locked); + }); +} + +template +void benchmark_weak_lock_failure(ankerl::nanobench::Bench &bench) { + using Traits = PointerTraits; + + typename Traits::weak_type weak_ptr; + { + auto strong_ptr = Traits::make(TestObject{999}); + weak_ptr = strong_ptr; + } + bench.run(std::string(Traits::weak_name()) + " lock failure", [&] { + auto locked = weak_ptr.lock(); + ankerl::nanobench::doNotOptimizeAway(locked); + }); +} + +template +void benchmark_multithreaded_copy(ankerl::nanobench::Bench &bench, + int num_threads) { + using Traits = PointerTraits; + + // Create the shared object outside the benchmark + auto ptr = Traits::make(TestObject{456}); + + // Create background threads that will create contention + std::atomic keep_running{true}; + std::vector background_threads; + + for (int i = 0; i < num_threads - 1; ++i) { + background_threads.emplace_back([&]() { + while (keep_running.load(std::memory_order_relaxed)) { + auto copy = ptr; + ankerl::nanobench::doNotOptimizeAway(copy); + } + }); + } + + // Benchmark the foreground thread under contention + bench.run(std::string(Traits::name()) + " copy under contention", [&] { + auto copy = ptr; + ankerl::nanobench::doNotOptimizeAway(copy); + }); + + // Clean up background threads + keep_running.store(false, std::memory_order_relaxed); + for (auto &t : background_threads) { + t.join(); + } +} + +template +void benchmark_multithreaded_weak_lock(ankerl::nanobench::Bench &bench, + int num_threads) { + using Traits = PointerTraits; + + // Create the shared object and weak reference outside the benchmark + auto strong_ptr = Traits::make(TestObject{789}); + typename Traits::weak_type weak_ptr = strong_ptr; + + // Create background threads that will create contention + std::atomic keep_running{true}; + std::vector background_threads; + + for (int i = 0; i < num_threads - 1; ++i) { + background_threads.emplace_back([&]() { + while (keep_running.load(std::memory_order_relaxed)) { + auto locked = weak_ptr.lock(); + ankerl::nanobench::doNotOptimizeAway(locked); + } + }); + } + + // Benchmark the foreground thread under contention + bench.run(std::string(Traits::weak_name()) + " lock under contention", [&] { + auto locked = weak_ptr.lock(); + ankerl::nanobench::doNotOptimizeAway(locked); + }); + + // Clean up background threads + keep_running.store(false, std::memory_order_relaxed); + for (auto &t : background_threads) { + t.join(); + } +} + +template +void benchmark_weak_copy_with_strong_contention(ankerl::nanobench::Bench &bench, + int num_threads) { + using Traits = PointerTraits; + + // Create the shared object and weak reference outside the benchmark + auto strong_ptr = Traits::make(TestObject{456}); + typename Traits::weak_type weak_ptr = strong_ptr; + + // Create background threads copying the strong pointer + std::atomic keep_running{true}; + std::vector background_threads; + + for (int i = 0; i < num_threads - 1; ++i) { + background_threads.emplace_back([&]() { + while (keep_running.load(std::memory_order_relaxed)) { + auto copy = strong_ptr; + ankerl::nanobench::doNotOptimizeAway(copy); + } + }); + } + + // Benchmark weak reference copying under strong reference contention + bench.run(std::string(Traits::weak_name()) + " copy with strong contention", + [&] { + auto weak_copy = weak_ptr; + ankerl::nanobench::doNotOptimizeAway(weak_copy); + }); + + // Clean up background threads + keep_running.store(false, std::memory_order_relaxed); + for (auto &t : background_threads) { + t.join(); + } +} + +template +void benchmark_strong_copy_with_weak_contention(ankerl::nanobench::Bench &bench, + int num_threads) { + using Traits = PointerTraits; + + // Create the shared object and weak reference outside the benchmark + auto strong_ptr = Traits::make(TestObject{789}); + typename Traits::weak_type weak_ptr = strong_ptr; + + // Create background threads copying the weak pointer + std::atomic keep_running{true}; + std::vector background_threads; + + for (int i = 0; i < num_threads - 1; ++i) { + background_threads.emplace_back([&]() { + while (keep_running.load(std::memory_order_relaxed)) { + auto weak_copy = weak_ptr; + ankerl::nanobench::doNotOptimizeAway(weak_copy); + } + }); + } + + // Benchmark strong reference copying under weak reference contention + bench.run(std::string(Traits::name()) + " copy with weak contention", [&] { + auto strong_copy = strong_ptr; + ankerl::nanobench::doNotOptimizeAway(strong_copy); + }); + + // Clean up background threads + keep_running.store(false, std::memory_order_relaxed); + for (auto &t : background_threads) { + t.join(); + } +} + +} // anonymous namespace + +void compare_creation() { + ankerl::nanobench::Bench bench; + bench.title("Creation performance comparison"); + bench.relative(true); + benchmark_creation>(bench); + benchmark_creation>(bench); +} + +void compare_copy() { + ankerl::nanobench::Bench bench; + bench.title("Copy performance comparison"); + bench.relative(true); + benchmark_copy>(bench); + benchmark_copy>(bench); +} + +void compare_move() { + ankerl::nanobench::Bench bench; + bench.title("Move performance comparison"); + bench.relative(true); + benchmark_move>(bench); + benchmark_move>(bench); +} + +void compare_weak_copy() { + ankerl::nanobench::Bench bench; + bench.title("Weak copy performance comparison"); + bench.relative(true); + benchmark_weak_copy>(bench); + benchmark_weak_copy>(bench); +} + +void compare_weak_move() { + ankerl::nanobench::Bench bench; + bench.title("Weak move performance comparison"); + bench.relative(true); + benchmark_weak_move>(bench); + benchmark_weak_move>(bench); +} + +void compare_dereference() { + ankerl::nanobench::Bench bench; + bench.title("Dereference performance comparison"); + bench.relative(true); + benchmark_dereference>(bench); + benchmark_dereference>(bench); +} + +void compare_weak_lock_success() { + ankerl::nanobench::Bench bench; + bench.title("Weak lock success performance comparison"); + bench.relative(true); + benchmark_weak_lock_success>(bench); + benchmark_weak_lock_success>(bench); +} + +void compare_weak_lock_failure() { + ankerl::nanobench::Bench bench; + bench.title("Weak lock failure performance comparison"); + bench.relative(true); + benchmark_weak_lock_failure>(bench); + benchmark_weak_lock_failure>(bench); +} + +void compare_multithreaded_copy(int num_threads) { + ankerl::nanobench::Bench bench; + bench.title("Copy performance under contention"); + bench.relative(true); + bench.minEpochIterations(500000); + benchmark_multithreaded_copy>(bench, num_threads); + benchmark_multithreaded_copy>(bench, num_threads); +} + +void compare_multithreaded_weak_lock(int num_threads) { + ankerl::nanobench::Bench bench; + bench.title("Weak lock performance under contention"); + bench.relative(true); + bench.minEpochIterations(500000); + benchmark_multithreaded_weak_lock>(bench, + num_threads); + benchmark_multithreaded_weak_lock>(bench, num_threads); +} + +void compare_weak_copy_with_strong_contention(int num_threads) { + ankerl::nanobench::Bench bench; + bench.title("Weak copy performance under strong reference contention"); + bench.relative(true); + bench.minEpochIterations(500000); + benchmark_weak_copy_with_strong_contention>( + bench, num_threads); + benchmark_weak_copy_with_strong_contention>(bench, + num_threads); +} + +void compare_strong_copy_with_weak_contention(int num_threads) { + ankerl::nanobench::Bench bench; + bench.title("Strong copy performance under weak reference contention"); + bench.relative(true); + bench.minEpochIterations(500000); + benchmark_strong_copy_with_weak_contention>( + bench, num_threads); + benchmark_strong_copy_with_weak_contention>(bench, + num_threads); +} + +int main() { + const int num_threads = 3; + + // Each comparison has std::shared_ptr as 100% baseline + compare_creation(); + compare_copy(); + compare_move(); + compare_weak_copy(); + compare_weak_move(); + compare_dereference(); + compare_weak_lock_success(); + compare_weak_lock_failure(); + compare_multithreaded_copy(num_threads); + compare_multithreaded_weak_lock(num_threads); + compare_weak_copy_with_strong_contention(num_threads); + compare_strong_copy_with_weak_contention(num_threads); + + return 0; +} diff --git a/tools/check_snake_case.py b/tools/check_snake_case.py index 9120ecc..996076e 100755 --- a/tools/check_snake_case.py +++ b/tools/check_snake_case.py @@ -66,6 +66,8 @@ def check_snake_case_violations(filepath, check_new_only=True): exclusions = [ # C++ standard library and common libraries r"\b(std::|weaseljson|simdutf|doctest)", + # Nanobench library API (external camelCase API) + r"\b(nanobench::|doNotOptimizeAway|minEpochIterations)\b", # Template parameters and concepts r"\b[A-Z][a-zA-Z0-9_]*\b", # Class/struct names (PascalCase is correct)