benches: use criterion instead of bencher (#5981)

This commit is contained in:
M.Amin Rayej
2023-09-10 16:42:53 +02:00
committed by GitHub
parent 737dff40cb
commit b046c0dcbb
14 changed files with 779 additions and 646 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ test-util = ["tokio/test-util"]
[dependencies] [dependencies]
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] } tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
bencher = "0.1.5" criterion = "0.5.1"
rand = "0.8" rand = "0.8"
rand_chacha = "0.3" rand_chacha = "0.3"
+51 -39
View File
@@ -1,4 +1,4 @@
use bencher::{benchmark_group, benchmark_main, Bencher}; use criterion::{criterion_group, criterion_main, Criterion};
use rand::{Rng, SeedableRng}; use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng; use rand_chacha::ChaCha20Rng;
@@ -174,65 +174,77 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap() .unwrap()
} }
fn copy_mem_to_mem(b: &mut Bencher) { fn copy_mem_to_mem(c: &mut Criterion) {
let rt = rt(); let rt = rt();
b.iter(|| { c.bench_function("copy_mem_to_mem", |b| {
let task = || async { b.iter(|| {
let mut source = repeat(0).take(SOURCE_SIZE); let task = || async {
let mut dest = Vec::new(); let mut source = repeat(0).take(SOURCE_SIZE);
copy(&mut source, &mut dest).await.unwrap(); let mut dest = Vec::new();
}; copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task()); rt.block_on(task());
}) })
});
} }
fn copy_mem_to_slow_hdd(b: &mut Bencher) { fn copy_mem_to_slow_hdd(c: &mut Criterion) {
let rt = rt(); let rt = rt();
b.iter(|| { c.bench_function("copy_mem_to_slow_hdd", |b| {
let task = || async { b.iter(|| {
let mut source = repeat(0).take(SOURCE_SIZE); let task = || async {
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER); let mut source = repeat(0).take(SOURCE_SIZE);
copy(&mut source, &mut dest).await.unwrap(); let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
}; copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task()); rt.block_on(task());
}) })
});
} }
fn copy_chunk_to_mem(b: &mut Bencher) { fn copy_chunk_to_mem(c: &mut Criterion) {
let rt = rt(); let rt = rt();
b.iter(|| {
let task = || async {
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task()); c.bench_function("copy_chunk_to_mem", |b| {
}) b.iter(|| {
let task = || async {
let mut source =
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = Vec::new();
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
});
} }
fn copy_chunk_to_slow_hdd(b: &mut Bencher) { fn copy_chunk_to_slow_hdd(c: &mut Criterion) {
let rt = rt(); let rt = rt();
b.iter(|| {
let task = || async {
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task()); c.bench_function("copy_chunk_to_slow_hdd", |b| {
}) b.iter(|| {
let task = || async {
let mut source =
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
copy(&mut source, &mut dest).await.unwrap();
};
rt.block_on(task());
})
});
} }
benchmark_group!( criterion_group!(
copy_bench, copy_bench,
copy_mem_to_mem, copy_mem_to_mem,
copy_mem_to_slow_hdd, copy_mem_to_slow_hdd,
copy_chunk_to_mem, copy_chunk_to_mem,
copy_chunk_to_slow_hdd, copy_chunk_to_slow_hdd,
); );
benchmark_main!(copy_bench); criterion_main!(copy_bench);
+66 -57
View File
@@ -6,7 +6,7 @@ use tokio::fs::File;
use tokio::io::AsyncReadExt; use tokio::io::AsyncReadExt;
use tokio_util::codec::{BytesCodec, FramedRead /*FramedWrite*/}; use tokio_util::codec::{BytesCodec, FramedRead /*FramedWrite*/};
use bencher::{benchmark_group, benchmark_main, Bencher}; use criterion::{criterion_group, criterion_main, Criterion};
use std::fs::File as StdFile; use std::fs::File as StdFile;
use std::io::Read as StdRead; use std::io::Read as StdRead;
@@ -23,81 +23,90 @@ const BLOCK_COUNT: usize = 1_000;
const BUFFER_SIZE: usize = 4096; const BUFFER_SIZE: usize = 4096;
const DEV_ZERO: &str = "/dev/zero"; const DEV_ZERO: &str = "/dev/zero";
fn async_read_codec(b: &mut Bencher) { fn async_read_codec(c: &mut Criterion) {
let rt = rt(); let rt = rt();
b.iter(|| { c.bench_function("async_read_codec", |b| {
let task = || async { b.iter(|| {
let file = File::open(DEV_ZERO).await.unwrap(); let task = || async {
let mut input_stream = FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE); let file = File::open(DEV_ZERO).await.unwrap();
let mut input_stream =
FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
for _i in 0..BLOCK_COUNT { for _i in 0..BLOCK_COUNT {
let _bytes = input_stream.next().await.unwrap(); let _bytes = input_stream.next().await.unwrap();
}
};
rt.block_on(task());
});
}
fn async_read_buf(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let task = || async {
let mut file = File::open(DEV_ZERO).await.unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
let count = file.read(&mut buffer).await.unwrap();
if count == 0 {
break;
} }
} };
};
rt.block_on(task()); rt.block_on(task());
})
}); });
} }
fn async_read_std_file(b: &mut Bencher) { fn async_read_buf(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let task = || async { c.bench_function("async_read_buf", |b| {
let mut file = tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap())); b.iter(|| {
let task = || async {
let mut file = File::open(DEV_ZERO).await.unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT { for _i in 0..BLOCK_COUNT {
let count = file.read(&mut buffer).await.unwrap();
if count == 0 {
break;
}
}
};
rt.block_on(task());
});
});
}
fn async_read_std_file(c: &mut Criterion) {
let rt = rt();
c.bench_function("async_read_std_file", |b| {
b.iter(|| {
let task = || async {
let mut file =
tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
for _i in 0..BLOCK_COUNT {
let mut buffer = [0u8; BUFFER_SIZE];
let mut file_ref = file.as_mut();
tokio::task::block_in_place(move || {
file_ref.read_exact(&mut buffer).unwrap();
});
}
};
rt.block_on(task());
});
});
}
fn sync_read(c: &mut Criterion) {
c.bench_function("sync_read", |b| {
b.iter(|| {
let mut file = StdFile::open(DEV_ZERO).unwrap();
let mut buffer = [0u8; BUFFER_SIZE]; let mut buffer = [0u8; BUFFER_SIZE];
let mut file_ref = file.as_mut();
tokio::task::block_in_place(move || { for _i in 0..BLOCK_COUNT {
file_ref.read_exact(&mut buffer).unwrap(); file.read_exact(&mut buffer).unwrap();
}); }
} })
};
b.iter(|| {
rt.block_on(task());
}); });
} }
fn sync_read(b: &mut Bencher) { criterion_group!(
b.iter(|| {
let mut file = StdFile::open(DEV_ZERO).unwrap();
let mut buffer = [0u8; BUFFER_SIZE];
for _i in 0..BLOCK_COUNT {
file.read_exact(&mut buffer).unwrap();
}
});
}
benchmark_group!(
file, file,
async_read_std_file, async_read_std_file,
async_read_buf, async_read_buf,
async_read_codec, async_read_codec,
sync_read sync_read
); );
criterion_main!(file);
benchmark_main!(file);
+39 -33
View File
@@ -4,46 +4,50 @@
use tokio::runtime::{self, Runtime}; use tokio::runtime::{self, Runtime};
use bencher::{benchmark_group, benchmark_main, Bencher}; use criterion::{criterion_group, criterion_main, Criterion};
const NUM_SPAWN: usize = 1_000; const NUM_SPAWN: usize = 1_000;
fn spawn_many_local(b: &mut Bencher) { fn spawn_many_local(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN); let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| { c.bench_function("spawn_many_local", |b| {
rt.block_on(async { b.iter(|| {
for _ in 0..NUM_SPAWN { rt.block_on(async {
handles.push(tokio::spawn(async move {})); for _ in 0..NUM_SPAWN {
} handles.push(tokio::spawn(async move {}));
}
for handle in handles.drain(..) { for handle in handles.drain(..) {
handle.await.unwrap(); handle.await.unwrap();
} }
}); });
})
}); });
} }
fn spawn_many_remote_idle(b: &mut Bencher) { fn spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let rt_handle = rt.handle(); let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN); let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| { c.bench_function("spawn_many_remote_idle", |b| {
for _ in 0..NUM_SPAWN { b.iter(|| {
handles.push(rt_handle.spawn(async {})); for _ in 0..NUM_SPAWN {
} handles.push(rt_handle.spawn(async {}));
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
} }
});
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
}); });
} }
fn spawn_many_remote_busy(b: &mut Bencher) { fn spawn_many_remote_busy(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let rt_handle = rt.handle(); let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN); let mut handles = Vec::with_capacity(NUM_SPAWN);
@@ -56,16 +60,18 @@ fn spawn_many_remote_busy(b: &mut Bencher) {
iter() iter()
}); });
b.iter(|| { c.bench_function("spawn_many_remote_busy", |b| {
for _ in 0..NUM_SPAWN { b.iter(|| {
handles.push(rt_handle.spawn(async {})); for _ in 0..NUM_SPAWN {
} handles.push(rt_handle.spawn(async {}));
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
} }
});
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
}); });
} }
@@ -73,11 +79,11 @@ fn rt() -> Runtime {
runtime::Builder::new_current_thread().build().unwrap() runtime::Builder::new_current_thread().build().unwrap()
} }
benchmark_group!( criterion_group!(
scheduler, scheduler,
spawn_many_local, spawn_many_local,
spawn_many_remote_idle, spawn_many_remote_idle,
spawn_many_remote_busy spawn_many_remote_busy
); );
benchmark_main!(scheduler); criterion_main!(scheduler);
+117 -104
View File
@@ -5,63 +5,68 @@
use tokio::runtime::{self, Runtime}; use tokio::runtime::{self, Runtime};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::sync::atomic::Ordering::Relaxed; use std::sync::atomic::Ordering::Relaxed;
use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{mpsc, Arc}; use std::sync::{mpsc, Arc};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use criterion::{criterion_group, criterion_main, Criterion};
const NUM_WORKERS: usize = 4; const NUM_WORKERS: usize = 4;
const NUM_SPAWN: usize = 10_000; const NUM_SPAWN: usize = 10_000;
const STALL_DUR: Duration = Duration::from_micros(10); const STALL_DUR: Duration = Duration::from_micros(10);
fn spawn_many_local(b: &mut Bencher) { fn spawn_many_local(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let (tx, rx) = mpsc::sync_channel(1000); let (tx, rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0)); let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| { c.bench_function("spawn_many_local", |b| {
rem.store(NUM_SPAWN, Relaxed); b.iter(|| {
rem.store(NUM_SPAWN, Relaxed);
rt.block_on(async { rt.block_on(async {
for _ in 0..NUM_SPAWN { for _ in 0..NUM_SPAWN {
let tx = tx.clone(); let tx = tx.clone();
let rem = rem.clone(); let rem = rem.clone();
tokio::spawn(async move { tokio::spawn(async move {
if 1 == rem.fetch_sub(1, Relaxed) { if 1 == rem.fetch_sub(1, Relaxed) {
tx.send(()).unwrap(); tx.send(()).unwrap();
} }
}); });
} }
let _ = rx.recv().unwrap(); let _ = rx.recv().unwrap();
}); });
})
}); });
} }
fn spawn_many_remote_idle(b: &mut Bencher) { fn spawn_many_remote_idle(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let mut handles = Vec::with_capacity(NUM_SPAWN); let mut handles = Vec::with_capacity(NUM_SPAWN);
b.iter(|| { c.bench_function("spawn_many_remote_idle", |b| {
for _ in 0..NUM_SPAWN { b.iter(|| {
handles.push(rt.spawn(async {})); for _ in 0..NUM_SPAWN {
} handles.push(rt.spawn(async {}));
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
} }
});
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
}); });
} }
// The runtime is busy with tasks that consume CPU time and yield. Yielding is a // The runtime is busy with tasks that consume CPU time and yield. Yielding is a
// lower notification priority than spawning / regular notification. // lower notification priority than spawning / regular notification.
fn spawn_many_remote_busy1(b: &mut Bencher) { fn spawn_many_remote_busy1(c: &mut Criterion) {
let rt = rt(); let rt = rt();
let rt_handle = rt.handle(); let rt_handle = rt.handle();
let mut handles = Vec::with_capacity(NUM_SPAWN); let mut handles = Vec::with_capacity(NUM_SPAWN);
@@ -78,16 +83,18 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
}); });
} }
b.iter(|| { c.bench_function("spawn_many_remote_busy1", |b| {
for _ in 0..NUM_SPAWN { b.iter(|| {
handles.push(rt_handle.spawn(async {})); for _ in 0..NUM_SPAWN {
} handles.push(rt_handle.spawn(async {}));
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
} }
});
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
}); });
flag.store(false, Relaxed); flag.store(false, Relaxed);
@@ -95,7 +102,7 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
// The runtime is busy with tasks that consume CPU time and spawn new high-CPU // The runtime is busy with tasks that consume CPU time and spawn new high-CPU
// tasks. Spawning goes via a higher notification priority than yielding. // tasks. Spawning goes via a higher notification priority than yielding.
fn spawn_many_remote_busy2(b: &mut Bencher) { fn spawn_many_remote_busy2(c: &mut Criterion) {
const NUM_SPAWN: usize = 1_000; const NUM_SPAWN: usize = 1_000;
let rt = rt(); let rt = rt();
@@ -119,49 +126,52 @@ fn spawn_many_remote_busy2(b: &mut Bencher) {
}); });
} }
b.iter(|| { c.bench_function("spawn_many_remote_busy2", |b| {
for _ in 0..NUM_SPAWN { b.iter(|| {
handles.push(rt_handle.spawn(async {})); for _ in 0..NUM_SPAWN {
} handles.push(rt_handle.spawn(async {}));
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
} }
});
rt.block_on(async {
for handle in handles.drain(..) {
handle.await.unwrap();
}
});
})
}); });
flag.store(false, Relaxed); flag.store(false, Relaxed);
} }
fn yield_many(b: &mut Bencher) { fn yield_many(c: &mut Criterion) {
const NUM_YIELD: usize = 1_000; const NUM_YIELD: usize = 1_000;
const TASKS: usize = 200; const TASKS: usize = 200;
let rt = rt(); c.bench_function("yield_many", |b| {
let rt = rt();
let (tx, rx) = mpsc::sync_channel(TASKS);
let (tx, rx) = mpsc::sync_channel(TASKS); b.iter(move || {
for _ in 0..TASKS {
let tx = tx.clone();
b.iter(move || { rt.spawn(async move {
for _ in 0..TASKS { for _ in 0..NUM_YIELD {
let tx = tx.clone(); tokio::task::yield_now().await;
}
rt.spawn(async move { tx.send(()).unwrap();
for _ in 0..NUM_YIELD { });
tokio::task::yield_now().await; }
}
tx.send(()).unwrap(); for _ in 0..TASKS {
}); let _ = rx.recv().unwrap();
} }
})
for _ in 0..TASKS {
let _ = rx.recv().unwrap();
}
}); });
} }
fn ping_pong(b: &mut Bencher) { fn ping_pong(c: &mut Criterion) {
const NUM_PINGS: usize = 1_000; const NUM_PINGS: usize = 1_000;
let rt = rt(); let rt = rt();
@@ -169,46 +179,46 @@ fn ping_pong(b: &mut Bencher) {
let (done_tx, done_rx) = mpsc::sync_channel(1000); let (done_tx, done_rx) = mpsc::sync_channel(1000);
let rem = Arc::new(AtomicUsize::new(0)); let rem = Arc::new(AtomicUsize::new(0));
b.iter(|| { c.bench_function("ping_pong", |b| {
let done_tx = done_tx.clone(); b.iter(|| {
let rem = rem.clone(); let done_tx = done_tx.clone();
rem.store(NUM_PINGS, Relaxed); let rem = rem.clone();
rem.store(NUM_PINGS, Relaxed);
rt.block_on(async { rt.block_on(async {
tokio::spawn(async move { tokio::spawn(async move {
for _ in 0..NUM_PINGS { for _ in 0..NUM_PINGS {
let rem = rem.clone(); let rem = rem.clone();
let done_tx = done_tx.clone(); let done_tx = done_tx.clone();
tokio::spawn(async move {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
tokio::spawn(async move { tokio::spawn(async move {
rx1.await.unwrap(); let (tx1, rx1) = oneshot::channel();
tx2.send(()).unwrap(); let (tx2, rx2) = oneshot::channel();
tokio::spawn(async move {
rx1.await.unwrap();
tx2.send(()).unwrap();
});
tx1.send(()).unwrap();
rx2.await.unwrap();
if 1 == rem.fetch_sub(1, Relaxed) {
done_tx.send(()).unwrap();
}
}); });
}
});
tx1.send(()).unwrap(); done_rx.recv().unwrap();
rx2.await.unwrap();
if 1 == rem.fetch_sub(1, Relaxed) {
done_tx.send(()).unwrap();
}
});
}
}); });
})
done_rx.recv().unwrap();
});
}); });
} }
fn chained_spawn(b: &mut Bencher) { fn chained_spawn(c: &mut Criterion) {
const ITER: usize = 1_000; const ITER: usize = 1_000;
let rt = rt();
fn iter(done_tx: mpsc::SyncSender<()>, n: usize) { fn iter(done_tx: mpsc::SyncSender<()>, n: usize) {
if n == 0 { if n == 0 {
done_tx.send(()).unwrap(); done_tx.send(()).unwrap();
@@ -219,18 +229,21 @@ fn chained_spawn(b: &mut Bencher) {
} }
} }
let (done_tx, done_rx) = mpsc::sync_channel(1000); c.bench_function("chained_spawn", |b| {
let rt = rt();
let (done_tx, done_rx) = mpsc::sync_channel(1000);
b.iter(move || { b.iter(move || {
let done_tx = done_tx.clone(); let done_tx = done_tx.clone();
rt.block_on(async { rt.block_on(async {
tokio::spawn(async move { tokio::spawn(async move {
iter(done_tx, ITER); iter(done_tx, ITER);
});
done_rx.recv().unwrap();
}); });
})
done_rx.recv().unwrap();
});
}); });
} }
@@ -249,7 +262,7 @@ fn stall() {
} }
} }
benchmark_group!( criterion_group!(
scheduler, scheduler,
spawn_many_local, spawn_many_local,
spawn_many_remote_idle, spawn_many_remote_idle,
@@ -260,4 +273,4 @@ benchmark_group!(
chained_spawn, chained_spawn,
); );
benchmark_main!(scheduler); criterion_main!(scheduler);
+17 -15
View File
@@ -1,7 +1,7 @@
//! Benchmark the delay in propagating OS signals to any listeners. //! Benchmark the delay in propagating OS signals to any listeners.
#![cfg(unix)] #![cfg(unix)]
use bencher::{benchmark_group, benchmark_main, Bencher}; use criterion::{criterion_group, criterion_main, Criterion};
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@@ -41,7 +41,7 @@ pub fn send_signal(signal: libc::c_int) {
} }
} }
fn many_signals(bench: &mut Bencher) { fn many_signals(c: &mut Criterion) {
let num_signals = 10; let num_signals = 10;
let (tx, mut rx) = mpsc::channel(num_signals); let (tx, mut rx) = mpsc::channel(num_signals);
@@ -75,21 +75,23 @@ fn many_signals(bench: &mut Bencher) {
// tasks have been polled at least once // tasks have been polled at least once
rt.block_on(Spinner::new()); rt.block_on(Spinner::new());
bench.iter(|| { c.bench_function("many_signals", |b| {
rt.block_on(async { b.iter(|| {
send_signal(libc::SIGCHLD); rt.block_on(async {
for _ in 0..num_signals { send_signal(libc::SIGCHLD);
rx.recv().await.expect("channel closed"); for _ in 0..num_signals {
} rx.recv().await.expect("channel closed");
}
send_signal(libc::SIGIO); send_signal(libc::SIGIO);
for _ in 0..num_signals { for _ in 0..num_signals {
rx.recv().await.expect("channel closed"); rx.recv().await.expect("channel closed");
} }
}); });
})
}); });
} }
benchmark_group!(signal_group, many_signals,); criterion_group!(signal_group, many_signals);
benchmark_main!(signal_group); criterion_main!(signal_group);
+47 -40
View File
@@ -2,10 +2,7 @@
//! This essentially measure the time to enqueue a task in the local and remote //! This essentially measure the time to enqueue a task in the local and remote
//! case. //! case.
#[macro_use] use criterion::{black_box, criterion_group, criterion_main, Criterion};
extern crate bencher;
use bencher::{black_box, Bencher};
async fn work() -> usize { async fn work() -> usize {
let val = 1 + 1; let val = 1 + 1;
@@ -13,67 +10,77 @@ async fn work() -> usize {
black_box(val) black_box(val)
} }
fn basic_scheduler_spawn(bench: &mut Bencher) { fn basic_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.build() .build()
.unwrap(); .unwrap();
bench.iter(|| {
runtime.block_on(async { c.bench_function("basic_scheduler_spawn", |b| {
let h = tokio::spawn(work()); b.iter(|| {
assert_eq!(h.await.unwrap(), 2); runtime.block_on(async {
}); let h = tokio::spawn(work());
assert_eq!(h.await.unwrap(), 2);
});
})
}); });
} }
fn basic_scheduler_spawn10(bench: &mut Bencher) { fn basic_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_current_thread() let runtime = tokio::runtime::Builder::new_current_thread()
.build() .build()
.unwrap(); .unwrap();
bench.iter(|| {
runtime.block_on(async { c.bench_function("basic_scheduler_spawn10", |b| {
let mut handles = Vec::with_capacity(10); b.iter(|| {
for _ in 0..10 { runtime.block_on(async {
handles.push(tokio::spawn(work())); let mut handles = Vec::with_capacity(10);
} for _ in 0..10 {
for handle in handles { handles.push(tokio::spawn(work()));
assert_eq!(handle.await.unwrap(), 2); }
} for handle in handles {
}); assert_eq!(handle.await.unwrap(), 2);
}
});
})
}); });
} }
fn threaded_scheduler_spawn(bench: &mut Bencher) { fn threaded_scheduler_spawn(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread() let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1) .worker_threads(1)
.build() .build()
.unwrap(); .unwrap();
bench.iter(|| { c.bench_function("threaded_scheduler_spawn", |b| {
runtime.block_on(async { b.iter(|| {
let h = tokio::spawn(work()); runtime.block_on(async {
assert_eq!(h.await.unwrap(), 2); let h = tokio::spawn(work());
}); assert_eq!(h.await.unwrap(), 2);
});
})
}); });
} }
fn threaded_scheduler_spawn10(bench: &mut Bencher) { fn threaded_scheduler_spawn10(c: &mut Criterion) {
let runtime = tokio::runtime::Builder::new_multi_thread() let runtime = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1) .worker_threads(1)
.build() .build()
.unwrap(); .unwrap();
bench.iter(|| { c.bench_function("threaded_scheduler_spawn10", |b| {
runtime.block_on(async { b.iter(|| {
let mut handles = Vec::with_capacity(10); runtime.block_on(async {
for _ in 0..10 { let mut handles = Vec::with_capacity(10);
handles.push(tokio::spawn(work())); for _ in 0..10 {
} handles.push(tokio::spawn(work()));
for handle in handles { }
assert_eq!(handle.await.unwrap(), 2); for handle in handles {
} assert_eq!(handle.await.unwrap(), 2);
}); }
});
})
}); });
} }
bencher::benchmark_group!( criterion_group!(
spawn, spawn,
basic_scheduler_spawn, basic_scheduler_spawn,
basic_scheduler_spawn10, basic_scheduler_spawn10,
@@ -81,4 +88,4 @@ bencher::benchmark_group!(
threaded_scheduler_spawn10, threaded_scheduler_spawn10,
); );
bencher::benchmark_main!(spawn); criterion_main!(spawn);
+152 -130
View File
@@ -1,8 +1,23 @@
use bencher::{black_box, Bencher};
use tokio::sync::mpsc; use tokio::sync::mpsc;
type Medium = [usize; 64]; use criterion::measurement::WallTime;
type Large = [Medium; 64]; use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
#[derive(Debug, Copy, Clone)]
struct Medium([usize; 64]);
impl Default for Medium {
fn default() -> Self {
Medium([0; 64])
}
}
#[derive(Debug, Copy, Clone)]
struct Large([Medium; 64]);
impl Default for Large {
fn default() -> Self {
Large([Medium::default(); 64])
}
}
fn rt() -> tokio::runtime::Runtime { fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
@@ -11,169 +26,176 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap() .unwrap()
} }
fn create_1_medium(b: &mut Bencher) { fn create_medium<const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>) {
b.iter(|| { g.bench_function(SIZE.to_string(), |b| {
black_box(&mpsc::channel::<Medium>(1)); b.iter(|| {
}); black_box(&mpsc::channel::<Medium>(SIZE));
}
fn create_100_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100));
});
}
fn create_100_000_medium(b: &mut Bencher) {
b.iter(|| {
black_box(&mpsc::channel::<Medium>(100_000));
});
}
fn send_medium(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Medium>(1000);
let _ = rt.block_on(tx.send([0; 64]));
rt.block_on(rx.recv()).unwrap();
});
}
fn send_large(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
let (tx, mut rx) = mpsc::channel::<Large>(1000);
let _ = rt.block_on(tx.send([[0; 64]; 64]));
rt.block_on(rx.recv()).unwrap();
});
}
fn contention_bounded(b: &mut Bencher) {
let rt = rt();
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 {
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 {
let _ = rx.recv().await;
}
}) })
}); });
} }
fn contention_bounded_full(b: &mut Bencher) { fn send_data<T: Default, const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>, prefix: &str) {
let rt = rt(); let rt = rt();
b.iter(|| { g.bench_function(format!("{}_{}", prefix, SIZE), |b| {
rt.block_on(async move { b.iter(|| {
let (tx, mut rx) = mpsc::channel::<usize>(100); let (tx, mut rx) = mpsc::channel::<T>(SIZE);
for _ in 0..5 { let _ = rt.block_on(tx.send(T::default()));
let tx = tx.clone();
tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..1_000 * 5 { rt.block_on(rx.recv()).unwrap();
let _ = rx.recv().await;
}
}) })
}); });
} }
fn contention_unbounded(b: &mut Bencher) { fn contention_bounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt(); let rt = rt();
b.iter(|| { g.bench_function("bounded", |b| {
rt.block_on(async move { b.iter(|| {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>(); rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
for _ in 0..5 { for _ in 0..5 {
let tx = tx.clone(); let tx = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
for i in 0..1000 { for i in 0..1000 {
tx.send(i).unwrap(); tx.send(i).await.unwrap();
} }
}); });
} }
for _ in 0..1_000 * 5 { for _ in 0..1_000 * 5 {
let _ = rx.recv().await; let _ = rx.recv().await;
} }
})
}) })
}); });
} }
fn uncontented_bounded(b: &mut Bencher) { fn contention_bounded_full(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt(); let rt = rt();
b.iter(|| { g.bench_function("bounded_full", |b| {
rt.block_on(async move { b.iter(|| {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000); rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(100);
for i in 0..5000 { for _ in 0..5 {
tx.send(i).await.unwrap(); let tx = tx.clone();
} tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).await.unwrap();
}
});
}
for _ in 0..5_000 { for _ in 0..1_000 * 5 {
let _ = rx.recv().await; let _ = rx.recv().await;
} }
})
}) })
}); });
} }
fn uncontented_unbounded(b: &mut Bencher) { fn contention_unbounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt(); let rt = rt();
b.iter(|| { g.bench_function("unbounded", |b| {
rt.block_on(async move { b.iter(|| {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>(); rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for i in 0..5000 { for _ in 0..5 {
tx.send(i).unwrap(); let tx = tx.clone();
} tokio::spawn(async move {
for i in 0..1000 {
tx.send(i).unwrap();
}
});
}
for _ in 0..5_000 { for _ in 0..1_000 * 5 {
let _ = rx.recv().await; let _ = rx.recv().await;
} }
})
}) })
}); });
} }
bencher::benchmark_group!( fn uncontented_bounded(g: &mut BenchmarkGroup<WallTime>) {
create, let rt = rt();
create_1_medium,
create_100_medium,
create_100_000_medium
);
bencher::benchmark_group!(send, send_medium, send_large); g.bench_function("bounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
bencher::benchmark_group!( for i in 0..5000 {
contention, tx.send(i).await.unwrap();
contention_bounded, }
contention_bounded_full,
contention_unbounded,
uncontented_bounded,
uncontented_unbounded
);
bencher::benchmark_main!(create, send, contention); for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
})
});
}
fn uncontented_unbounded(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt();
g.bench_function("unbounded", |b| {
b.iter(|| {
rt.block_on(async move {
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
for i in 0..5000 {
tx.send(i).unwrap();
}
for _ in 0..5_000 {
let _ = rx.recv().await;
}
})
})
});
}
fn bench_create_medium(c: &mut Criterion) {
let mut group = c.benchmark_group("create_medium");
create_medium::<1>(&mut group);
create_medium::<100>(&mut group);
create_medium::<100_000>(&mut group);
group.finish();
}
fn bench_send(c: &mut Criterion) {
let mut group = c.benchmark_group("send");
send_data::<Medium, 1000>(&mut group, "medium");
send_data::<Large, 1000>(&mut group, "large");
group.finish();
}
fn bench_contention(c: &mut Criterion) {
let mut group = c.benchmark_group("contention");
contention_bounded(&mut group);
contention_bounded_full(&mut group);
contention_unbounded(&mut group);
group.finish();
}
fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
uncontented_bounded(&mut group);
uncontented_unbounded(&mut group);
group.finish();
}
criterion_group!(create, bench_create_medium);
criterion_group!(send, bench_send);
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(create, send, contention, uncontented);
+19 -16
View File
@@ -1,27 +1,28 @@
use bencher::{benchmark_group, benchmark_main, Bencher};
use tokio::{ use tokio::{
runtime::Runtime, runtime::Runtime,
sync::{mpsc, oneshot}, sync::{mpsc, oneshot},
}; };
fn request_reply_current_thread(b: &mut Bencher) { use criterion::{criterion_group, criterion_main, Criterion};
fn request_reply_current_thread(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.build() .build()
.unwrap(); .unwrap();
request_reply(b, rt); request_reply(c, rt);
} }
fn request_reply_multi_threaded(b: &mut Bencher) { fn request_reply_multi_threaded(c: &mut Criterion) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1) .worker_threads(1)
.build() .build()
.unwrap(); .unwrap();
request_reply(b, rt); request_reply(c, rt);
} }
fn request_reply(b: &mut Bencher, rt: Runtime) { fn request_reply(b: &mut Criterion, rt: Runtime) {
let tx = rt.block_on(async move { let tx = rt.block_on(async move {
let (tx, mut rx) = mpsc::channel::<oneshot::Sender<()>>(10); let (tx, mut rx) = mpsc::channel::<oneshot::Sender<()>>(10);
tokio::spawn(async move { tokio::spawn(async move {
@@ -32,22 +33,24 @@ fn request_reply(b: &mut Bencher, rt: Runtime) {
tx tx
}); });
b.iter(|| { b.bench_function("request_reply", |b| {
let task_tx = tx.clone(); b.iter(|| {
rt.block_on(async move { let task_tx = tx.clone();
for _ in 0..1_000 { rt.block_on(async move {
let (o_tx, o_rx) = oneshot::channel(); for _ in 0..1_000 {
task_tx.send(o_tx).await.unwrap(); let (o_tx, o_rx) = oneshot::channel();
let _ = o_rx.await; task_tx.send(o_tx).await.unwrap();
} let _ = o_rx.await;
}
})
}) })
}); });
} }
benchmark_group!( criterion_group!(
sync_mpsc_oneshot_group, sync_mpsc_oneshot_group,
request_reply_current_thread, request_reply_current_thread,
request_reply_multi_threaded, request_reply_multi_threaded,
); );
benchmark_main!(sync_mpsc_oneshot_group); criterion_main!(sync_mpsc_oneshot_group);
+47 -33
View File
@@ -1,9 +1,11 @@
use bencher::Bencher;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::Notify; use tokio::sync::Notify;
use criterion::measurement::WallTime;
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn rt() -> tokio::runtime::Runtime { fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
@@ -11,7 +13,7 @@ fn rt() -> tokio::runtime::Runtime {
.unwrap() .unwrap()
} }
fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) { fn notify_waiters<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt(); let rt = rt();
let notify = Arc::new(Notify::new()); let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0)); let counter = Arc::new(AtomicUsize::new(0));
@@ -29,18 +31,20 @@ fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
} }
const N_ITERS: usize = 500; const N_ITERS: usize = 500;
b.iter(|| { g.bench_function(N_WAITERS.to_string(), |b| {
counter.store(0, Ordering::Relaxed); b.iter(|| {
loop { counter.store(0, Ordering::Relaxed);
notify.notify_waiters(); loop {
if counter.load(Ordering::Relaxed) >= N_ITERS { notify.notify_waiters();
break; if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
} }
} })
}); });
} }
fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) { fn notify_one<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
let rt = rt(); let rt = rt();
let notify = Arc::new(Notify::new()); let notify = Arc::new(Notify::new());
let counter = Arc::new(AtomicUsize::new(0)); let counter = Arc::new(AtomicUsize::new(0));
@@ -58,33 +62,43 @@ fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) {
} }
const N_ITERS: usize = 500; const N_ITERS: usize = 500;
b.iter(|| { g.bench_function(N_WAITERS.to_string(), |b| {
counter.store(0, Ordering::Relaxed); b.iter(|| {
loop { counter.store(0, Ordering::Relaxed);
notify.notify_one(); loop {
if counter.load(Ordering::Relaxed) >= N_ITERS { notify.notify_one();
break; if counter.load(Ordering::Relaxed) >= N_ITERS {
break;
}
} }
} })
}); });
} }
bencher::benchmark_group!( fn bench_notify_one(c: &mut Criterion) {
let mut group = c.benchmark_group("notify_one");
notify_one::<10>(&mut group);
notify_one::<50>(&mut group);
notify_one::<100>(&mut group);
notify_one::<200>(&mut group);
notify_one::<500>(&mut group);
group.finish();
}
fn bench_notify_waiters(c: &mut Criterion) {
let mut group = c.benchmark_group("notify_waiters");
notify_waiters::<10>(&mut group);
notify_waiters::<50>(&mut group);
notify_waiters::<100>(&mut group);
notify_waiters::<200>(&mut group);
notify_waiters::<500>(&mut group);
group.finish();
}
criterion_group!(
notify_waiters_simple, notify_waiters_simple,
notify_waiters::<10>, bench_notify_one,
notify_waiters::<50>, bench_notify_waiters
notify_waiters::<100>,
notify_waiters::<200>,
notify_waiters::<500>
); );
bencher::benchmark_group!( criterion_main!(notify_waiters_simple);
notify_one_simple,
notify_one::<10>,
notify_one::<50>,
notify_one::<100>,
notify_one::<200>,
notify_one::<500>
);
bencher::benchmark_main!(notify_waiters_simple, notify_one_simple);
+91 -70
View File
@@ -1,26 +1,30 @@
use bencher::{black_box, Bencher};
use std::sync::Arc; use std::sync::Arc;
use tokio::{sync::RwLock, task}; use tokio::{sync::RwLock, task};
fn read_uncontended(b: &mut Bencher) { use criterion::measurement::WallTime;
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn read_uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
.build() .build()
.unwrap(); .unwrap();
let lock = Arc::new(RwLock::new(())); let lock = Arc::new(RwLock::new(()));
b.iter(|| { g.bench_function("read", |b| {
let lock = lock.clone(); b.iter(|| {
rt.block_on(async move { let lock = lock.clone();
for _ in 0..6 { rt.block_on(async move {
let read = lock.read().await; for _ in 0..6 {
let _read = black_box(read); let read = lock.read().await;
} let _read = black_box(read);
}
})
}) })
}); });
} }
fn read_concurrent_uncontended_multi(b: &mut Bencher) { fn read_concurrent_uncontended_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
.build() .build()
@@ -32,23 +36,25 @@ fn read_concurrent_uncontended_multi(b: &mut Bencher) {
} }
let lock = Arc::new(RwLock::new(())); let lock = Arc::new(RwLock::new(()));
b.iter(|| { g.bench_function("read_concurrent_multi", |b| {
let lock = lock.clone(); b.iter(|| {
rt.block_on(async move { let lock = lock.clone();
let j = tokio::try_join! { rt.block_on(async move {
task::spawn(task(lock.clone())), let j = tokio::try_join! {
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())) task::spawn(task(lock.clone())),
}; task::spawn(task(lock.clone()))
j.unwrap(); };
j.unwrap();
})
}) })
}); });
} }
fn read_concurrent_uncontended(b: &mut Bencher) { fn read_concurrent_uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.build() .build()
.unwrap(); .unwrap();
@@ -59,22 +65,24 @@ fn read_concurrent_uncontended(b: &mut Bencher) {
} }
let lock = Arc::new(RwLock::new(())); let lock = Arc::new(RwLock::new(()));
b.iter(|| { g.bench_function("read_concurrent", |b| {
let lock = lock.clone(); b.iter(|| {
rt.block_on(async move { let lock = lock.clone();
tokio::join! { rt.block_on(async move {
task(lock.clone()), tokio::join! {
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
task(lock.clone()) task(lock.clone()),
}; task(lock.clone())
};
})
}) })
}); });
} }
fn read_concurrent_contended_multi(b: &mut Bencher) { fn read_concurrent_contended_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
.build() .build()
@@ -86,24 +94,26 @@ fn read_concurrent_contended_multi(b: &mut Bencher) {
} }
let lock = Arc::new(RwLock::new(())); let lock = Arc::new(RwLock::new(()));
b.iter(|| { g.bench_function("read_concurrent_multi", |b| {
let lock = lock.clone(); b.iter(|| {
rt.block_on(async move { let lock = lock.clone();
let write = lock.write().await; rt.block_on(async move {
let j = tokio::try_join! { let write = lock.write().await;
async move { drop(write); Ok(()) }, let j = tokio::try_join! {
task::spawn(task(lock.clone())), async move { drop(write); Ok(()) },
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
task::spawn(task(lock.clone())), task::spawn(task(lock.clone())),
}; task::spawn(task(lock.clone())),
j.unwrap(); };
j.unwrap();
})
}) })
}); });
} }
fn read_concurrent_contended(b: &mut Bencher) { fn read_concurrent_contended(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.build() .build()
.unwrap(); .unwrap();
@@ -114,29 +124,40 @@ fn read_concurrent_contended(b: &mut Bencher) {
} }
let lock = Arc::new(RwLock::new(())); let lock = Arc::new(RwLock::new(()));
b.iter(|| { g.bench_function("read_concurrent", |b| {
let lock = lock.clone(); b.iter(|| {
rt.block_on(async move { let lock = lock.clone();
let write = lock.write().await; rt.block_on(async move {
tokio::join! { let write = lock.write().await;
async move { drop(write) }, tokio::join! {
task(lock.clone()), async move { drop(write) },
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
task(lock.clone()), task(lock.clone()),
}; task(lock.clone()),
};
})
}) })
}); });
} }
bencher::benchmark_group!( fn bench_contention(c: &mut Criterion) {
sync_rwlock, let mut group = c.benchmark_group("contention");
read_uncontended, read_concurrent_contended(&mut group);
read_concurrent_uncontended, read_concurrent_contended_multi(&mut group);
read_concurrent_uncontended_multi, group.finish();
read_concurrent_contended, }
read_concurrent_contended_multi
);
bencher::benchmark_main!(sync_rwlock); fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
read_uncontended(&mut group);
read_concurrent_uncontended(&mut group);
read_concurrent_uncontended_multi(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(contention, uncontented);
+106 -84
View File
@@ -1,21 +1,36 @@
use bencher::Bencher;
use std::sync::Arc; use std::sync::Arc;
use tokio::runtime::Runtime;
use tokio::{sync::Semaphore, task}; use tokio::{sync::Semaphore, task};
fn uncontended(b: &mut Bencher) { use criterion::measurement::WallTime;
let rt = tokio::runtime::Builder::new_multi_thread() use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
fn single_rt() -> Runtime {
tokio::runtime::Builder::new_current_thread()
.build()
.unwrap()
}
fn multi_rt() -> Runtime {
tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
.build() .build()
.unwrap(); .unwrap()
}
fn uncontended(g: &mut BenchmarkGroup<WallTime>) {
let rt = multi_rt();
let s = Arc::new(Semaphore::new(10)); let s = Arc::new(Semaphore::new(10));
b.iter(|| { g.bench_function("multi", |b| {
let s = s.clone(); b.iter(|| {
rt.block_on(async move { let s = s.clone();
for _ in 0..6 { rt.block_on(async move {
let permit = s.acquire().await; for _ in 0..6 {
drop(permit); let permit = s.acquire().await;
} drop(permit);
}
})
}) })
}); });
} }
@@ -25,101 +40,108 @@ async fn task(s: Arc<Semaphore>) {
drop(permit); drop(permit);
} }
fn uncontended_concurrent_multi(b: &mut Bencher) { fn uncontended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = multi_rt();
.worker_threads(6)
.build()
.unwrap();
let s = Arc::new(Semaphore::new(10)); let s = Arc::new(Semaphore::new(10));
b.iter(|| { g.bench_function("concurrent_multi", |b| {
let s = s.clone(); b.iter(|| {
rt.block_on(async move { let s = s.clone();
let j = tokio::try_join! { rt.block_on(async move {
task::spawn(task(s.clone())), let j = tokio::try_join! {
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())) task::spawn(task(s.clone())),
}; task::spawn(task(s.clone()))
j.unwrap(); };
j.unwrap();
})
}) })
}); });
} }
fn uncontended_concurrent_single(b: &mut Bencher) { fn uncontended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = single_rt();
.build()
.unwrap();
let s = Arc::new(Semaphore::new(10)); let s = Arc::new(Semaphore::new(10));
b.iter(|| { g.bench_function("concurrent_single", |b| {
let s = s.clone(); b.iter(|| {
rt.block_on(async move { let s = s.clone();
tokio::join! { rt.block_on(async move {
task(s.clone()), tokio::join! {
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()) task(s.clone()),
}; task(s.clone())
};
})
}) })
}); });
} }
fn contended_concurrent_multi(b: &mut Bencher) { fn contended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_multi_thread() let rt = multi_rt();
.worker_threads(6)
.build()
.unwrap();
let s = Arc::new(Semaphore::new(5)); let s = Arc::new(Semaphore::new(5));
b.iter(|| { g.bench_function("concurrent_multi", |b| {
let s = s.clone(); b.iter(|| {
rt.block_on(async move { let s = s.clone();
let j = tokio::try_join! { rt.block_on(async move {
task::spawn(task(s.clone())), let j = tokio::try_join! {
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())), task::spawn(task(s.clone())),
task::spawn(task(s.clone())) task::spawn(task(s.clone())),
}; task::spawn(task(s.clone()))
j.unwrap(); };
j.unwrap();
})
}) })
}); });
} }
fn contended_concurrent_single(b: &mut Bencher) { fn contended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = single_rt();
.build()
.unwrap();
let s = Arc::new(Semaphore::new(5)); let s = Arc::new(Semaphore::new(5));
b.iter(|| { g.bench_function("concurrent_single", |b| {
let s = s.clone(); b.iter(|| {
rt.block_on(async move { let s = s.clone();
tokio::join! { rt.block_on(async move {
task(s.clone()), tokio::join! {
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()), task(s.clone()),
task(s.clone()) task(s.clone()),
}; task(s.clone())
};
})
}) })
}); });
} }
bencher::benchmark_group!( fn bench_contention(c: &mut Criterion) {
sync_semaphore, let mut group = c.benchmark_group("contention");
uncontended, contended_concurrent_multi(&mut group);
uncontended_concurrent_multi, contended_concurrent_single(&mut group);
uncontended_concurrent_single, group.finish();
contended_concurrent_multi, }
contended_concurrent_single
);
bencher::benchmark_main!(sync_semaphore); fn bench_uncontented(c: &mut Criterion) {
let mut group = c.benchmark_group("uncontented");
uncontended(&mut group);
uncontended_concurrent_multi(&mut group);
uncontended_concurrent_single(&mut group);
group.finish();
}
criterion_group!(contention, bench_contention);
criterion_group!(uncontented, bench_uncontented);
criterion_main!(contention, uncontented);
+16 -13
View File
@@ -1,9 +1,10 @@
use bencher::{black_box, Bencher};
use rand::prelude::*; use rand::prelude::*;
use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::{watch, Notify}; use tokio::sync::{watch, Notify};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
fn rt() -> tokio::runtime::Runtime { fn rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_multi_thread() tokio::runtime::Builder::new_multi_thread()
.worker_threads(6) .worker_threads(6)
@@ -24,7 +25,7 @@ fn do_work(rng: &mut impl RngCore) -> u32 {
.fold(0, u32::wrapping_add) .fold(0, u32::wrapping_add)
} }
fn contention_resubscribe(b: &mut Bencher) { fn contention_resubscribe(c: &mut Criterion) {
const NTASK: u64 = 1000; const NTASK: u64 = 1000;
let rt = rt(); let rt = rt();
@@ -46,19 +47,21 @@ fn contention_resubscribe(b: &mut Bencher) {
}); });
} }
b.iter(|| { c.bench_function("contention_resubscribe", |b| {
rt.block_on(async { b.iter(|| {
for _ in 0..100 { rt.block_on(async {
assert_eq!(wg.0.fetch_add(NTASK, Ordering::Relaxed), 0); for _ in 0..100 {
let _ = snd.send(black_box(42)); assert_eq!(wg.0.fetch_add(NTASK, Ordering::Relaxed), 0);
while wg.0.load(Ordering::Acquire) > 0 { let _ = snd.send(black_box(42));
wg.1.notified().await; while wg.0.load(Ordering::Acquire) > 0 {
wg.1.notified().await;
}
} }
} });
}); })
}); });
} }
bencher::benchmark_group!(contention, contention_resubscribe); criterion_group!(contention, contention_resubscribe);
bencher::benchmark_main!(contention); criterion_main!(contention);
+10 -11
View File
@@ -2,24 +2,23 @@
//! This essentially measure the time to enqueue a task in the local and remote //! This essentially measure the time to enqueue a task in the local and remote
//! case. //! case.
#[macro_use] use criterion::{black_box, criterion_group, criterion_main, Criterion};
extern crate bencher;
use bencher::{black_box, Bencher}; fn time_now_current_thread(c: &mut Criterion) {
fn time_now_current_thread(bench: &mut Bencher) {
let rt = tokio::runtime::Builder::new_current_thread() let rt = tokio::runtime::Builder::new_current_thread()
.enable_time() .enable_time()
.build() .build()
.unwrap(); .unwrap();
bench.iter(|| { c.bench_function("time_now_current_thread", |b| {
rt.block_on(async { b.iter(|| {
black_box(tokio::time::Instant::now()); rt.block_on(async {
black_box(tokio::time::Instant::now());
})
}) })
}) });
} }
bencher::benchmark_group!(time_now, time_now_current_thread,); criterion_group!(time_now, time_now_current_thread);
bencher::benchmark_main!(time_now); criterion_main!(time_now);