-
Notifications
You must be signed in to change notification settings - Fork 284
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add optional async/await support. #403
- Loading branch information
Showing
11 changed files
with
614 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use criterion::{async_executor::FuturesExecutor, criterion_group, BatchSize, Criterion}; | ||
|
||
fn some_benchmark(c: &mut Criterion) { | ||
let mut group = c.benchmark_group("async overhead"); | ||
group.bench_function("iter", |b| b.to_async(FuturesExecutor).iter(|| async { 1 })); | ||
group.bench_function("iter_with_setup", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_with_setup(|| (), |_| async { 1 }) | ||
}); | ||
group.bench_function("iter_with_large_setup", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_with_large_setup(|| (), |_| async { 1 }) | ||
}); | ||
group.bench_function("iter_with_large_drop", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_with_large_drop(|| async { 1 }) | ||
}); | ||
group.bench_function("iter_batched_small_input", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_batched(|| (), |_| async { 1 }, BatchSize::SmallInput) | ||
}); | ||
group.bench_function("iter_batched_large_input", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_batched(|| (), |_| async { 1 }, BatchSize::LargeInput) | ||
}); | ||
group.bench_function("iter_batched_per_iteration", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_batched(|| (), |_| async { 1 }, BatchSize::PerIteration) | ||
}); | ||
group.bench_function("iter_batched_ref_small_input", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_batched_ref(|| (), |_| async { 1 }, BatchSize::SmallInput) | ||
}); | ||
group.bench_function("iter_batched_ref_large_input", |b| { | ||
b.to_async(FuturesExecutor) | ||
.iter_batched_ref(|| (), |_| async { 1 }, BatchSize::LargeInput) | ||
}); | ||
group.bench_function("iter_batched_ref_per_iteration", |b| { | ||
b.to_async(FuturesExecutor).iter_batched_ref( | ||
|| (), | ||
|_| async { 1 }, | ||
BatchSize::PerIteration, | ||
) | ||
}); | ||
group.finish(); | ||
} | ||
|
||
criterion_group!(benches, some_benchmark); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
## Benchmarking async functions | ||
|
||
As of version 0.3.4, Criterion.rs has optional support for benchmarking async functions. | ||
Benchmarking async functions works just like benchmarking regular functions, except that the | ||
caller must provide a futures executor to run the benchmark in. | ||
|
||
### Example: | ||
|
||
```rust | ||
use criterion::BenchmarkId; | ||
use criterion::Criterion; | ||
use criterion::{criterion_group, criterion_main}; | ||
|
||
// This is a struct that tells Criterion.rs to use the "futures" crate's current-thread executor | ||
use criterion::async_executor::FuturesExecutor; | ||
|
||
// Here we have an async function to benchmark | ||
async fn do_something(size: usize) { | ||
// Do something async with the size | ||
} | ||
|
||
fn from_elem(c: &mut Criterion) { | ||
let size: usize = 1024; | ||
|
||
c.bench_with_input(BenchmarkId::new("input_example", size), &size, |b, &s| { | ||
// Insert a call to `to_async` to convert the bencher to async mode. | ||
// The timing loops are the same as with the normal bencher. | ||
b.to_async(FuturesExecutor).iter(|| do_something(s)); | ||
}); | ||
} | ||
|
||
criterion_group!(benches, from_elem); | ||
criterion_main!(benches); | ||
``` | ||
|
||
As can be seen in the code above, to benchmark async functions we must provide an async runtime to | ||
the bencher to run the benchmark in. The runtime structs are listed in the table below. | ||
|
||
### Enabling Async Benchmarking | ||
|
||
To enable async benchmark support, Criterion.rs must be compiled with one or more of the following | ||
features, depending on which futures executor(s) you want to benchmark on. It is recommended to use | ||
the same executor that you would use in production. If your executor is not listed here, you can | ||
implement the `criterion::async_executor::AsyncExecutor` trait for it to add support, or send a pull | ||
request. | ||
|
||
| Crate | Feature | Executor Struct | | ||
| --------- | ----------------------------- | ----------------------------------------------------- | | ||
| Tokio | "async_tokio" | `tokio::runtime::Runtime`, `&tokio::runtime::Runtime` | | ||
| async-std | "async_std" (note underscore) | `AsyncStdExecutor` | | ||
| Smol | "async_smol" | `SmolExecutor` | | ||
| futures | "async_futures" | `FuturesExecutor` | | ||
| Other | "async" | | | ||
|
||
### Considerations when benchmarking async functions | ||
|
||
Async functions naturally result in more measurement overhead than synchronous functions. It is | ||
recommended to prefer synchronous functions when benchmarking where possible, especially for small | ||
functions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
//! This module defines a trait that can be used to plug in different Futures executors into | ||
//! Criterion.rs' async benchmarking support. | ||
//! | ||
//! Implementations are provided for: | ||
//! * Tokio (implemented directly for tokio::Runtime) | ||
//! * Async-std | ||
//! * Smol | ||
//! * The Futures crate | ||
//! | ||
//! Please note that async benchmarks will have a small amount of measurement overhead relative | ||
//! to synchronous benchmarks. It is recommended to use synchronous benchmarks where possible, to | ||
//! improve measurement accuracy. | ||
|
||
use std::future::Future; | ||
|
||
/// Plugin trait used to allow benchmarking on multiple different async runtimes. | ||
/// | ||
/// Smol, Tokio and Async-std are supported out of the box, as is the current-thread runner from the | ||
/// Futures crate; it is recommended to use whichever runtime you use in production. | ||
pub trait AsyncExecutor { | ||
/// Spawn the given future onto this runtime and block until it's complete, returning the result. | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T; | ||
} | ||
|
||
/// Runs futures on the 'futures' crate's built-in current-thread executor | ||
#[cfg(feature = "async_futures")] | ||
pub struct FuturesExecutor; | ||
#[cfg(feature = "async_futures")] | ||
impl AsyncExecutor for FuturesExecutor { | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T { | ||
futures::executor::block_on(future) | ||
} | ||
} | ||
|
||
/// Runs futures on the 'soml' crate's global executor | ||
#[cfg(feature = "async_smol")] | ||
pub struct SmolExecutor; | ||
#[cfg(feature = "async_smol")] | ||
impl AsyncExecutor for SmolExecutor { | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T { | ||
smol::block_on(future) | ||
} | ||
} | ||
|
||
#[cfg(feature = "async_tokio")] | ||
impl AsyncExecutor for tokio::runtime::Runtime { | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T { | ||
self.block_on(future) | ||
} | ||
} | ||
#[cfg(feature = "async_tokio")] | ||
impl AsyncExecutor for &tokio::runtime::Runtime { | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T { | ||
(*self).block_on(future) | ||
} | ||
} | ||
|
||
/// Runs futures on the 'async-std' crate's global executor | ||
#[cfg(feature = "async_std")] | ||
pub struct AsyncStdExecutor; | ||
#[cfg(feature = "async_std")] | ||
impl AsyncExecutor for AsyncStdExecutor { | ||
fn block_on<T>(&self, future: impl Future<Output = T>) -> T { | ||
async_std::task::block_on(future) | ||
} | ||
} |
Oops, something went wrong.