Skip to content

Commit

Permalink
Refactors benchmark
Browse files Browse the repository at this point in the history
- tokio-rs#148 exposed Runtime. It is now possible to use Criterions
async support directly. Do so

- Adds criterion support for flamegraph generation of only the
  benchmarked code (pprof)

- Now crashes on second call to block_on, as the AsyncFd still exists
  Introduced in tokio-rs#142
  • Loading branch information
ollie-etl authored and ollie-etl committed Oct 27, 2022
1 parent e44a2c9 commit cb676c8
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 43 deletions.
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,24 @@ tokio = { version = "1.2", features = ["net", "rt"] }
scoped-tls = "1.0.0"
slab = "0.4.2"
libc = "0.2.80"
io-uring = { version = "0.5.0", features = ["unstable"] }
socket2 = { version = "0.4.4", features = ["all"] }
io-uring = { version = "0.5.0", features = [ "unstable" ] }
socket2 = { version = "0.4.4", features = [ "all"] }
bytes = { version = "1.0", optional = true }

[dev-dependencies]
tempfile = "3.2.0"
tokio-test = "0.4.2"
iai = "0.1.1"
criterion = "0.4.0"
tokio = { version = "1.21.2", features = [] }
futures = "0.3.25"
criterion = {version = "0.3.6", features = ["async"]}
pprof = {version = "0.10.1", features = ["flamegraph", "criterion"]}

[package.metadata.docs.rs]
all-features = true

[profile.bench]
lto = "fat"
codegen = "1"
debug = true

[[bench]]
Expand Down
77 changes: 38 additions & 39 deletions benches/criterion/no_op.rs
Original file line number Diff line number Diff line change
@@ -1,85 +1,84 @@
use criterion::{
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
criterion_group, criterion_main, BenchmarkId, Criterion, Throughput,
};
use std::time::{Duration, Instant};
use pprof::criterion::{Output, PProfProfiler};

use tokio::task::JoinSet;

// To be upstreamed into Criterion, on next release (> 0.3.0)
struct AsyncRuntime(tokio_uring::Runtime);

impl criterion::async_executor::AsyncExecutor for &AsyncRuntime {
fn block_on<T>(&self, future: impl futures::Future<Output = T>) -> T {
self.0.block_on(future)
}
}

#[derive(Clone)]
struct Options {
iterations: usize,
concurrency: usize,
sq_size: usize,
cq_size: usize,
}

impl Default for Options {
fn default() -> Self {
Self {
iterations: 100000,
concurrency: 1,
sq_size: 128,
cq_size: 256,
}
}
}

fn run_no_ops(opts: &Options, count: u64) -> Duration {
let mut ring_opts = tokio_uring::uring_builder();
ring_opts
.setup_cqsize(opts.cq_size as _)
// .setup_sqpoll(10)
// .setup_sqpoll_cpu(1)
;

let mut m = Duration::ZERO;

// Run the required number of iterations
for _ in 0..count {
m += tokio_uring::builder()
.entries(opts.sq_size as _)
.uring_builder(&ring_opts)
.start(async move {
let mut js = JoinSet::new();

for _ in 0..opts.iterations {
js.spawn_local(tokio_uring::no_op());
}

let start = Instant::now();
async fn run_no_ops(opts: &Options) {
let mut js = JoinSet::new();

while let Some(res) = js.join_next().await {
res.unwrap().unwrap();
}
for _ in 0..opts.iterations {
js.spawn_local(tokio_uring::no_op());
}

start.elapsed()
})
while let Some(res) = js.join_next().await {
res.unwrap().unwrap();
}
m
}

fn bench(c: &mut Criterion) {
let mut group = c.benchmark_group("no_op");
let mut opts = Options::default();

let mut ring_opts = tokio_uring::uring_builder();
ring_opts
.setup_cqsize(256)
// .setup_sqpoll(10)
// .setup_sqpoll_cpu(1)
;

let mut builder = tokio_uring::builder();
builder.entries(128).uring_builder(&ring_opts);

let runtime = AsyncRuntime(tokio_uring::Runtime::new(&builder).unwrap());
let runtime = &runtime;

for concurrency in [1, 32, 64, 256].iter() {
opts.concurrency = *concurrency;

// We perform long running benchmarks: this is the best mode
group.sampling_mode(SamplingMode::Flat);

group.throughput(Throughput::Elements(opts.iterations as u64));
group.bench_with_input(
BenchmarkId::from_parameter(concurrency),
&opts,
|b, opts| {
// Custom iterator used because we don't expose access to runtime,
// which is required to do async benchmarking with criterion
b.iter_custom(move |iter| run_no_ops(opts, iter));
b.to_async(runtime).iter(|| run_no_ops(opts));
},
);
}
group.finish();
}

criterion_group!(benches, bench);
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
targets = bench
}
criterion_main!(benches);

0 comments on commit cb676c8

Please sign in to comment.