2021-04-01 16:26:13 +02:00
|
|
|
use std::{future::Future, time::Instant};
|
2021-01-08 23:17:19 +01:00
|
|
|
|
2021-12-04 20:40:47 +01:00
|
|
|
use actix_http::body::BoxBody;
|
2021-04-01 16:26:13 +02:00
|
|
|
use actix_utils::future::{ready, Ready};
|
2021-12-04 20:40:47 +01:00
|
|
|
use actix_web::{
|
|
|
|
error, http::StatusCode, test::TestRequest, Error, HttpRequest, HttpResponse, Responder,
|
|
|
|
};
|
2021-01-08 23:17:19 +01:00
|
|
|
use criterion::{criterion_group, criterion_main, Criterion};
|
2021-04-01 16:26:13 +02:00
|
|
|
use futures_util::future::{join_all, Either};
|
2021-01-08 23:17:19 +01:00
|
|
|
|
|
|
|
// responder simulate the old responder trait.
|
|
|
|
trait FutureResponder {
|
|
|
|
type Error;
|
|
|
|
type Future: Future<Output = Result<HttpResponse, Self::Error>>;
|
|
|
|
|
|
|
|
fn future_respond_to(self, req: &HttpRequest) -> Self::Future;
|
|
|
|
}
|
|
|
|
|
|
|
|
// a simple option responder type.
|
|
|
|
struct OptionResponder<T>(Option<T>);
|
|
|
|
|
|
|
|
// a simple wrapper type around string
|
|
|
|
struct StringResponder(String);
|
|
|
|
|
|
|
|
impl FutureResponder for StringResponder {
|
|
|
|
type Error = Error;
|
2021-06-26 16:33:43 +02:00
|
|
|
type Future = Ready<Result<HttpResponse, Self::Error>>;
|
2021-01-08 23:17:19 +01:00
|
|
|
|
|
|
|
fn future_respond_to(self, _: &HttpRequest) -> Self::Future {
|
|
|
|
// this is default builder for string response in both new and old responder trait.
|
2021-06-26 16:33:43 +02:00
|
|
|
ready(Ok(HttpResponse::build(StatusCode::OK)
|
2021-01-08 23:17:19 +01:00
|
|
|
.content_type("text/plain; charset=utf-8")
|
|
|
|
.body(self.0)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> FutureResponder for OptionResponder<T>
|
|
|
|
where
|
|
|
|
T: FutureResponder,
|
2021-06-26 16:33:43 +02:00
|
|
|
T::Future: Future<Output = Result<HttpResponse, Error>>,
|
2021-01-08 23:17:19 +01:00
|
|
|
{
|
|
|
|
type Error = Error;
|
|
|
|
type Future = Either<T::Future, Ready<Result<HttpResponse, Self::Error>>>;
|
|
|
|
|
|
|
|
fn future_respond_to(self, req: &HttpRequest) -> Self::Future {
|
|
|
|
match self.0 {
|
|
|
|
Some(t) => Either::Left(t.future_respond_to(req)),
|
|
|
|
None => Either::Right(ready(Err(error::ErrorInternalServerError("err")))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Responder for StringResponder {
|
2021-12-04 20:40:47 +01:00
|
|
|
type Body = BoxBody;
|
|
|
|
|
|
|
|
fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
|
2021-06-26 16:33:43 +02:00
|
|
|
HttpResponse::build(StatusCode::OK)
|
2021-01-08 23:17:19 +01:00
|
|
|
.content_type("text/plain; charset=utf-8")
|
|
|
|
.body(self.0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Responder> Responder for OptionResponder<T> {
|
2021-12-04 20:40:47 +01:00
|
|
|
type Body = BoxBody;
|
|
|
|
|
|
|
|
fn respond_to(self, req: &HttpRequest) -> HttpResponse<Self::Body> {
|
2021-01-08 23:17:19 +01:00
|
|
|
match self.0 {
|
2021-12-04 20:40:47 +01:00
|
|
|
Some(t) => t.respond_to(req).map_into_boxed_body(),
|
2021-06-26 16:33:43 +02:00
|
|
|
None => HttpResponse::from_error(error::ErrorInternalServerError("err")),
|
2021-01-08 23:17:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn future_responder(c: &mut Criterion) {
|
2021-02-07 02:00:40 +01:00
|
|
|
let rt = actix_rt::System::new();
|
2021-01-08 23:17:19 +01:00
|
|
|
let req = TestRequest::default().to_http_request();
|
|
|
|
|
|
|
|
c.bench_function("future_responder", move |b| {
|
|
|
|
b.iter_custom(|_| {
|
|
|
|
let futs = (0..100_000).map(|_| async {
|
|
|
|
StringResponder(String::from("Hello World!!"))
|
|
|
|
.future_respond_to(&req)
|
|
|
|
.await
|
|
|
|
});
|
|
|
|
|
2021-04-01 16:26:13 +02:00
|
|
|
let futs = join_all(futs);
|
2021-01-08 23:17:19 +01:00
|
|
|
|
|
|
|
let start = Instant::now();
|
|
|
|
|
|
|
|
let _res = rt.block_on(async { futs.await });
|
|
|
|
|
|
|
|
start.elapsed()
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn responder(c: &mut Criterion) {
|
2021-02-07 02:00:40 +01:00
|
|
|
let rt = actix_rt::System::new();
|
2021-01-08 23:17:19 +01:00
|
|
|
let req = TestRequest::default().to_http_request();
|
|
|
|
c.bench_function("responder", move |b| {
|
|
|
|
b.iter_custom(|_| {
|
|
|
|
let responders =
|
|
|
|
(0..100_000).map(|_| StringResponder(String::from("Hello World!!")));
|
|
|
|
|
|
|
|
let start = Instant::now();
|
|
|
|
let _res = rt.block_on(async {
|
|
|
|
// don't need runtime block on but to be fair.
|
|
|
|
responders.map(|r| r.respond_to(&req)).collect::<Vec<_>>()
|
|
|
|
});
|
|
|
|
|
|
|
|
start.elapsed()
|
|
|
|
})
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
criterion_group!(responder_bench, future_responder, responder);
|
|
|
|
criterion_main!(responder_bench);
|