Skip to content

Commit c6b884e

Browse files
ollie-etlollie-etl
andauthored
chore: add benchmarks for no-op submission (#144)
Co-authored-by: ollie-etl <Oliver Bunting@etlsystems.com>
1 parent 13f7409 commit c6b884e

4 files changed

Lines changed: 183 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ jobs:
2727
steps:
2828
- run: exit 0
2929

30+
bench:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v3
34+
- name: Install Rust
35+
run: rustup update stable
36+
- run: cargo bench --no-run
37+
3038
check:
3139
runs-on: ubuntu-latest
3240
steps:
@@ -59,7 +67,6 @@ jobs:
5967
run: rustup update stable
6068
- run: cargo test --doc
6169

62-
6370
fmt:
6471
runs-on: ubuntu-latest
6572
steps:

Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ bytes = { version = "1.0", optional = true }
2828
[dev-dependencies]
2929
tempfile = "3.2.0"
3030
tokio-test = "0.4.2"
31+
iai = "0.1.1"
32+
futures = "0.3.25"
33+
criterion = "0.4.0"
3134

3235
[package.metadata.docs.rs]
3336
all-features = true
37+
38+
[[bench]]
39+
name = "lai_no_op"
40+
path = "benches/lai/no_op.rs"
41+
harness = false
42+
43+
[[bench]]
44+
name = "criterion_no_op"
45+
path = "benches/criterion/no_op.rs"
46+
harness = false

benches/criterion/no_op.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use criterion::{
2+
criterion_group, criterion_main, BenchmarkId, Criterion, SamplingMode, Throughput,
3+
};
4+
use std::time::{Duration, Instant};
5+
6+
use futures::stream::{self, StreamExt};
7+
8+
#[derive(Clone)]
9+
struct Options {
10+
iterations: usize,
11+
concurrency: usize,
12+
sq_size: usize,
13+
cq_size: usize,
14+
}
15+
16+
impl Default for Options {
17+
fn default() -> Self {
18+
Self {
19+
iterations: 100000,
20+
concurrency: 1,
21+
sq_size: 128,
22+
cq_size: 256,
23+
}
24+
}
25+
}
26+
27+
fn run_no_ops(opts: &Options, count: u64) -> Duration {
28+
let mut ring_opts = tokio_uring::uring_builder();
29+
ring_opts
30+
.setup_cqsize(opts.cq_size as _)
31+
// .setup_sqpoll(10)
32+
// .setup_sqpoll_cpu(1)
33+
;
34+
35+
let mut m = Duration::ZERO;
36+
37+
// Run the required number of iterations
38+
for _ in 0..count {
39+
m += tokio_uring::builder()
40+
.entries(opts.sq_size as _)
41+
.uring_builder(&ring_opts)
42+
.start(async move {
43+
let start = Instant::now();
44+
stream::iter(0..opts.iterations)
45+
.for_each_concurrent(Some(opts.concurrency), |_| async move {
46+
tokio_uring::no_op().await.unwrap();
47+
})
48+
.await;
49+
start.elapsed()
50+
})
51+
}
52+
m
53+
}
54+
55+
fn bench(c: &mut Criterion) {
56+
let mut group = c.benchmark_group("no_op");
57+
let mut opts = Options::default();
58+
for concurrency in [1, 32, 64, 256].iter() {
59+
opts.concurrency = *concurrency;
60+
61+
// We perform long running benchmarks: this is the best mode
62+
group.sampling_mode(SamplingMode::Flat);
63+
64+
group.throughput(Throughput::Elements(opts.iterations as u64));
65+
group.bench_with_input(
66+
BenchmarkId::from_parameter(concurrency),
67+
&opts,
68+
|b, opts| {
69+
// Custom iterator used because we don't expose access to runtime,
70+
// which is required to do async benchmarking with criterion
71+
b.iter_custom(move |iter| run_no_ops(opts, iter));
72+
},
73+
);
74+
}
75+
group.finish();
76+
}
77+
78+
criterion_group!(benches, bench);
79+
criterion_main!(benches);

benches/lai/no_op.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use futures::stream::{self, StreamExt};
2+
use iai::black_box;
3+
4+
#[derive(Clone)]
5+
struct Options {
6+
iterations: usize,
7+
concurrency: usize,
8+
sq_size: usize,
9+
cq_size: usize,
10+
}
11+
12+
impl Default for Options {
13+
fn default() -> Self {
14+
Self {
15+
iterations: 100000,
16+
concurrency: 1,
17+
sq_size: 64,
18+
cq_size: 256,
19+
}
20+
}
21+
}
22+
23+
fn runtime_only() -> Result<(), Box<dyn std::error::Error>> {
24+
let opts = Options::default();
25+
let mut ring_opts = tokio_uring::uring_builder();
26+
ring_opts
27+
.setup_cqsize(opts.cq_size as _)
28+
// .setup_sqpoll(10)
29+
// .setup_sqpoll_cpu(1)
30+
;
31+
32+
tokio_uring::builder()
33+
.entries(opts.sq_size as _)
34+
.uring_builder(&ring_opts)
35+
.start(async move { black_box(Ok(())) })
36+
}
37+
38+
fn run_no_ops(opts: Options) -> Result<(), Box<dyn std::error::Error>> {
39+
let mut ring_opts = tokio_uring::uring_builder();
40+
ring_opts
41+
.setup_cqsize(opts.cq_size as _)
42+
// .setup_sqpoll(10)
43+
// .setup_sqpoll_cpu(1)
44+
;
45+
46+
tokio_uring::builder()
47+
.entries(opts.sq_size as _)
48+
.uring_builder(&ring_opts)
49+
.start(async move {
50+
stream::iter(0..opts.iterations)
51+
.for_each_concurrent(Some(opts.concurrency), |_| async move {
52+
tokio_uring::no_op().await.unwrap();
53+
})
54+
.await;
55+
Ok(())
56+
})
57+
}
58+
59+
// This provides a baseline for estimating op overhead on top of this
60+
fn no_op_x1() -> Result<(), Box<dyn std::error::Error>> {
61+
let opts = Options::default();
62+
run_no_ops(black_box(opts))
63+
}
64+
65+
fn no_op_x32() -> Result<(), Box<dyn std::error::Error>> {
66+
let mut opts = Options::default();
67+
opts.concurrency = 32;
68+
run_no_ops(black_box(opts))
69+
}
70+
71+
fn no_op_x64() -> Result<(), Box<dyn std::error::Error>> {
72+
let mut opts = Options::default();
73+
opts.concurrency = 64;
74+
run_no_ops(black_box(opts))
75+
}
76+
77+
fn no_op_x256() -> Result<(), Box<dyn std::error::Error>> {
78+
let mut opts = Options::default();
79+
opts.concurrency = 256;
80+
run_no_ops(black_box(opts))
81+
}
82+
83+
iai::main!(runtime_only, no_op_x1, no_op_x32, no_op_x64, no_op_x256);

0 commit comments

Comments
 (0)