#![allow(unused_mut, clippy::drop_ref)]
use crossbeam_queue::ArrayQueue;
use hdrhistogram::Histogram;
use nexus_queue::mpsc;
use std::sync::Arc;
use std::thread;
const WARMUP: u64 = 10_000;
const SAMPLES: u64 = 100_000;
#[cfg(target_arch = "x86_64")]
#[inline]
fn rdtscp() -> u64 {
let mut aux: u32 = 0;
unsafe { core::arch::x86_64::__rdtscp(&raw mut aux) }
}
#[cfg(not(target_arch = "x86_64"))]
#[inline]
fn rdtscp() -> u64 {
0
}
fn bench_nexus_mpsc() -> Histogram<u64> {
let (mut tx_fwd, mut rx_fwd) = mpsc::ring_buffer::<u64>(1024);
let (mut tx_back, mut rx_back) = mpsc::ring_buffer::<()>(1024);
let consumer = thread::spawn(move || {
for _ in 0..(WARMUP + SAMPLES) {
loop {
if rx_fwd.pop().is_some() {
break;
}
std::hint::spin_loop();
}
while tx_back.push(()).is_err() {
std::hint::spin_loop();
}
}
});
let mut hist = Histogram::<u64>::new(3).unwrap();
for _ in 0..WARMUP {
while tx_fwd.push(0).is_err() {
std::hint::spin_loop();
}
loop {
if rx_back.pop().is_some() {
break;
}
std::hint::spin_loop();
}
}
for _ in 0..SAMPLES {
let start = rdtscp();
while tx_fwd.push(start).is_err() {
std::hint::spin_loop();
}
loop {
if rx_back.pop().is_some() {
break;
}
std::hint::spin_loop();
}
let end = rdtscp();
let _ = hist.record((end - start) / 2);
}
consumer.join().unwrap();
hist
}
fn bench_crossbeam() -> Histogram<u64> {
let fwd = Arc::new(ArrayQueue::<u64>::new(1024));
let back = Arc::new(ArrayQueue::<()>::new(1024));
let fwd_rx = Arc::clone(&fwd);
let back_tx = Arc::clone(&back);
let consumer = thread::spawn(move || {
for _ in 0..(WARMUP + SAMPLES) {
loop {
if fwd_rx.pop().is_some() {
break;
}
std::hint::spin_loop();
}
while back_tx.push(()).is_err() {
std::hint::spin_loop();
}
}
});
let mut hist = Histogram::<u64>::new(3).unwrap();
for _ in 0..WARMUP {
while fwd.push(0).is_err() {
std::hint::spin_loop();
}
loop {
if back.pop().is_some() {
break;
}
std::hint::spin_loop();
}
}
for _ in 0..SAMPLES {
let start = rdtscp();
while fwd.push(start).is_err() {
std::hint::spin_loop();
}
loop {
if back.pop().is_some() {
break;
}
std::hint::spin_loop();
}
let end = rdtscp();
let _ = hist.record((end - start) / 2);
}
consumer.join().unwrap();
hist
}
fn print_hist(name: &str, hist: &Histogram<u64>, freq_ghz: f64) {
let p50 = hist.value_at_quantile(0.5);
let p99 = hist.value_at_quantile(0.99);
let p999 = hist.value_at_quantile(0.999);
println!("{}:", name);
println!(
" p50: {:4} cy ({:5.1} ns) p99: {:4} cy ({:5.1} ns) p999: {:5} cy ({:6.1} ns)",
p50,
p50 as f64 / freq_ghz,
p99,
p99 as f64 / freq_ghz,
p999,
p999 as f64 / freq_ghz
);
}
fn main() {
println!("MPSC Ping-Pong Latency Benchmark");
println!("=================================\n");
let start_time = std::time::Instant::now();
let start_tsc = rdtscp();
std::thread::sleep(std::time::Duration::from_millis(100));
let end_tsc = rdtscp();
let elapsed = start_time.elapsed();
let freq_ghz = (end_tsc - start_tsc) as f64 / elapsed.as_nanos() as f64;
println!("CPU freq: {:.2} GHz\n", freq_ghz);
println!("One-way latency (RTT/2):");
println!("------------------------");
let nexus_hist = bench_nexus_mpsc();
print_hist("nexus-queue MPSC", &nexus_hist, freq_ghz);
let crossbeam_hist = bench_crossbeam();
print_hist("crossbeam ArrayQueue", &crossbeam_hist, freq_ghz);
}