diff --git a/benchmarks/ring_buffer_bench.cpp b/benchmarks/ring_buffer_bench.cpp index 442268c..8283fbe 100644 --- a/benchmarks/ring_buffer_bench.cpp +++ b/benchmarks/ring_buffer_bench.cpp @@ -1,13 +1,64 @@ #include #include +#include +#include -static void BM_BufferCreation(benchmark::State& state){ - for(auto _ : state){ - shovy::RingBuffer buffer(1024); - benchmark::DoNotOptimize(buffer); +template +static void BM_SPSC_Throughput(benchmark::State& state) { + const size_t capacity = state.range(0); + constexpr size_t total_operations = 10'000'000; + + for (auto _ : state) { + state.PauseTiming(); + + Queue buffer{capacity}; + std::atomic start{false}; + + std::thread producer([&]() { + while (!start.load(std::memory_order_acquire)) { + + } + + for (size_t i = 0; i < total_operations; ++i) { + while (!buffer.push(i)) { + std::this_thread::yield(); + } + } + }); + + std::thread consumer([&]() { + while (!start.load(std::memory_order_acquire)) { + + } + + size_t val = 0; + for (size_t i = 0; i < total_operations; ++i) { + while (!buffer.pop(val)) { + std::this_thread::yield(); + } + benchmark::DoNotOptimize(val); + } + }); + + state.ResumeTiming(); + + start.store(true, std::memory_order_release); + + producer.join(); + consumer.join(); + + state.PauseTiming(); } + + state.SetItemsProcessed(state.iterations() * total_operations * 2); } -BENCHMARK(BM_BufferCreation); +BENCHMARK_TEMPLATE(BM_SPSC_Throughput, shovy::RingBuffer) + ->Arg(64) + ->Arg(1024) + ->Arg(4096) + ->UseRealTime() + ->Repetitions(5) + ->ReportAggregatesOnly(true); BENCHMARK_MAIN(); \ No newline at end of file diff --git a/include/spsc/ring_buffer.hpp b/include/spsc/ring_buffer.hpp index 4d41d7f..77bc924 100644 --- a/include/spsc/ring_buffer.hpp +++ b/include/spsc/ring_buffer.hpp @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace shovy{ @@ -21,29 +22,29 @@ class RingBuffer{ // producer bool push(const T& item) { if (full()) return false; - buffer_[write_idx % capacity_] = item; - write_idx++; + buffer_[write_idx.load() % capacity_] = item; + write_idx.fetch_add(1); // write_idx++; is okay return true; } // consumer bool pop(T& item) { if (empty()) return false; - item = buffer_[read_idx % capacity_]; - read_idx++; + item = buffer_[read_idx.load() % capacity_]; + read_idx.fetch_add(1); return true; } - bool empty() const { return write_idx == read_idx; } + bool empty() const { return write_idx.load() == read_idx.load(); } bool full() const { return size() == capacity_; } - size_t size() const { return write_idx - read_idx; } + size_t size() const { return write_idx.load() - read_idx.load(); } size_t capacity() const { return capacity_; } private: T* buffer_; size_t capacity_; - size_t write_idx{0}; - size_t read_idx{0}; + std::atomic write_idx{0}; + std::atomic read_idx{0}; }; } // namespace shovy \ No newline at end of file