From 99eb0c7f9663b5cb043273f4421ace587acc648d Mon Sep 17 00:00:00 2001 From: HawkinWay <1826784860@qq.com> Date: Mon, 20 Jul 2026 16:47:10 +0800 Subject: [PATCH 1/3] feat: introduce atomic indices with seq_cst ordering (#2) --- include/spsc/ring_buffer.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) 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 From ebeb33deafbe2404dab61ac0b415b1772e180d96 Mon Sep 17 00:00:00 2001 From: HawkinWay <1826784860@qq.com> Date: Fri, 24 Jul 2026 03:36:55 +0800 Subject: [PATCH 2/3] perf: improve SPSC benchmark methodology --- benchmarks/ring_buffer_bench.cpp | 75 +++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/benchmarks/ring_buffer_bench.cpp b/benchmarks/ring_buffer_bench.cpp index 442268c..e8e167f 100644 --- a/benchmarks/ring_buffer_bench.cpp +++ b/benchmarks/ring_buffer_bench.cpp @@ -1,13 +1,78 @@ #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); + // 每次迭代处理的 Ops 数量,10,000,000 次非常适合做长跑吞吐量测试 + constexpr size_t total_operations = 10'000'000; + + for (auto _ : state) { + // 1. 停止计时:准备测试资源 + state.PauseTiming(); + + Queue buffer{capacity}; + std::atomic start{false}; + + // 2. 创建子线程 + std::thread producer([&]() { + // 线程自旋等待起跑信号 + while (!start.load(std::memory_order_acquire)) { +#if defined(__x86_64__) || defined(_M_X64) + _mm_pause(); // x86 友好自旋 +#endif + } + + 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)) { +#if defined(__x86_64__) || defined(_M_X64) + _mm_pause(); +#endif + } + + size_t val = 0; + for (size_t i = 0; i < total_operations; ++i) { + while (!buffer.pop(val)) { + std::this_thread::yield(); + } + // 阻止编译器将 pop 出来的 val 优化掉 + benchmark::DoNotOptimize(val); + } + }); + + // 3. 恢复计时:正式开始发车! + state.ResumeTiming(); + + // 释放屏障,触发双线程同时狂飚 + start.store(true, std::memory_order_release); + + producer.join(); + consumer.join(); + + // 4. 再次停止计时:线程回收不计入队列性能 + state.PauseTiming(); } + + // 正确设置处理的 Item 总数 (Push Count + Pop Count = 2 * total_operations) + state.SetItemsProcessed(state.iterations() * total_operations * 2); } -BENCHMARK(BM_BufferCreation); +// 绑定不同的缓冲区容量测试 (64, 1024, 4096) +BENCHMARK_TEMPLATE(BM_SPSC_Throughput, shovy::RingBuffer) + ->Arg(64) + ->Arg(1024) + ->Arg(4096) + ->UseRealTime() + ->Repetitions(5) + ->ReportAggregatesOnly(true); // 只输出 Mean/Median/StdDev,界面更清爽 BENCHMARK_MAIN(); \ No newline at end of file From 8f39a6fc6b6447ca95263c3eb0b6a0dd81e58576 Mon Sep 17 00:00:00 2001 From: HawkinWay <1826784860@qq.com> Date: Fri, 24 Jul 2026 20:11:26 +0800 Subject: [PATCH 3/3] bench: remove #if define --- benchmarks/ring_buffer_bench.cpp | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/benchmarks/ring_buffer_bench.cpp b/benchmarks/ring_buffer_bench.cpp index e8e167f..8283fbe 100644 --- a/benchmarks/ring_buffer_bench.cpp +++ b/benchmarks/ring_buffer_bench.cpp @@ -6,23 +6,17 @@ template static void BM_SPSC_Throughput(benchmark::State& state) { const size_t capacity = state.range(0); - // 每次迭代处理的 Ops 数量,10,000,000 次非常适合做长跑吞吐量测试 constexpr size_t total_operations = 10'000'000; for (auto _ : state) { - // 1. 停止计时:准备测试资源 state.PauseTiming(); Queue buffer{capacity}; std::atomic start{false}; - // 2. 创建子线程 std::thread producer([&]() { - // 线程自旋等待起跑信号 while (!start.load(std::memory_order_acquire)) { -#if defined(__x86_64__) || defined(_M_X64) - _mm_pause(); // x86 友好自旋 -#endif + } for (size_t i = 0; i < total_operations; ++i) { @@ -34,9 +28,7 @@ static void BM_SPSC_Throughput(benchmark::State& state) { std::thread consumer([&]() { while (!start.load(std::memory_order_acquire)) { -#if defined(__x86_64__) || defined(_M_X64) - _mm_pause(); -#endif + } size_t val = 0; @@ -44,35 +36,29 @@ static void BM_SPSC_Throughput(benchmark::State& state) { while (!buffer.pop(val)) { std::this_thread::yield(); } - // 阻止编译器将 pop 出来的 val 优化掉 benchmark::DoNotOptimize(val); } }); - // 3. 恢复计时:正式开始发车! state.ResumeTiming(); - // 释放屏障,触发双线程同时狂飚 start.store(true, std::memory_order_release); producer.join(); consumer.join(); - // 4. 再次停止计时:线程回收不计入队列性能 state.PauseTiming(); } - // 正确设置处理的 Item 总数 (Push Count + Pop Count = 2 * total_operations) state.SetItemsProcessed(state.iterations() * total_operations * 2); } -// 绑定不同的缓冲区容量测试 (64, 1024, 4096) BENCHMARK_TEMPLATE(BM_SPSC_Throughput, shovy::RingBuffer) ->Arg(64) ->Arg(1024) ->Arg(4096) ->UseRealTime() ->Repetitions(5) - ->ReportAggregatesOnly(true); // 只输出 Mean/Median/StdDev,界面更清爽 + ->ReportAggregatesOnly(true); BENCHMARK_MAIN(); \ No newline at end of file