Skip to content

Commit b87debd

Browse files
authored
feat: Optimize for timer timeout extension
in case we constantly extend the same timer the head might already point to the timer we want to cancel. in this case we can directly remove the timer to reduce building up the heap unnecessarily.
1 parent 9558927 commit b87debd

File tree

4 files changed

+147
-0
lines changed

4 files changed

+147
-0
lines changed

.github/workflows/ci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ jobs:
131131
command: update
132132
args: --package log --precise 0.4.16
133133

134+
- name: Downgrade regex
135+
uses: actions-rs/cargo@v1
136+
if: ${{ matrix.rust == '1.63.0' }}
137+
with:
138+
command: update
139+
args: --package regex --precise 1.9.6
140+
141+
- name: Downgrade half
142+
uses: actions-rs/cargo@v1
143+
if: ${{ matrix.rust == '1.63.0' || matrix.rust == '1.69.0' }}
144+
with:
145+
command: update
146+
args: --package half --precise 2.2.1
147+
134148
- name: Run tests
135149
uses: actions-rs/cargo@v1
136150
with:
@@ -174,6 +188,20 @@ jobs:
174188
toolchain: ${{ matrix.rust }}
175189
override: true
176190

191+
- name: Downgrade regex
192+
uses: actions-rs/cargo@v1
193+
if: ${{ matrix.rust == '1.63.0' }}
194+
with:
195+
command: update
196+
args: --package regex --precise 1.9.6
197+
198+
- name: Downgrade half
199+
uses: actions-rs/cargo@v1
200+
if: ${{ matrix.rust == '1.63.0' }}
201+
with:
202+
command: update
203+
args: --package half --precise 2.2.1
204+
177205
- name: Run tests
178206
uses: actions-rs/cargo@v1
179207
with:

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ nix = { version = "0.29", default-features = false, features = ["signal"], optio
3434
[dev-dependencies]
3535
futures = "0.3.5"
3636
rustix = { version = "0.38", default-features = false, features = ["net"] }
37+
criterion = { version = "0.4" }
3738

3839
[features]
3940
block_on = ["pin-utils"]
@@ -48,3 +49,7 @@ rustdoc-args = ["--cfg", "docsrs"]
4849
[[test]]
4950
name = "signals"
5051
harness = false
52+
53+
[[bench]]
54+
name = "timer"
55+
harness = false

benches/timer.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use std::time::Duration;
2+
3+
use calloop::timer::TimeoutAction;
4+
use criterion::{criterion_group, criterion_main, Criterion};
5+
6+
fn single(c: &mut Criterion) {
7+
let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap();
8+
let loop_handle = event_loop.handle();
9+
10+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
11+
let mut timeout_token = loop_handle
12+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
13+
.unwrap();
14+
15+
c.bench_function("extend_single", |b| {
16+
b.iter(|| {
17+
loop_handle.remove(timeout_token);
18+
19+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
20+
timeout_token = loop_handle
21+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
22+
.unwrap();
23+
24+
event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap();
25+
});
26+
});
27+
}
28+
29+
fn mixed(c: &mut Criterion) {
30+
let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap();
31+
let loop_handle = event_loop.handle();
32+
33+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10 - 1));
34+
loop_handle
35+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
36+
.unwrap();
37+
38+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
39+
let mut timeout_token = loop_handle
40+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
41+
.unwrap();
42+
43+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(90 * 10));
44+
loop_handle
45+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
46+
.unwrap();
47+
48+
c.bench_function("extend_mixed", |b| {
49+
b.iter(|| {
50+
loop_handle.remove(timeout_token);
51+
52+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
53+
timeout_token = loop_handle
54+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
55+
.unwrap();
56+
57+
event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap();
58+
});
59+
});
60+
}
61+
62+
fn mixed_multiple(c: &mut Criterion) {
63+
let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap();
64+
let loop_handle = event_loop.handle();
65+
66+
for _ in 0..1000 {
67+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10 - 1));
68+
loop_handle
69+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
70+
.unwrap();
71+
}
72+
73+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
74+
let mut timeout_token = loop_handle
75+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
76+
.unwrap();
77+
78+
for _ in 0..1000 {
79+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(90 * 10));
80+
loop_handle
81+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
82+
.unwrap();
83+
}
84+
85+
c.bench_function("extend_mixed_many", |b| {
86+
b.iter(|| {
87+
loop_handle.remove(timeout_token);
88+
89+
let timer = calloop::timer::Timer::from_duration(Duration::from_secs(60 * 10));
90+
timeout_token = loop_handle
91+
.insert_source(timer, |_, _, _| TimeoutAction::Drop)
92+
.unwrap();
93+
94+
event_loop.dispatch(Some(Duration::ZERO), &mut ()).unwrap();
95+
});
96+
});
97+
}
98+
99+
criterion_group!(benches, single, mixed, mixed_multiple);
100+
criterion_main!(benches);

src/sources/timer.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,19 @@ impl TimerWheel {
229229
}
230230

231231
pub(crate) fn cancel(&mut self, counter: u32) {
232+
if self
233+
.heap
234+
.peek()
235+
.map(|data| data.counter == counter)
236+
.unwrap_or(false)
237+
{
238+
self.heap.pop();
239+
return;
240+
};
241+
232242
self.heap
233243
.iter()
244+
.rev()
234245
.find(|data| data.counter == counter)
235246
.map(|data| data.token.take());
236247
}
@@ -265,13 +276,15 @@ impl TimerWheel {
265276
// trait implementations for TimeoutData
266277

267278
impl std::cmp::Ord for TimeoutData {
279+
#[inline]
268280
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
269281
// earlier values have priority
270282
self.deadline.cmp(&other.deadline).reverse()
271283
}
272284
}
273285

274286
impl std::cmp::PartialOrd for TimeoutData {
287+
#[inline]
275288
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
276289
Some(self.cmp(other))
277290
}
@@ -281,6 +294,7 @@ impl std::cmp::PartialOrd for TimeoutData {
281294
// and the type is private, so ignore its coverage
282295
impl std::cmp::PartialEq for TimeoutData {
283296
#[cfg_attr(feature = "nightly_coverage", coverage(off))]
297+
#[inline]
284298
fn eq(&self, other: &Self) -> bool {
285299
self.deadline == other.deadline
286300
}

0 commit comments

Comments
 (0)