use std::iter;
use std::sync::{Arc, Condvar, Mutex};
use std::thread::spawn;
use std::time::Instant;
use criterion::{criterion_group, criterion_main, Criterion};
use num_cpus;
use st3;
mod generic_queue;
mod tokio_queue;
use generic_queue::{
CrossbeamFifoWorker, CrossbeamLifoWorker, GenericStealError, GenericStealer, GenericWorker,
};
pub fn push_pop<T, W: GenericWorker<usize>, const N: usize>(name: &str, c: &mut Criterion) {
let worker = W::new();
c.bench_function(&format!("push_pop-{}", name), |b| {
b.iter(|| {
for i in 0..N {
let _ = worker.push(i);
}
for _ in 0..N {
let _ = worker.pop();
}
})
});
}
pub fn push_pop_small_batch_st3_fifo(c: &mut Criterion) {
push_pop::<usize, st3::fifo::Worker<_>, 64>("small_batch-st3_fifo", c);
}
pub fn push_pop_small_batch_st3_lifo(c: &mut Criterion) {
push_pop::<usize, st3::lifo::Worker<_>, 64>("small_batch-st3_lifo", c);
}
pub fn push_pop_small_batch_tokio(c: &mut Criterion) {
push_pop::<usize, tokio_queue::Local<_>, 64>("small_batch-tokio", c);
}
pub fn push_pop_small_batch_crossbeam_fifo(c: &mut Criterion) {
push_pop::<usize, CrossbeamFifoWorker<_>, 64>("small_batch-crossbeam_fifo", c);
}
pub fn push_pop_small_batch_crossbeam_lifo(c: &mut Criterion) {
push_pop::<usize, CrossbeamLifoWorker<_>, 64>("small_batch-crossbeam_lifo", c);
}
pub fn push_pop_large_batch_st3_fifo(c: &mut Criterion) {
push_pop::<usize, st3::fifo::Worker<_>, 256>("large_batch-st3_fifo", c);
}
pub fn push_pop_large_batch_st3_lifo(c: &mut Criterion) {
push_pop::<usize, st3::lifo::Worker<_>, 256>("large_batch-st3_lifo", c);
}
pub fn push_pop_large_batch_tokio(c: &mut Criterion) {
push_pop::<usize, tokio_queue::Local<_>, 256>("large_batch-tokio", c);
}
pub fn push_pop_large_batch_crossbeam_fifo(c: &mut Criterion) {
push_pop::<usize, CrossbeamFifoWorker<_>, 256>("large_batch-crossbeam_fifo", c);
}
pub fn push_pop_large_batch_crossbeam_lifo(c: &mut Criterion) {
push_pop::<usize, CrossbeamLifoWorker<_>, 256>("large_batch-crossbeam_lifo", c);
}
pub fn executor<T, W: GenericWorker<u64> + 'static>(name: &str, c: &mut Criterion) {
c.bench_function(&format!("executor-{}", name), |b| {
const MAX_TASKS_PER_THREAD: u64 = 256;
const MAX_TASK_REPEAT: u64 = 100;
let thread_count = num_cpus::get().min(4);
b.iter(|| {
let workers: Vec<_> = iter::repeat_with(|| W::new()).take(thread_count).collect();
let stealers: Vec<_> = workers.iter().map(|w| w.stealer()).collect();
let wait_barrier = Arc::new((Mutex::new(0usize), Condvar::new()));
let mut threads = Vec::with_capacity(thread_count);
for (th_id, worker) in workers.into_iter().enumerate() {
let stealers = stealers.clone();
let wait_barrier = wait_barrier.clone();
threads.push(spawn(move || -> Option<Instant> {
let mut rng = oorandom::Rand64::new(th_id as u128);
let task_count = rng.rand_range(1..(MAX_TASKS_PER_THREAD + 1));
for _ in 0..task_count {
worker
.push(rng.rand_range(0..MAX_TASK_REPEAT))
.expect("attempting to schedule more tasks than the queue can hold");
}
let mut other_workers_id = Vec::with_capacity(thread_count - 1);
other_workers_id.resize(thread_count - 1, 0);
let start_time = {
let (lock, cvar) = &*wait_barrier;
let mut ready_count = lock.lock().unwrap();
*ready_count += 1;
if *ready_count == thread_count {
cvar.notify_all();
Some(Instant::now())
} else {
while *ready_count < thread_count {
ready_count = cvar.wait(ready_count).unwrap();
}
None
}
};
'new_task: loop {
if let Some(repeat_count) = worker.pop() {
if repeat_count > 0 {
while let Err(_) = worker.push(repeat_count - 1) {}
}
} else {
for (i, th) in other_workers_id.iter_mut().enumerate() {
if i < th_id {
*th = i;
} else {
*th = i + 1;
}
}
let mut pool_size = thread_count - 1;
loop {
let idx = rng.rand_range(0..pool_size as u64) as usize;
let steal_from = other_workers_id[idx];
match stealers[steal_from].steal_batch_and_pop(&worker) {
Ok(repeat_count) => {
if repeat_count > 0 {
while let Err(_) = worker.push(repeat_count - 1) {}
}
continue 'new_task;
}
Err(GenericStealError::Empty) => {
if pool_size == 1 {
return start_time;
}
pool_size -= 1;
other_workers_id[idx] = other_workers_id[pool_size];
}
Err(GenericStealError::Busy) => {}
}
}
}
}
}));
}
let mut start = None;
for th in threads {
if let Ok(Some(start_time)) = th.join() {
start = Some(start_time);
}
}
start.unwrap().elapsed()
})
});
}
pub fn executor_st3_fifo(c: &mut Criterion) {
executor::<usize, st3::fifo::Worker<_>>("st3_fifo", c);
}
pub fn executor_st3_lifo(c: &mut Criterion) {
executor::<usize, st3::lifo::Worker<_>>("st3_lifo", c);
}
pub fn executor_tokio(c: &mut Criterion) {
executor::<usize, tokio_queue::Local<_>>("tokio", c);
}
pub fn executor_crossbeam_fifo(c: &mut Criterion) {
executor::<usize, CrossbeamFifoWorker<_>>("crossbeam_fifo", c);
}
pub fn executor_crossbeam_lifo(c: &mut Criterion) {
executor::<usize, CrossbeamLifoWorker<_>>("crossbeam_lifo", c);
}
criterion_group!(
benches,
push_pop_small_batch_st3_fifo,
push_pop_small_batch_st3_lifo,
push_pop_small_batch_tokio,
push_pop_small_batch_crossbeam_fifo,
push_pop_small_batch_crossbeam_lifo,
push_pop_large_batch_st3_fifo,
push_pop_large_batch_st3_lifo,
push_pop_large_batch_tokio,
push_pop_large_batch_crossbeam_fifo,
push_pop_large_batch_crossbeam_lifo,
executor_st3_fifo,
executor_st3_lifo,
executor_tokio,
executor_crossbeam_fifo,
executor_crossbeam_lifo,
);
criterion_main!(benches);