diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fcc180d4..67fcf5b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,6 +131,20 @@ jobs: command: update args: --package log --precise 0.4.16 + - name: Downgrade regex + uses: actions-rs/cargo@v1 + if: ${{ matrix.rust == '1.63.0' }} + with: + command: update + args: --package regex --precise 1.9.6 + + - name: Downgrade half + uses: actions-rs/cargo@v1 + if: ${{ matrix.rust == '1.63.0' || matrix.rust == '1.69.0' }} + with: + command: update + args: --package half --precise 2.2.1 + - name: Run tests uses: actions-rs/cargo@v1 with: @@ -174,6 +188,20 @@ jobs: toolchain: ${{ matrix.rust }} override: true + - name: Downgrade regex + uses: actions-rs/cargo@v1 + if: ${{ matrix.rust == '1.63.0' }} + with: + command: update + args: --package regex --precise 1.9.6 + + - name: Downgrade half + uses: actions-rs/cargo@v1 + if: ${{ matrix.rust == '1.63.0' }} + with: + command: update + args: --package half --precise 2.2.1 + - name: Run tests uses: actions-rs/cargo@v1 with: diff --git a/Cargo.toml b/Cargo.toml index adec26c5..648bbaab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ nix = { version = "0.29", default-features = false, features = ["signal"], optio [dev-dependencies] futures = "0.3.5" rustix = { version = "0.38", default-features = false, features = ["net"] } +criterion = { version = "0.4" } [features] block_on = ["pin-utils"] @@ -48,3 +49,7 @@ rustdoc-args = ["--cfg", "docsrs"] [[test]] name = "signals" harness = false + +[[bench]] +name = "timer" +harness = false \ No newline at end of file diff --git a/benches/timer.rs b/benches/timer.rs new file mode 100644 index 00000000..144d77de --- /dev/null +++ b/benches/timer.rs @@ -0,0 +1,100 @@ +use std::time::Duration; + +use calloop::timer::TimeoutAction; +use criterion::{criterion_group, criterion_main, Criterion}; + +fn single(c: &mut Criterion) { + let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap(); + let loop_handle = event_loop.handle(); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + let mut timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + c.bench_function("extend_single", |b| { + b.iter(|| { + loop_handle.remove(timeout_token); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap(); + }); + }); +} + +fn mixed(c: &mut Criterion) { + let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap(); + let loop_handle = event_loop.handle(); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10 - 1)); + loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + let mut timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(90 * 10)); + loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + c.bench_function("extend_mixed", |b| { + b.iter(|| { + loop_handle.remove(timeout_token); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap(); + }); + }); +} + +fn mixed_multiple(c: &mut Criterion) { + let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap(); + let loop_handle = event_loop.handle(); + + for _ in 0..1000 { + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10 - 1)); + loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + } + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + let mut timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + for _ in 0..1000 { + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(90 * 10)); + loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + } + + c.bench_function("extend_mixed_many", |b| { + b.iter(|| { + loop_handle.remove(timeout_token); + + let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10)); + timeout_token = loop_handle + .insert_source(timer, |_, _, _| TimeoutAction::Drop) + .unwrap(); + + event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap(); + }); + }); +} + +criterion_group!(benches, single, mixed, mixed_multiple); +criterion_main!(benches); diff --git a/src/sources/timer.rs b/src/sources/timer.rs index 7a4cba85..b6331c9d 100644 --- a/src/sources/timer.rs +++ b/src/sources/timer.rs @@ -229,8 +229,19 @@ impl TimerWheel { } pub(crate) fn cancel(&mut self, counter: u32) { + if self + .heap + .peek() + .map(|data| data.counter == counter) + .unwrap_or(false) + { + self.heap.pop(); + return; + }; + self.heap .iter() + .rev() .find(|data| data.counter == counter) .map(|data| data.token.take()); } @@ -265,6 +276,7 @@ impl TimerWheel { // trait implementations for TimeoutData impl std::cmp::Ord for TimeoutData { + #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { // earlier values have priority self.deadline.cmp(&other.deadline).reverse() @@ -272,6 +284,7 @@ impl std::cmp::Ord for TimeoutData { } impl std::cmp::PartialOrd for TimeoutData { + #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } @@ -281,6 +294,7 @@ impl std::cmp::PartialOrd for TimeoutData { // and the type is private, so ignore its coverage impl std::cmp::PartialEq for TimeoutData { #[cfg_attr(feature = "nightly_coverage", coverage(off))] + #[inline] fn eq(&self, other: &Self) -> bool { self.deadline == other.deadline }