Add bench_reference.cpp
Also update snake case script for nanobench symbols
This commit is contained in:
406
benchmarks/bench_reference.cpp
Normal file
406
benchmarks/bench_reference.cpp
Normal file
@@ -0,0 +1,406 @@
|
|||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <nanobench.h>
|
||||||
|
|
||||||
|
#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 <typename T> struct PointerTraits;
|
||||||
|
|
||||||
|
template <typename T> struct PointerTraits<std::shared_ptr<T>> {
|
||||||
|
using pointer_type = std::shared_ptr<T>;
|
||||||
|
using weak_type = std::weak_ptr<T>;
|
||||||
|
|
||||||
|
template <typename... Args> static pointer_type make(Args &&...args) {
|
||||||
|
return std::make_shared<T>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *name() { return "std::shared_ptr"; }
|
||||||
|
static const char *weak_name() { return "std::weak_ptr"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct PointerTraits<Ref<T>> {
|
||||||
|
using pointer_type = Ref<T>;
|
||||||
|
using weak_type = WeakRef<T>;
|
||||||
|
|
||||||
|
template <typename... Args> static pointer_type make(Args &&...args) {
|
||||||
|
return make_ref<T>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *name() { return "Ref"; }
|
||||||
|
static const char *weak_name() { return "WeakRef"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PtrType>
|
||||||
|
void benchmark_creation(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
bench.run(std::string(Traits::name()) + " creation", [&] {
|
||||||
|
auto ptr = Traits::make(TestObject{123});
|
||||||
|
ankerl::nanobench::doNotOptimizeAway(ptr);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PtrType>
|
||||||
|
void benchmark_copy(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
auto original = Traits::make(TestObject{123});
|
||||||
|
bench.run(std::string(Traits::name()) + " copy", [&] {
|
||||||
|
auto copy = original;
|
||||||
|
ankerl::nanobench::doNotOptimizeAway(copy);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PtrType>
|
||||||
|
void benchmark_move(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
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 <typename PtrType>
|
||||||
|
void benchmark_weak_copy(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
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 <typename PtrType>
|
||||||
|
void benchmark_weak_move(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
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 <typename PtrType>
|
||||||
|
void benchmark_dereference(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
auto ptr = Traits::make(TestObject{456});
|
||||||
|
bench.run(std::string(Traits::name()) + " dereference",
|
||||||
|
[&] { ankerl::nanobench::doNotOptimizeAway(ptr->data); });
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PtrType>
|
||||||
|
void benchmark_weak_lock_success(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
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 <typename PtrType>
|
||||||
|
void benchmark_weak_lock_failure(ankerl::nanobench::Bench &bench) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
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 <typename PtrType>
|
||||||
|
void benchmark_multithreaded_copy(ankerl::nanobench::Bench &bench,
|
||||||
|
int num_threads) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
// Create the shared object outside the benchmark
|
||||||
|
auto ptr = Traits::make(TestObject{456});
|
||||||
|
|
||||||
|
// Create background threads that will create contention
|
||||||
|
std::atomic<bool> keep_running{true};
|
||||||
|
std::vector<std::thread> 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 <typename PtrType>
|
||||||
|
void benchmark_multithreaded_weak_lock(ankerl::nanobench::Bench &bench,
|
||||||
|
int num_threads) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
// 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<bool> keep_running{true};
|
||||||
|
std::vector<std::thread> 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 <typename PtrType>
|
||||||
|
void benchmark_weak_copy_with_strong_contention(ankerl::nanobench::Bench &bench,
|
||||||
|
int num_threads) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
// 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<bool> keep_running{true};
|
||||||
|
std::vector<std::thread> 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 <typename PtrType>
|
||||||
|
void benchmark_strong_copy_with_weak_contention(ankerl::nanobench::Bench &bench,
|
||||||
|
int num_threads) {
|
||||||
|
using Traits = PointerTraits<PtrType>;
|
||||||
|
|
||||||
|
// 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<bool> keep_running{true};
|
||||||
|
std::vector<std::thread> 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<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_creation<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_copy() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Copy performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_copy<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_copy<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_move() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Move performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_move<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_move<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_weak_copy() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Weak copy performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_weak_copy<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_weak_copy<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_weak_move() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Weak move performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_weak_move<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_weak_move<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_dereference() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Dereference performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_dereference<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_dereference<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_weak_lock_success() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Weak lock success performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_weak_lock_success<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_weak_lock_success<Ref<TestObject>>(bench);
|
||||||
|
}
|
||||||
|
|
||||||
|
void compare_weak_lock_failure() {
|
||||||
|
ankerl::nanobench::Bench bench;
|
||||||
|
bench.title("Weak lock failure performance comparison");
|
||||||
|
bench.relative(true);
|
||||||
|
benchmark_weak_lock_failure<std::shared_ptr<TestObject>>(bench);
|
||||||
|
benchmark_weak_lock_failure<Ref<TestObject>>(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<std::shared_ptr<TestObject>>(bench, num_threads);
|
||||||
|
benchmark_multithreaded_copy<Ref<TestObject>>(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<std::shared_ptr<TestObject>>(bench,
|
||||||
|
num_threads);
|
||||||
|
benchmark_multithreaded_weak_lock<Ref<TestObject>>(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<std::shared_ptr<TestObject>>(
|
||||||
|
bench, num_threads);
|
||||||
|
benchmark_weak_copy_with_strong_contention<Ref<TestObject>>(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<std::shared_ptr<TestObject>>(
|
||||||
|
bench, num_threads);
|
||||||
|
benchmark_strong_copy_with_weak_contention<Ref<TestObject>>(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;
|
||||||
|
}
|
||||||
@@ -66,6 +66,8 @@ def check_snake_case_violations(filepath, check_new_only=True):
|
|||||||
exclusions = [
|
exclusions = [
|
||||||
# C++ standard library and common libraries
|
# C++ standard library and common libraries
|
||||||
r"\b(std::|weaseljson|simdutf|doctest)",
|
r"\b(std::|weaseljson|simdutf|doctest)",
|
||||||
|
# Nanobench library API (external camelCase API)
|
||||||
|
r"\b(nanobench::|doNotOptimizeAway|minEpochIterations)\b",
|
||||||
# Template parameters and concepts
|
# Template parameters and concepts
|
||||||
r"\b[A-Z][a-zA-Z0-9_]*\b",
|
r"\b[A-Z][a-zA-Z0-9_]*\b",
|
||||||
# Class/struct names (PascalCase is correct)
|
# Class/struct names (PascalCase is correct)
|
||||||
|
|||||||
Reference in New Issue
Block a user