Skip to content

Commit d9ec48c

Browse files
authored
Timer features (#14)
# Main - Timer specific events -- `tick_event` -- `timer_registration` - Added benchmarking (between different timer implementations) # Misc - Update CI/CD - Update version to 0.3.0
1 parent 2600df2 commit d9ec48c

File tree

10 files changed

+373
-53
lines changed

10 files changed

+373
-53
lines changed

.github/workflows/rust.yml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ jobs:
1717
os: [ubuntu-latest, windows-latest, macos-latest]
1818
runs-on: ${{ matrix.os }}
1919
steps:
20-
- uses: actions/checkout@v4
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
- name: Rust Toolchain Setup
23+
uses: dtolnay/[email protected]
2124

2225
- name: Install
2326
if: ${{ matrix.os == 'ubuntu-latest' }}
@@ -26,18 +29,32 @@ jobs:
2629
2730
- name: Test
2831
run: |
32+
rustup component add clippy
2933
cargo clippy
3034
cargo test
31-
35+
cargo test --features tick_event
36+
cargo test --features timer_registration
37+
cargo test --features tick_event,timer_registration
38+
39+
- name: Bench
40+
run: |
41+
cargo bench
42+
cargo bench --features tick_event
43+
cargo bench --features timer_registration
44+
cargo bench --features tick_event,timer_registration
45+
3246
- name: Build
3347
run: |
3448
cargo build
3549
cargo build --release
50+
cargo build --features tick_event
51+
cargo build --features timer_registration
52+
cargo build --features tick_event,timer_registration
3653
3754
- name: Generate Coverage Report
3855
if: ${{ matrix.os == 'ubuntu-latest' }}
3956
run: |
40-
cargo tarpaulin --engine llvm --out xml --output-dir target
57+
cargo tarpaulin --engine llvm --out xml --output-dir target --all-features
4158
4259
- name: Upload coverage reports to Codecov
4360
if: ${{ matrix.os == 'ubuntu-latest' }}

Cargo.toml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "ticked_async_executor"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
authors = ["coder137"]
55
edition = "2021"
66
description = "Local executor that runs woken async tasks when it is ticked"
@@ -9,10 +9,27 @@ repository = "https://github.com/coder137/ticked-async-executor"
99
categories = ["asynchronous", "concurrency", "game-development", "simulation"]
1010
readme = "README.md"
1111

12+
[features]
13+
# Provides a tick event in the form of a `tokio::sync::watch::Receiver<f64>` as per the `delta` provided
14+
# to the `TickedAsyncExecutorTicker::tick` API
15+
# Also provides a timer implementation: `TickedTimerFromTickEvent`
16+
tick_event = []
17+
18+
# Timers can be registered with the `TickedAsyncExecutorTicker` via the `TickedAsyncExecutorSpawner`
19+
# The timers count down with every call to `TickedAsyncExecutorTicker::tick` API as per the delta provided
20+
# Once the timer has elapsed, the corresponding `tokio::sync::oneshot::*` channel is notified
21+
# Also provides a timer implementation: `TickedTimerFromTimerRegistration`
22+
timer_registration = []
23+
1224
[dependencies]
1325
async-task = "4.7"
1426
pin-project = "1"
1527
tokio = { version = "1", default-features = false, features = ["sync"] }
1628

1729
[dev-dependencies]
1830
tokio = { version = "1", features = ["full"] }
31+
criterion = "0.5"
32+
33+
[[bench]]
34+
name = "benchmark"
35+
harness = false

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Async Local Executor which executes woken tasks only when it is ticked
44

5+
MSRV: 1.87
6+
57
# Usage
68

79
## Default Local Executor
@@ -11,7 +13,7 @@ use ticked_async_executor::*;
1113

1214
const DELTA: f64 = 1000.0 / 60.0;
1315

14-
let executor = TickedAsyncExecutor::default();
16+
let mut executor = TickedAsyncExecutor::default();
1517

1618
executor.spawn_local("MyIdentifier", async move {}).detach();
1719

@@ -28,7 +30,7 @@ use ticked_async_executor::*;
2830

2931
const DELTA: f64 = 1000.0 / 60.0;
3032

31-
let (spawner, ticker) = SplitTickedAsyncExecutor::default();
33+
let (spawner, mut ticker) = SplitTickedAsyncExecutor::default();
3234

3335
spawner.spawn_local("MyIdentifier", async move {}).detach();
3436

@@ -45,7 +47,7 @@ use ticked_async_executor::*;
4547

4648
const DELTA: f64 = 1000.0 / 60.0;
4749

48-
let executor = TickedAsyncExecutor::default();
50+
let mut executor = TickedAsyncExecutor::default();
4951

5052
executor.spawn_local("MyIdentifier1", async move {}).detach();
5153
executor.spawn_local("MyIdentifier2", async move {}).detach();

benches/benchmark.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use criterion::{criterion_group, criterion_main, Criterion};
2+
3+
use ticked_async_executor::TickedAsyncExecutor;
4+
5+
fn ticked_async_executor_benchmark(c: &mut Criterion) {
6+
spawn_tasks_benchmark(c);
7+
8+
#[cfg(feature = "tick_event")]
9+
timer_from_tick_event_benchmark(c);
10+
11+
#[cfg(feature = "timer_registration")]
12+
timer_from_timer_registration_benchmark(c);
13+
}
14+
15+
fn spawn_tasks_benchmark(c: &mut Criterion) {
16+
c.bench_function("Spawn 1 task", |b| {
17+
b.iter_with_large_drop(|| {
18+
let mut executor = TickedAsyncExecutor::default();
19+
executor.spawn_local("empty", async move {}).detach();
20+
executor.tick(0.1, None);
21+
assert_eq!(executor.num_tasks(), 0);
22+
});
23+
});
24+
25+
c.bench_function("Spawn 2 tasks", |b| {
26+
b.iter_with_large_drop(|| {
27+
let mut executor = TickedAsyncExecutor::default();
28+
executor.spawn_local("empty1", async move {}).detach();
29+
executor.spawn_local("empty2", async move {}).detach();
30+
31+
executor.tick(0.1, None);
32+
assert_eq!(executor.num_tasks(), 0);
33+
});
34+
});
35+
36+
c.bench_function("Spawn 100 tasks", |b| {
37+
b.iter_with_large_drop(|| {
38+
let mut executor = TickedAsyncExecutor::default();
39+
for _ in 0..100 {
40+
executor.spawn_local("_", async move {}).detach();
41+
}
42+
43+
executor.tick(0.1, None);
44+
assert_eq!(executor.num_tasks(), 0);
45+
});
46+
});
47+
48+
c.bench_function("Spawn 1000 tasks", |b| {
49+
b.iter_with_large_drop(|| {
50+
let mut executor = TickedAsyncExecutor::default();
51+
for _ in 0..1000 {
52+
executor.spawn_local("_", async move {}).detach();
53+
}
54+
55+
executor.tick(0.1, None);
56+
assert_eq!(executor.num_tasks(), 0);
57+
});
58+
});
59+
60+
c.bench_function("Spawn 10000 tasks", |b| {
61+
b.iter_with_large_drop(|| {
62+
let mut executor = TickedAsyncExecutor::default();
63+
for _ in 0..10000 {
64+
executor.spawn_local("_", async move {}).detach();
65+
}
66+
67+
executor.tick(0.1, None);
68+
assert_eq!(executor.num_tasks(), 0);
69+
});
70+
});
71+
}
72+
73+
#[cfg(feature = "tick_event")]
74+
fn timer_from_tick_event_benchmark(c: &mut Criterion) {
75+
c.bench_function("Spawn 1 timer from tick event", |b| {
76+
b.iter_with_large_drop(|| {
77+
let mut executor = TickedAsyncExecutor::default();
78+
79+
let timer = executor.create_timer_from_tick_event();
80+
executor
81+
.spawn_local("empty", async move {
82+
timer.sleep_for(1.0).await;
83+
})
84+
.detach();
85+
86+
executor.wait_till_completed(0.1);
87+
assert_eq!(executor.num_tasks(), 0);
88+
});
89+
});
90+
91+
c.bench_function("Spawn 1000 timers from tick event", |b| {
92+
b.iter_with_large_drop(|| {
93+
let mut executor = TickedAsyncExecutor::default();
94+
95+
for _ in 0..1000 {
96+
let timer = executor.create_timer_from_tick_event();
97+
executor
98+
.spawn_local("empty", async move {
99+
timer.sleep_for(1.0).await;
100+
})
101+
.detach();
102+
}
103+
104+
executor.wait_till_completed(0.1);
105+
assert_eq!(executor.num_tasks(), 0);
106+
});
107+
});
108+
}
109+
110+
#[cfg(feature = "timer_registration")]
111+
fn timer_from_timer_registration_benchmark(c: &mut Criterion) {
112+
c.bench_function("Spawn 1 timer from timer registration", |b| {
113+
b.iter_with_large_drop(|| {
114+
let mut executor = TickedAsyncExecutor::default();
115+
116+
let timer = executor.create_timer_from_timer_registration();
117+
executor
118+
.spawn_local("empty", async move {
119+
timer.sleep_for(1.0).await;
120+
})
121+
.detach();
122+
123+
executor.wait_till_completed(0.1);
124+
assert_eq!(executor.num_tasks(), 0);
125+
});
126+
});
127+
128+
c.bench_function("Spawn 1000 timers from timer registration", |b| {
129+
b.iter_with_large_drop(|| {
130+
let mut executor = TickedAsyncExecutor::default();
131+
132+
for _ in 0..1000 {
133+
let timer = executor.create_timer_from_timer_registration();
134+
executor
135+
.spawn_local("empty", async move {
136+
timer.sleep_for(1.0).await;
137+
})
138+
.detach();
139+
}
140+
141+
executor.wait_till_completed(0.1);
142+
assert_eq!(executor.num_tasks(), 0);
143+
});
144+
});
145+
}
146+
147+
criterion_group!(benches, ticked_async_executor_benchmark);
148+
criterion_main!(benches);

src/lib.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,12 @@ pub use split_ticked_async_executor::*;
1212
mod ticked_async_executor;
1313
pub use ticked_async_executor::*;
1414

15-
mod ticked_timer;
16-
pub use ticked_timer::*;
15+
#[cfg(feature = "tick_event")]
16+
mod ticked_timer_from_tick_event;
17+
#[cfg(feature = "tick_event")]
18+
pub use ticked_timer_from_tick_event::*;
19+
20+
#[cfg(feature = "timer_registration")]
21+
mod ticked_timer_from_timer_registration;
22+
#[cfg(feature = "timer_registration")]
23+
pub use ticked_timer_from_timer_registration::*;

0 commit comments

Comments
 (0)