diff --git a/README.md b/README.md index 860ae6e..dc54603 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Ringbuffer + ![Github Workflows](https://img.shields.io/github/actions/workflow/status/NULLx76/ringbuffer/rust.yml?style=for-the-badge) [![Docs.rs](https://img.shields.io/badge/docs.rs-ringbuffer-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K)](https://docs.rs/ringbuffer) [![Crates.io](https://img.shields.io/crates/v/ringbuffer?logo=rust&style=for-the-badge)](https://crates.io/crates/ringbuffer) @@ -15,13 +16,13 @@ Implementations for three kinds of ringbuffers, with a mostly similar API are pr All of these ringbuffers also implement the RingBuffer trait for their shared API surface. - MSRV: Rust 1.59 # Usage ```rust use ringbuffer::{AllocRingBuffer, RingBuffer, RingBufferExt, RingBufferWrite}; + fn main() { let mut buffer = AllocRingBuffer::with_capacity(2); @@ -46,7 +47,7 @@ fn main() { # Comparison of ringbuffer types | type | heap allocated | growable | size must be power of 2 | `no_std` | -| ----------------------------------------------|----------------|----------|-------------------------|----------| +|-----------------------------------------------|----------------|----------|-------------------------|----------| | `AllocRingBuffer` | yes | no | yes | no | | `AllocRingBuffer` | yes | no | no | no | | `GrowableAllocRingBuffer` | yes | yes | no | no | @@ -54,6 +55,31 @@ fn main() { [^1]: Using a size that is not a power of 2 will be ~3x slower. +## To other ringbuffers + +We ran a benchmark, pushing `10 000` elements to a ringbuffer with a capacity of `1000` (where it was possible to +configure one) to compare +`ringbuffer` to a few common alternatives. +The outcomes show that using the [`ConstGenericRingBuffer`] is about 23 times faster than using an `std::channel` ( +although `ringbuffer` doesn't give the thread safety a channel does). +A maybe more reasonable comparison is to an `std::VecDeque` and `heapless::Deque`, +where ringbuffer is slightly faster as well (among 100 measurements). + +| implementation | time (95% confidence interval, \[lower estimate upper\]) | +|---------------------------------------|----------------------------------------------------------| +| `std::Vec` | `[13.190 ms 13.206 ms 13.223 ms]` | +| `std::LinkedList` | `[225.64 µs 228.09 µs 231.06 µs]` | +| `std::channel` | `[174.86 µs 175.41 µs 176.30 µs]` | +| `std::VecDeque` (growable ringbuffer) | `[33.894 µs 33.934 µs 33.974 µs]` | +| `AllocRingBuffer` | `[30.382 µs 30.451 µs 30.551 µs]` | +| `heapless::Deque` | `[16.260 µs 16.464 µs 16.748 µs]` | +| `ConstGenericRingBuffer` | `[13.685 µs 13.712 µs 13.743 µs]` | + +Note that none of the alternatives to `RingBuffer` have the exact same behavior to `RingBuffer`. All `std` datastructures +compared here can grow unbounded (though in benchmarks they weren't filled over `10 000` elements). + +`heapless::Deque` doesn't drop old items like `ringbuffer` does when the deque is full. Instead, new items aren't let in on push operations. + # Features | name | default | description | diff --git a/benches/bench.rs b/benches/bench.rs index d199a39..3cdf736 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -93,7 +93,7 @@ macro_rules! generate_benches { fn benchmark_non_power_of_two(b: &mut Bencher) { b.iter(|| { - let mut rb = AllocRingBuffer::with_capacity_non_power_of_two(L); + let mut rb = AllocRingBuffer::with_capacity_power_of_2(L); for i in 0..1_000_000 { rb.push(i); diff --git a/benches/comparison.rs b/benches/comparison.rs index dfa67b4..3a2018c 100644 --- a/benches/comparison.rs +++ b/benches/comparison.rs @@ -15,6 +15,9 @@ fn std_chan(b: &mut Bencher) { for i in 0..ITER { let _ = tx.send(i); black_box(()); + } + + for i in 0..ITER { let res = rx.recv(); let _ = black_box(res); } @@ -28,6 +31,9 @@ fn vec(b: &mut Bencher) { for i in 0..ITER { let _ = vd.push(i); black_box(()); + } + + for i in 0..ITER { let res = vd.remove(0); let _ = black_box(res); } @@ -41,6 +47,8 @@ fn vecdeque(b: &mut Bencher) { for i in 0..ITER { let _ = vd.push_back(i); black_box(()); + } + for i in 0..ITER { let res = vd.pop_front(); let _ = black_box(res); } @@ -54,6 +62,9 @@ fn linked_list(b: &mut Bencher) { for i in 0..ITER { let _ = ll.push_back(i); black_box(()); + } + + for i in 0..ITER { let res = ll.pop_front(); let _ = black_box(res); } @@ -67,6 +78,8 @@ fn cg_rb(b: &mut Bencher) { for i in 0..ITER { let _ = rb.push(i); black_box(()); + } + for i in 0..ITER { let res = rb.dequeue(); let _ = black_box(res); } @@ -80,6 +93,8 @@ fn heapless_deque(b: &mut Bencher) { for i in 0..ITER { let _ = rb.push_back(i); black_box(()); + } + for i in 0..ITER { let res = rb.pop_front(); let _ = black_box(res); } @@ -90,9 +105,11 @@ fn al_rb(b: &mut Bencher) { let mut rb = AllocRingBuffer::with_capacity_non_power_of_two(CAP); b.iter(|| { - for i in 0..10_000 { + for i in 0..ITER { let _ = rb.push(i); black_box(()); + } + for i in 0..ITER { let res = rb.dequeue(); let _ = black_box(res); } @@ -102,6 +119,7 @@ fn al_rb(b: &mut Bencher) { fn criterion_benchmark(c: &mut Criterion) { c.bench_function("comparison std channel", std_chan); c.bench_function("comparison std vec", vec); + c.bench_function("comparison std linked list", linked_list); c.bench_function("comparison std vecdeque (growable ringbuffer)", vecdeque); c.bench_function("comparison const generic ringbuffer", cg_rb); c.bench_function("comparison alloc ringbuffer", al_rb);