mirror of https://github.com/fafhrd91/actix-web
add cloneable service benchmarks
This commit is contained in:
parent
538d028f2e
commit
28d8106cf9
|
@ -125,3 +125,7 @@ harness = false
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "service"
|
name = "service"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "cloneable"
|
||||||
|
harness = false
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
mod experiments;
|
||||||
|
mod service;
|
||||||
|
|
||||||
|
use actix_web::test::ok_service;
|
||||||
|
use criterion::{criterion_main, Criterion};
|
||||||
|
use experiments::cloneable::cloneable;
|
||||||
|
use experiments::cloneable::cloneable_safe;
|
||||||
|
use service::bench_async_service;
|
||||||
|
|
||||||
|
// This is benchmark of effect by replacing UnsafeCell to RefCell in CloneableService
|
||||||
|
// Issue: https://github.com/actix/actix-web/issues/1295
|
||||||
|
//
|
||||||
|
// Note: numbers may vary from run to run +-20%, probably due to async env
|
||||||
|
// async_service_direct time: [1.0076 us 1.0300 us 1.0507 us]
|
||||||
|
// change: [-32.491% -23.295% -15.790%] (p = 0.00 < 0.05)
|
||||||
|
// async_service_cloneable_unsafe
|
||||||
|
// time: [1.0857 us 1.1208 us 1.1629 us]
|
||||||
|
// change: [-2.9318% +5.7660% +15.004%] (p = 0.27 > 0.05)
|
||||||
|
// async_service_cloneable_safe
|
||||||
|
// time: [1.0703 us 1.1002 us 1.1390 us]
|
||||||
|
// change: [-9.2951% -1.1186% +6.5384%] (p = 0.80 > 0.05)
|
||||||
|
|
||||||
|
pub fn service_benches() {
|
||||||
|
let mut criterion: Criterion<_> = Criterion::default().configure_from_args();
|
||||||
|
bench_async_service(&mut criterion, ok_service(), "async_service_direct");
|
||||||
|
bench_async_service(
|
||||||
|
&mut criterion,
|
||||||
|
cloneable::CloneableService::new(ok_service()),
|
||||||
|
"async_service_cloneable_unsafe",
|
||||||
|
);
|
||||||
|
bench_async_service(
|
||||||
|
&mut criterion,
|
||||||
|
cloneable_safe::CloneableService::new(ok_service()),
|
||||||
|
"async_service_cloneable_safe",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
criterion_main!(service_benches);
|
|
@ -0,0 +1,36 @@
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
use actix_service::Service;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Service that allows to turn non-clone service to a service with `Clone` impl
|
||||||
|
pub(crate) struct CloneableService<T: Service>(Rc<UnsafeCell<T>>);
|
||||||
|
|
||||||
|
impl<T: Service> CloneableService<T> {
|
||||||
|
pub(crate) fn new(service: T) -> Self {
|
||||||
|
Self(Rc::new(UnsafeCell::new(service)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Service> Clone for CloneableService<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Service> Service for CloneableService<T> {
|
||||||
|
type Request = T::Request;
|
||||||
|
type Response = T::Response;
|
||||||
|
type Error = T::Error;
|
||||||
|
type Future = T::Future;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
unsafe { &mut *self.0.as_ref().get() }.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: T::Request) -> Self::Future {
|
||||||
|
unsafe { &mut *self.0.as_ref().get() }.call(req)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
|
||||||
|
use actix_service::Service;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
/// Service that allows to turn non-clone service to a service with `Clone` impl
|
||||||
|
pub(crate) struct CloneableService<T: Service>(Rc<RefCell<T>>);
|
||||||
|
|
||||||
|
impl<T: Service> CloneableService<T> {
|
||||||
|
pub(crate) fn new(service: T) -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(service)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Service> Clone for CloneableService<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Service> Service for CloneableService<T> {
|
||||||
|
type Request = T::Request;
|
||||||
|
type Response = T::Response;
|
||||||
|
type Error = T::Error;
|
||||||
|
type Future = T::Future;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.0.borrow_mut().poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: T::Request) -> Self::Future {
|
||||||
|
self.0.borrow_mut().call(req)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod cloneable;
|
||||||
|
pub mod cloneable_safe;
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod cloneable;
|
|
@ -1,22 +1,33 @@
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||||
use actix_web::{test, web, App, Error, HttpResponse};
|
use actix_web::{web, App, Error, HttpResponse};
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_main, Criterion};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::test::{init_service, ok_service, TestRequest};
|
use actix_web::test::{init_service, ok_service, TestRequest};
|
||||||
|
|
||||||
// TOOD: probably convert to macro?
|
/// Criterion Benchmark for async Service
|
||||||
|
/// Should be used from within criterion group:
|
||||||
// Following approach is usable for benching Service wrappers
|
/// ```rust,ignore
|
||||||
// Using minimum service code implementation we first measure
|
/// let mut criterion: ::criterion::Criterion<_> =
|
||||||
// time to run minimum service, then measure time with wrapper.
|
/// ::criterion::Criterion::default().configure_from_args();
|
||||||
// Sample results on MacBook Pro '14
|
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
|
||||||
// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
|
/// ```
|
||||||
fn async_cloneable_wrapper_service(c: &mut Criterion) {
|
///
|
||||||
|
/// Usable for benching Service wrappers:
|
||||||
|
/// Using minimum service code implementation we first measure
|
||||||
|
/// time to run minimum service, then measure time with wrapper.
|
||||||
|
///
|
||||||
|
/// Sample output
|
||||||
|
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
|
||||||
|
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
|
||||||
|
where
|
||||||
|
S: Service<Request = ServiceRequest, Response = ServiceResponse, Error = Error>
|
||||||
|
+ 'static,
|
||||||
|
{
|
||||||
let mut rt = actix_rt::System::new("test");
|
let mut rt = actix_rt::System::new("test");
|
||||||
let srv = Rc::new(RefCell::new(ok_service()));
|
let srv = Rc::new(RefCell::new(srv));
|
||||||
|
|
||||||
let req = TestRequest::default().to_srv_request();
|
let req = TestRequest::default().to_srv_request();
|
||||||
assert!(rt
|
assert!(rt
|
||||||
|
@ -26,7 +37,7 @@ fn async_cloneable_wrapper_service(c: &mut Criterion) {
|
||||||
.is_success());
|
.is_success());
|
||||||
|
|
||||||
// start benchmark loops
|
// start benchmark loops
|
||||||
c.bench_function("async_service_direct", move |b| {
|
c.bench_function(name, move |b| {
|
||||||
b.iter_custom(|iters| {
|
b.iter_custom(|iters| {
|
||||||
let srv = srv.clone();
|
let srv = srv.clone();
|
||||||
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
|
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
|
||||||
|
@ -55,7 +66,7 @@ async fn index(req: ServiceRequest) -> Result<ServiceResponse, Error> {
|
||||||
// this approach is usable for benching WebService, though it adds some time to direct service call:
|
// this approach is usable for benching WebService, though it adds some time to direct service call:
|
||||||
// Sample results on MacBook Pro '14
|
// Sample results on MacBook Pro '14
|
||||||
// time: [2.0724 us 2.1345 us 2.2074 us]
|
// time: [2.0724 us 2.1345 us 2.2074 us]
|
||||||
fn async_cloneable_wrapper_web_service(c: &mut Criterion) {
|
fn async_web_service(c: &mut Criterion) {
|
||||||
let mut rt = actix_rt::System::new("test");
|
let mut rt = actix_rt::System::new("test");
|
||||||
let srv = Rc::new(RefCell::new(rt.block_on(init_service(
|
let srv = Rc::new(RefCell::new(rt.block_on(init_service(
|
||||||
App::new().service(web::service("/").finish(index)),
|
App::new().service(web::service("/").finish(index)),
|
||||||
|
@ -88,9 +99,10 @@ fn async_cloneable_wrapper_web_service(c: &mut Criterion) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(
|
pub fn service_benches() {
|
||||||
service_benches,
|
let mut criterion: ::criterion::Criterion<_> =
|
||||||
async_cloneable_wrapper_service,
|
::criterion::Criterion::default().configure_from_args();
|
||||||
async_cloneable_wrapper_web_service
|
bench_async_service(&mut criterion, ok_service(), "async_service_direct");
|
||||||
);
|
async_web_service(&mut criterion);
|
||||||
|
}
|
||||||
criterion_main!(service_benches);
|
criterion_main!(service_benches);
|
||||||
|
|
Loading…
Reference in New Issue