use std::hint::spin_loop;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Barrier};
use std::thread;
use hdrhistogram::Histogram;
use super::{SpscBench, SpscCons, SpscProd};
use crate::bench::harness::TwoCoreCtx;
use crate::bench::harness::report::LatencyReport;
use crate::bench::loc;
use crate::bench::tsc::rdtscp;
#[derive(Clone, Copy, Debug)]
pub struct SpscHandoffCfg {
pub n: u64,
pub warmup: u64,
}
impl Default for SpscHandoffCfg {
fn default() -> Self {
Self {
n: 10_000_000,
warmup: 1_000_000,
}
}
}
pub fn run_spsc_handoff<Q, const CAPACITY: usize>(
ctx: &TwoCoreCtx,
cfg: SpscHandoffCfg,
) -> LatencyReport
where
Q: SpscBench<u64, CAPACITY>,
{
let (mut producer, mut consumer) = Q::new();
let cores = ctx.used_cpu_ids();
let barrier = Arc::new(Barrier::new(3));
let done = Arc::new(AtomicBool::new(false));
let loc_before = loc::read(&cores);
let producer_core = ctx.producer_core;
let consumer_core = ctx.consumer_core;
let cfg_n = cfg.n;
let cfg_warmup = cfg.warmup;
let cthread = {
let barrier = barrier.clone();
let done = done.clone();
thread::spawn(move || -> Histogram<u64> {
assert!(
core_affinity::set_for_current(consumer_core),
"failed to set core affinity for consumer: desired core: {consumer_core:?}"
);
let actual = unsafe { libc::sched_getcpu() };
assert_eq!(
actual, consumer_core.id as i32,
"consumer not pinned where requested"
);
let mut hist = Histogram::<u64>::new(3).unwrap();
let mut seen: u64 = 0;
barrier.wait();
loop {
while let Some(ts) = consumer.try_pop() {
let now = rdtscp();
if seen >= cfg_warmup {
let _ = hist.record(now.wrapping_sub(ts));
}
seen += 1;
}
if done.load(Ordering::Acquire) {
while let Some(ts) = consumer.try_pop() {
let now = rdtscp();
if seen >= cfg_warmup {
let _ = hist.record(now.wrapping_sub(ts));
}
seen += 1;
}
break;
}
spin_loop();
}
hist
})
};
let pthread = {
let barrier = barrier.clone();
let done = done.clone();
thread::spawn(move || {
assert!(
core_affinity::set_for_current(producer_core),
"failed to set core affinity for producer: desired core: {producer_core:?}"
);
let actual = unsafe { libc::sched_getcpu() };
assert_eq!(
actual, producer_core.id as i32,
"producer not pinned where requested"
);
barrier.wait();
for _ in 0..cfg_n {
loop {
let ts = rdtscp();
if producer.try_push(ts).is_ok() {
break;
}
spin_loop();
}
}
done.store(true, Ordering::Release);
})
};
barrier.wait();
pthread.join().unwrap();
let hist = cthread.join().unwrap();
let loc_after = loc::read(&cores);
let loc_delta = cores
.iter()
.enumerate()
.map(|(i, &cpu)| {
let d = match (loc_before[i], loc_after[i]) {
(Some(b), Some(a)) => Some(a.saturating_sub(b)),
_ => None,
};
(cpu, d)
})
.collect();
LatencyReport {
impl_name: Q::NAME,
warnings: Q::WARNINGS,
p50: hist.value_at_quantile(0.50),
p90: hist.value_at_quantile(0.90),
p99: hist.value_at_quantile(0.99),
p999: hist.value_at_quantile(0.999),
max: hist.max(),
loc_delta,
}
}