spsc-ring 0.1.0

Lock-free SPSC ring buffer — sequence-number protocol, cache-line padded, zero dependencies
Documentation
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use spsc_ring::ring;
use std::hint::black_box;
use std::thread;

fn throughput_1m(c: &mut Criterion) {
    let count = 1_000_000usize;

    c.bench_function("spsc_1M_events", |b| {
        b.iter(|| {
            let (tx, rx) = ring(1024).unwrap();

            let producer = thread::spawn(move || {
                for i in 0..count {
                    while tx.try_push(black_box(i)).is_err() {
                        std::hint::spin_loop();
                    }
                }
            });

            let consumer = thread::spawn(move || {
                let mut n = 0usize;
                while n < count {
                    match rx.try_pop() {
                        Ok(v) => {
                            black_box(v);
                            n += 1;
                        }
                        Err(_) => std::hint::spin_loop(),
                    }
                }
                n
            });

            producer.join().unwrap();
            let received = consumer.join().unwrap();
            assert_eq!(received, count);
        });
    });
}

fn bench_push_slice_1m(c: &mut Criterion) {
    const COUNT: usize = 1_000_000;
    const CHUNK: usize = 64;
    const CAP: usize = 4096;

    c.bench_function("spsc_push_slice_1M_chunk64", |b| {
        b.iter(|| {
            let (tx, rx) = ring::<u32>(CAP).unwrap();
            let src = [black_box(42u32); CHUNK];

            let producer = thread::spawn(move || {
                let mut sent = 0usize;
                while sent < COUNT {
                    let remaining = COUNT - sent;
                    let chunk = &src[..remaining.min(CHUNK)];
                    let mut offset = 0usize;
                    while offset < chunk.len() {
                        let n = tx.push_slice(&chunk[offset..]);
                        offset += n;
                        if n == 0 {
                            std::hint::spin_loop();
                        }
                    }
                    sent += chunk.len();
                }
            });

            let consumer = thread::spawn(move || {
                let mut dst = [0u32; CHUNK];
                let mut received = 0usize;
                while received < COUNT {
                    let n = rx.pop_into_slice(&mut dst);
                    black_box(&dst);
                    received += n;
                    if n == 0 {
                        std::hint::spin_loop();
                    }
                }
                received
            });

            producer.join().unwrap();
            let received = consumer.join().unwrap();
            assert_eq!(received, COUNT);
        });
    });
}

fn bench_push_slice_chunk_sizes(c: &mut Criterion) {
    const COUNT: usize = 1_000_000;
    const CAP: usize = 4096;

    let mut group = c.benchmark_group("spsc_push_slice_chunk_size");

    for &chunk_size in &[1usize, 8, 32, 64, 256] {
        group.bench_with_input(
            BenchmarkId::from_parameter(chunk_size),
            &chunk_size,
            |b, &chunk_size| {
                b.iter(|| {
                    let (tx, rx) = ring::<u32>(CAP).unwrap();
                    let src = vec![black_box(42u32); chunk_size];

                    let producer = thread::spawn(move || {
                        let mut sent = 0usize;
                        while sent < COUNT {
                            let remaining = COUNT - sent;
                            let chunk = &src[..remaining.min(chunk_size)];
                            let mut offset = 0usize;
                            while offset < chunk.len() {
                                let n = tx.push_slice(&chunk[offset..]);
                                offset += n;
                                if n == 0 {
                                    std::hint::spin_loop();
                                }
                            }
                            sent += chunk.len();
                        }
                    });

                    let consumer = thread::spawn(move || {
                        let mut dst = vec![0u32; chunk_size];
                        let mut received = 0usize;
                        while received < COUNT {
                            let n = rx.pop_into_slice(&mut dst);
                            black_box(&dst);
                            received += n;
                            if n == 0 {
                                std::hint::spin_loop();
                            }
                        }
                        received
                    });

                    producer.join().unwrap();
                    let received = consumer.join().unwrap();
                    assert_eq!(received, COUNT);
                });
            },
        );
    }

    group.finish();
}

criterion_group!(
    benches,
    throughput_1m,
    bench_push_slice_1m,
    bench_push_slice_chunk_sizes
);
criterion_main!(benches);