use bytes::Bytes;
use criterion::{black_box, criterion_group, criterion_main, Criterion};
#[allow(unused_imports)]
use typeway_core::*;
use typeway_macros::*;
use typeway_server::handler::WithBody;
use typeway_server::*;
async fn bare_noop() -> &'static str {
"ok"
}
async fn bare_with_work() -> String {
let mut s = String::with_capacity(64);
for i in 0..10 {
s.push_str(&format!("item-{i},"));
}
s
}
typeway_path!(type UserByIdPath = "users" / u32);
async fn bare_path_extract(path: Path<UserByIdPath>) -> String {
let (id,) = path.0;
format!("user-{id}")
}
#[derive(Clone)]
struct AppState {
name: String,
}
async fn bare_state_extract(state: State<AppState>) -> String {
state.0.name.clone()
}
async fn bare_multi_extract(path: Path<UserByIdPath>, state: State<AppState>) -> String {
let (id,) = path.0;
format!("{}-{id}", state.0.name)
}
#[derive(serde::Deserialize)]
struct CreateBody {
name: String,
}
async fn bare_json_body(body: Json<CreateBody>) -> String {
body.0.name
}
fn mock_parts(path: &str, state: Option<AppState>) -> http::request::Parts {
let (mut parts, _) = http::Request::builder()
.uri(path)
.body(())
.unwrap()
.into_parts();
if let Some(s) = state {
parts.extensions.insert(s);
}
parts
}
fn bench_handler_dispatch(c: &mut Criterion) {
let rt = tokio::runtime::Runtime::new().unwrap();
let mut group = c.benchmark_group("handler_dispatch");
group.bench_function("baseline/noop", |b| {
b.to_async(&rt).iter(|| async {
let res = bare_noop().await;
black_box(res);
});
});
group.bench_function("baseline/with_work", |b| {
b.to_async(&rt).iter(|| async {
let res = bare_with_work().await;
black_box(res);
});
});
let noop_boxed = into_boxed_handler::<_, ()>(bare_noop);
group.bench_function("boxed_handler/noop", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/hello", None);
let bytes = Bytes::new();
let handler = &noop_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
let work_boxed = into_boxed_handler::<_, ()>(bare_with_work);
group.bench_function("boxed_handler/with_work", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/hello", None);
let bytes = Bytes::new();
let handler = &work_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
let path_boxed = into_boxed_handler::<_, (Path<UserByIdPath>,)>(bare_path_extract);
group.bench_function("boxed_handler/path_extract", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/users/42", None);
let bytes = Bytes::new();
let handler = &path_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
let state_boxed = into_boxed_handler::<_, (State<AppState>,)>(bare_state_extract);
let state = AppState {
name: "test".into(),
};
group.bench_function("boxed_handler/state_extract", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/whatever", Some(state.clone()));
let bytes = Bytes::new();
let handler = &state_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
let multi_boxed =
into_boxed_handler::<_, (Path<UserByIdPath>, State<AppState>)>(bare_multi_extract);
group.bench_function("boxed_handler/multi_extract", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/users/42", Some(state.clone()));
let bytes = Bytes::new();
let handler = &multi_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
let json_boxed = into_boxed_handler::<_, WithBody<(), Json<CreateBody>>>(bare_json_body);
let json_body = Bytes::from(r#"{"name":"Alice"}"#);
group.bench_function("boxed_handler/json_body", |b| {
b.to_async(&rt).iter(|| {
let parts = mock_parts("/items", None);
let bytes = json_body.clone();
let handler = &json_boxed;
async move {
let res = handler(parts, bytes).await;
black_box(res);
}
});
});
group.finish();
}
fn bench_body_collection(c: &mut Criterion) {
let _rt = tokio::runtime::Runtime::new().unwrap();
let mut group = c.benchmark_group("body_collection");
for size in [0, 64, 1024, 16384, 65536] {
let data = Bytes::from(vec![b'x'; size]);
group.bench_function(format!("bytes_clone/{size}B"), |b| {
b.iter(|| {
let cloned = data.clone();
black_box(cloned);
});
});
}
group.finish();
}
criterion_group!(benches, bench_handler_dispatch, bench_body_collection);
criterion_main!(benches);