Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 56 additions & 5 deletions benchmarks/ring_buffer_bench.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,64 @@
#include <benchmark/benchmark.h>
#include <spsc/ring_buffer.hpp>
#include <thread>
#include <atomic>

static void BM_BufferCreation(benchmark::State& state){
for(auto _ : state){
shovy::RingBuffer<int> buffer(1024);
benchmark::DoNotOptimize(buffer);
template<typename Queue>
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<bool> 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<size_t>)
->Arg(64)
->Arg(1024)
->Arg(4096)
->UseRealTime()
->Repetitions(5)
->ReportAggregatesOnly(true);

BENCHMARK_MAIN();
17 changes: 9 additions & 8 deletions include/spsc/ring_buffer.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <stdexcept>
#include <atomic>

namespace shovy{

Expand All @@ -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<size_t> write_idx{0};
std::atomic<size_t> read_idx{0};
};

} // namespace shovy
Loading