Skip to content

Commit

Permalink
Add basic benchmarks (Leafwing-Studios#332)
Browse files Browse the repository at this point in the history
* Scaffolding

* Added simple benchmarks

* Use latest version of criterion

* Add benchmark for ActionState::update

* Add benchmarks for which_pressed

* Move app creation outside of the benchmark loop

* Move initialization out of the benchmarks

* Benchmark actionstate creation
  • Loading branch information
alice-i-cecile authored Mar 27, 2023
1 parent 120a3d2 commit 97ab7ed
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ bevy = { version = "0.10", default-features = false, features = [
"x11",
] }
serde_test = "1.0"
criterion = "0.4"

[[bench]]
name = "action_state"
harness = false

[[bench]]
name = "input_map"
harness = false

[lib]
name = "leafwing_input_manager"
Expand Down
70 changes: 70 additions & 0 deletions benches/action_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use criterion::{criterion_group, criterion_main, Criterion};
use leafwing_input_manager::{
action_state::{ActionData, Timing},
buttonlike::ButtonState,
prelude::ActionState,
Actionlike,
};

#[derive(Actionlike, Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestAction {
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
}

fn pressed(action_state: &ActionState<TestAction>) -> bool {
action_state.pressed(TestAction::A)
}

fn just_pressed(action_state: &ActionState<TestAction>) -> bool {
action_state.just_pressed(TestAction::A)
}

fn released(action_state: &ActionState<TestAction>) -> bool {
action_state.released(TestAction::A)
}

fn just_released(action_state: &ActionState<TestAction>) -> bool {
action_state.just_released(TestAction::A)
}

fn update(mut action_state: ActionState<TestAction>, action_data: Vec<ActionData>) {
action_state.update(action_data);
}

fn criterion_benchmark(c: &mut Criterion) {
let action_state = ActionState::<TestAction>::default();

c.bench_function("action_state_default", |b| {
b.iter(|| ActionState::<TestAction>::default())
});
c.bench_function("pressed", |b| b.iter(|| pressed(&action_state)));
c.bench_function("just_pressed", |b| b.iter(|| just_pressed(&action_state)));
c.bench_function("released", |b| b.iter(|| released(&action_state)));
c.bench_function("just_released", |b| b.iter(|| just_released(&action_state)));

let action_data: Vec<ActionData> = TestAction::variants()
.map(|_action| ActionData {
state: ButtonState::JustPressed,
value: 0.0,
axis_pair: None,
timing: Timing::default(),
consumed: false,
})
.collect();

c.bench_function("update", |b| {
b.iter(|| update(action_state.clone(), action_data.clone()))
});
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
91 changes: 91 additions & 0 deletions benches/input_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use bevy::{
input::InputPlugin,
prelude::{App, KeyCode},
};
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use leafwing_input_manager::{
action_state::ActionData,
input_streams::InputStreams,
prelude::{ClashStrategy, InputMap, MockInput},
Actionlike,
};

#[derive(Actionlike, Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TestAction {
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
}

fn construct_input_map_from_iter() -> InputMap<TestAction> {
black_box(InputMap::new([
(KeyCode::A, TestAction::A),
(KeyCode::B, TestAction::B),
(KeyCode::C, TestAction::C),
(KeyCode::D, TestAction::D),
(KeyCode::E, TestAction::E),
(KeyCode::F, TestAction::F),
(KeyCode::G, TestAction::G),
(KeyCode::H, TestAction::H),
(KeyCode::I, TestAction::I),
(KeyCode::J, TestAction::J),
]))
}

fn construct_input_map_from_chained_calls() -> InputMap<TestAction> {
black_box(
InputMap::default()
.insert(KeyCode::A, TestAction::A)
.insert(KeyCode::B, TestAction::B)
.insert(KeyCode::C, TestAction::C)
.insert(KeyCode::D, TestAction::D)
.insert(KeyCode::E, TestAction::E)
.insert(KeyCode::F, TestAction::F)
.insert(KeyCode::G, TestAction::G)
.insert(KeyCode::H, TestAction::H)
.insert(KeyCode::I, TestAction::I)
.insert(KeyCode::J, TestAction::J)
.build(),
)
}

fn which_pressed(input_streams: &InputStreams, clash_strategy: ClashStrategy) -> Vec<ActionData> {
let input_map = construct_input_map_from_iter();
input_map.which_pressed(input_streams, clash_strategy)
}

pub fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("construct_input_map_from_iter", |b| {
b.iter(|| construct_input_map_from_iter())
});
c.bench_function("construct_input_map_from_chained_calls", |b| {
b.iter(|| construct_input_map_from_chained_calls())
});
let mut which_pressed_group = c.benchmark_group("which_pressed");

// Constructing our test app / input stream outside of the timed benchmark
let mut app = App::new();
app.add_plugin(InputPlugin);
app.send_input(KeyCode::A);
app.send_input(KeyCode::B);
app.update();

let input_streams = InputStreams::from_world(&app.world, None);

for clash_strategy in ClashStrategy::variants() {
which_pressed_group.bench_function(format!("{:?}", clash_strategy), |b| {
b.iter(|| which_pressed(&input_streams, *clash_strategy))
});
}
which_pressed_group.finish();
}

criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
9 changes: 9 additions & 0 deletions src/clashing_inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ pub enum ClashStrategy {
UseActionOrder,
}

impl ClashStrategy {
/// Returns the list of all possible clash strategies.
pub fn variants() -> &'static [ClashStrategy] {
use ClashStrategy::*;

&[PressAll, PrioritizeLongest, UseActionOrder]
}
}

impl UserInput {
/// Does `self` clash with `other`?
#[must_use]
Expand Down

0 comments on commit 97ab7ed

Please sign in to comment.