From e884eb4dcf5e678548cdde164b63da82f26db3c6 Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 27 Dec 2020 02:41:30 +0000 Subject: [PATCH] remove old service benchmarks --- .github/workflows/bench.yml | 29 -- actix-service/Cargo.toml | 8 - actix-service/benches/and_then.rs | 334 ------------------ .../benches/unsafecell_vs_refcell.rs | 110 ------ 4 files changed, 481 deletions(-) delete mode 100644 .github/workflows/bench.yml delete mode 100644 actix-service/benches/and_then.rs delete mode 100644 actix-service/benches/unsafecell_vs_refcell.rs diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml deleted file mode 100644 index 7c76e171..00000000 --- a/.github/workflows/bench.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Benchmark (Linux) - -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: - - master - - '1.0' - -jobs: - check_benchmark: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - override: true - - - name: Check benchmark - uses: actions-rs/cargo@v1 - with: - command: bench - args: --package=actix-service diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 1505873b..df3087ef 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -23,11 +23,3 @@ pin-project = "1.0.0" [dev-dependencies] actix-rt = "1.0.0" criterion = "0.3" - -[[bench]] -name = "unsafecell_vs_refcell" -harness = false - -[[bench]] -name = "and_then" -harness = false diff --git a/actix-service/benches/and_then.rs b/actix-service/benches/and_then.rs deleted file mode 100644 index c8aa315d..00000000 --- a/actix-service/benches/and_then.rs +++ /dev/null @@ -1,334 +0,0 @@ -use actix_service::boxed::BoxFuture; -use actix_service::IntoService; -use actix_service::Service; -/// Benchmark various implementations of and_then -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::TryFutureExt; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll}; -use std::{ - cell::{RefCell, UnsafeCell}, - marker::PhantomData, -}; - -/* - * Test services A,B for AndThen service implementations - */ - -async fn svc1(_: ()) -> Result { - Ok(1) -} - -async fn svc2(req: usize) -> Result { - Ok(req + 1) -} - -/* - * AndThenUC - original AndThen service based on UnsafeCell - * Cut down version of actix_service::AndThenService based on actix-service::Cell - */ - -struct AndThenUC(Rc>, PhantomData); - -impl AndThenUC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(UnsafeCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenUC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenUC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponse; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = unsafe { &mut *(*self.0).get() }.0.call(req); - AndThenServiceResponse { - state: State::A(fut, Some(self.0.clone())), - _phantom: PhantomData, - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponse -where - A: Service, - B: Service, -{ - #[pin] - state: State, - _phantom: PhantomData, -} - -#[pin_project::pin_project(project = StateProj)] -enum State -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponse -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(State::Empty(PhantomData)); // drop fut A - let fut = unsafe { &mut (*b.get()).1 }.call(res); - this.state.set(State::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(State::Empty(PhantomData)); - r - }), - StateProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRC - AndThen service based on RefCell - */ - -struct AndThenRC(Rc>, PhantomData); - -impl AndThenRC { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRC { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRC -where - A: Service, - B: Service, -{ - type Response = B::Response; - type Error = A::Error; - type Future = AndThenServiceResponseRC; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - AndThenServiceResponseRC { - state: StateRC::A(fut, Some(self.0.clone())), - } - } -} - -#[pin_project::pin_project] -pub(crate) struct AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - #[pin] - state: StateRC, -} - -#[pin_project::pin_project(project = StateRCProj)] -enum StateRC -where - A: Service, - B: Service, -{ - A(#[pin] A::Future, Option>>), - B(#[pin] B::Future), - Empty(PhantomData), -} - -impl Future for AndThenServiceResponseRC -where - A: Service, - B: Service, -{ - type Output = Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let mut this = self.as_mut().project(); - - match this.state.as_mut().project() { - StateRCProj::A(fut, b) => match fut.poll(cx)? { - Poll::Ready(res) => { - let b = b.take().unwrap(); - this.state.set(StateRC::Empty(PhantomData)); // drop fut A - let fut = b.borrow_mut().1.call(res); - this.state.set(StateRC::B(fut)); - self.poll(cx) - } - Poll::Pending => Poll::Pending, - }, - StateRCProj::B(fut) => fut.poll(cx).map(|r| { - this.state.set(StateRC::Empty(PhantomData)); - r - }), - StateRCProj::Empty(_) => { - panic!("future must not be polled after it returned `Poll::Ready`") - } - } - } -} - -/* - * AndThenRCFuture - AndThen service based on RefCell - * and standard futures::future::and_then combinator in a Box - */ - -struct AndThenRCFuture(Rc>, PhantomData); - -impl AndThenRCFuture { - fn new(a: A, b: B) -> Self - where - A: Service, - B: Service, - { - Self(Rc::new(RefCell::new((a, b))), PhantomData) - } -} - -impl Clone for AndThenRCFuture { - fn clone(&self) -> Self { - Self(self.0.clone(), PhantomData) - } -} - -impl Service for AndThenRCFuture -where - A: Service + 'static, - A::Future: 'static, - B: Service + 'static, - B::Future: 'static, -{ - type Response = B::Response; - type Error = A::Error; - type Future = BoxFuture; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, req: Req) -> Self::Future { - let fut = self.0.borrow_mut().0.call(req); - let core = self.0.clone(); - let fut2 = move |res| (*core).borrow_mut().1.call(res); - Box::pin(fut.and_then(fut2)) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// 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(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenUC::new(svc1.into_service(), svc2.into_service()), - "AndThen with UnsafeCell", - ); - bench_async_service( - &mut criterion, - AndThenRC::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell", - ); - bench_async_service( - &mut criterion, - AndThenRCFuture::new(svc1.into_service(), svc2.into_service()), - "AndThen with RefCell via future::and_then", - ); -} - -criterion_main!(service_benches); diff --git a/actix-service/benches/unsafecell_vs_refcell.rs b/actix-service/benches/unsafecell_vs_refcell.rs deleted file mode 100644 index cdf91233..00000000 --- a/actix-service/benches/unsafecell_vs_refcell.rs +++ /dev/null @@ -1,110 +0,0 @@ -use actix_service::Service; -use criterion::{criterion_main, Criterion}; -use futures_util::future::join_all; -use futures_util::future::{ok, Ready}; -use std::cell::{RefCell, UnsafeCell}; -use std::rc::Rc; -use std::task::{Context, Poll}; - -struct SrvUC(Rc>); - -impl Default for SrvUC { - fn default() -> Self { - Self(Rc::new(UnsafeCell::new(0))) - } -} - -impl Clone for SrvUC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvUC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - unsafe { *(*self.0).get() = *(*self.0).get() + 1 }; - ok(unsafe { *self.0.get() }) - } -} - -struct SrvRC(Rc>); - -impl Default for SrvRC { - fn default() -> Self { - Self(Rc::new(RefCell::new(0))) - } -} - -impl Clone for SrvRC { - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Service<()> for SrvRC { - type Response = usize; - type Error = (); - type Future = Ready>; - - fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - let prev = *self.0.borrow(); - *(*self.0).borrow_mut() = prev + 1; - ok(*self.0.borrow()) - } -} - -/// Criterion Benchmark for async Service -/// Should be used from within criterion group: -/// ```rust,ignore -/// let mut criterion: ::criterion::Criterion<_> = -/// ::criterion::Criterion::default().configure_from_args(); -/// bench_async_service(&mut criterion, ok_service(), "async_service_direct"); -/// ``` -/// -/// 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(c: &mut Criterion, srv: S, name: &str) -where - S: Service<(), Response = usize, Error = ()> + Clone + 'static, -{ - let mut rt = actix_rt::System::new("test"); - - // start benchmark loops - c.bench_function(name, move |b| { - b.iter_custom(|iters| { - let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect(); - // exclude request generation, it appears it takes significant time vs call (3us vs 1us) - let start = std::time::Instant::now(); - // benchmark body - rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await }); - // check that at least first request succeeded - start.elapsed() - }) - }); -} - -pub fn service_benches() { - let mut criterion: ::criterion::Criterion<_> = - ::criterion::Criterion::default().configure_from_args(); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); - bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell"); - bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell"); -} -criterion_main!(service_benches);