I wanted to run a benchmark using criterion crate against the following code
pub struct Handler {
sender: mpsc::Sender<oneshot::Sender<String>>
}
impl Handler {
pub fn new() -> Self {
let (sender, receiver) = mpsc::channel(8);
tokio::spawn(listener(receiver));
Self { sender }
}
pub async fn process(&self) {
let (tx, rx) = oneshot::channel::<String>();
let _ = self.sender.send(tx).await;
rx.await.expect("something wrong happened");
}
}
pub async fn listener(mut receiver: mpsc::Receiver<oneshot::Sender<String>>) {
while let Some(sender) = receiver.recv().await {
let _ = sender.send("howdy!".to_string());
}
}
Benchmark code
fn bench_oneshot(c: &mut Criterion) {
let mut group = c.benchmark_group("bench_oneshot");
group.sample_size(100);
group.measurement_time(Duration::from_secs(90));
group.bench_function(
"one_shot",
|b| {
b.to_async(Runtime::new().unwrap()).iter(|| async {
let handler = Arc::new(Handler::new());
for _ in 1..100_000 {
let handler = handler.clone();
handler.process().await;
}
});
}
);
}
criterion_group!(benches, bench_oneshot);
criterion_main!(benches);
The benchmark comes back with a performance measure of ~660ms
I now modify the code to not use one shot channel
pub struct Handler {
sender: mpsc::Sender<String>
}
impl Handler {
pub fn new() -> Self {
let (sender, receiver) = mpsc::channel(8);
tokio::spawn(listener(receiver));
Self { sender }
}
pub async fn process(&self, msg: String) {
let _ = self.sender.send(msg).await;
"Ok";
}
}
pub async fn listener(mut receiver: mpsc::Receiver<String>) {
while let Some(msg) = receiver.recv().await {
println!("{:?}", msg);
}
}
Benchmark code
fn bench_oneshot(c: &mut Criterion) {
let mut group = c.benchmark_group("bench_oneshot");
group.sample_size(100);
group.measurement_time(Duration::from_secs(90));
group.bench_function(
"one_shot",
|b| {
b.to_async(Runtime::new().unwrap()).iter(|| async {
let handler = Arc::new(Handler::new());
for _ in 1..100_000 {
let handler = handler.clone();
handler.process("hi!".to_string()).await;
}
});
}
);
}
criterion_group!(benches, bench_oneshot);
criterion_main!(benches);
The benchmark then comes back with a performance measure of ~90ms
What I found is that when using the tokio one shot channel to send back a response, it degrades the performance by 7 fold. I'm not sure if I'm doing the benchmark wrong or if I'm setting up the handler and listener wrong. Could someone guide me as to whether this is expected performance or if my code needs to be revised or if I should be doing the benchmarking differently?