-
-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added initial benchmarks #551
Changes from 12 commits
6e61318
89186b6
35c6630
f3a3d79
08df777
c0be019
64e2e1e
52b3a35
82aed44
aecf7ea
dd19b7e
775ab1f
4e6b753
2cda622
2e7266f
74315eb
3bb2b1a
7bf425b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[package] | ||
name = "benches_common" | ||
version = "0.1.0" | ||
license = "Apache-2.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
bevy = { version = "0.14.0-rc.3", default-features = false } | ||
bevy_rapier3d = { version = "0.27.0-rc.1", path = "../bevy_rapier3d", default-features = false } | ||
divan = "0.1" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
use bevy::{ | ||
app::PluginsState, | ||
prelude::*, | ||
render::{ | ||
settings::{RenderCreation, WgpuSettings}, | ||
RenderPlugin, | ||
}, | ||
scene::ScenePlugin, | ||
time::TimeUpdateStrategy, | ||
}; | ||
use bevy_rapier3d::prelude::*; | ||
|
||
pub fn default_app() -> App { | ||
let mut app = App::new(); | ||
|
||
app.add_plugins(( | ||
WindowPlugin::default(), | ||
MinimalPlugins, | ||
AssetPlugin::default(), | ||
ScenePlugin, | ||
RenderPlugin { | ||
render_creation: RenderCreation::Automatic(WgpuSettings { | ||
backends: None, | ||
..Default::default() | ||
}), | ||
..Default::default() | ||
}, | ||
ImagePlugin::default(), | ||
HierarchyPlugin, | ||
TransformPlugin, | ||
RapierPhysicsPlugin::<()>::default(), | ||
)); | ||
|
||
// 60 physics | ||
app.insert_resource(TimeUpdateStrategy::ManualDuration( | ||
std::time::Duration::from_secs_f32(1f32 / 60f32), | ||
)); | ||
app | ||
} | ||
|
||
pub fn wait_app_start(app: &mut App) { | ||
while app.plugins_state() != PluginsState::Ready { | ||
bevy::tasks::tick_global_task_pools_on_main_thread(); | ||
} | ||
|
||
app.finish(); | ||
app.cleanup(); | ||
} | ||
|
||
pub fn bench_app(bencher: divan::Bencher, steps: u32, setup: impl Fn(&mut App)) { | ||
bencher | ||
.with_inputs(|| { | ||
let mut app = default_app(); | ||
setup(&mut app); | ||
wait_app_start(&mut app); | ||
app | ||
}) | ||
.bench_local_values(|mut app| { | ||
for _ in 0..steps { | ||
app.update(); | ||
} | ||
Vrixyz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
//! Translated from avian benchmark. | ||
Vrixyz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
use benches_common::bench_app; | ||
use bevy::prelude::*; | ||
use bevy_rapier3d::math::*; | ||
use bevy_rapier3d::prelude::*; | ||
|
||
fn setup_cubes(app: &mut App, size: u32) { | ||
app.add_systems(Startup, move |mut commands: Commands| { | ||
commands.spawn(( | ||
RigidBody::Fixed, | ||
Transform::from_translation(-2.0 * Vect::Z), | ||
Collider::cuboid(100.0, 1.0, 100.0), | ||
)); | ||
for x in 0..size { | ||
for z in 0..size { | ||
commands.spawn(( | ||
RigidBody::Dynamic, | ||
Transform::from_translation(Vec3::new(x as f32, 2.0, z as f32)), | ||
Collider::cuboid(1.0, 1.0, 1.0), | ||
)); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
#[divan::bench] | ||
fn cubes_3x3_30_steps(bencher: divan::Bencher) { | ||
bench_app(bencher, 30, |app| setup_cubes(app, 3)) | ||
} | ||
#[divan::bench] | ||
fn cubes_5x5_30_steps(bencher: divan::Bencher) { | ||
bench_app(bencher, 30, |app| setup_cubes(app, 5)) | ||
} | ||
#[divan::bench] | ||
fn cubes_10x10_30_steps(bencher: divan::Bencher) { | ||
bench_app(bencher, 30, |app| setup_cubes(app, 10)) | ||
} | ||
|
||
fn main() { | ||
// Run registered benchmarks. | ||
divan::main(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
//! Translated from rapier benchmark. | ||
Vrixyz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
use benches_common::bench_app; | ||
use bevy::prelude::*; | ||
use bevy_rapier3d::math::*; | ||
use bevy_rapier3d::prelude::*; | ||
|
||
pub fn create_pyramid(commands: &mut Commands, offset: Vect, stack_height: usize, rad: f32) { | ||
let shift = rad * 2.0; | ||
|
||
for i in 0usize..stack_height { | ||
for j in i..stack_height { | ||
let fj = j as f32; | ||
let fi = i as f32; | ||
let x = (fi * shift / 2.0) + (fj - fi) * shift; | ||
let y = fi * shift; | ||
|
||
// Build the rigid body. | ||
commands.spawn(( | ||
RigidBody::Dynamic, | ||
Transform::from_translation(Vec3::new(x, y, 0.0) + offset), | ||
Collider::cuboid(1.0, 1.0, 1.0), | ||
)); | ||
} | ||
} | ||
} | ||
|
||
pub fn setup_cubes(app: &mut App, pyramid_count: usize, stack_height: usize) { | ||
app.add_systems(Startup, move |mut commands: Commands| { | ||
let rad = 0.5; | ||
let spacing = 4.0; | ||
|
||
/* | ||
* Ground | ||
*/ | ||
let ground_size = 50.0; | ||
let ground_height = 0.1; | ||
|
||
commands.spawn(( | ||
RigidBody::Fixed, | ||
Transform::from_translation(Vect::new(0.0, -ground_height, 0.0)), | ||
Collider::cuboid( | ||
ground_size, | ||
ground_height, | ||
pyramid_count as f32 * spacing / 2.0 + ground_size, | ||
), | ||
)); | ||
|
||
/* | ||
* Create the cubes | ||
*/ | ||
for pyramid_index in 0..pyramid_count { | ||
let bottomy = rad; | ||
create_pyramid( | ||
&mut commands, | ||
Vect::new( | ||
0.0, | ||
bottomy, | ||
(pyramid_index as f32 - pyramid_count as f32 / 2.0) * spacing, | ||
), | ||
stack_height, | ||
rad, | ||
); | ||
} | ||
}); | ||
} | ||
|
||
#[divan::bench(sample_count = 5, sample_size = 5)] | ||
fn pyramid_1_with_height_2(bencher: divan::Bencher) { | ||
bench_app(bencher, 1000, |app| setup_cubes(app, 1, 2)); | ||
} | ||
|
||
#[divan::bench(sample_count = 2, sample_size = 2)] | ||
fn pyramid_1_with_height_20(bencher: divan::Bencher) { | ||
bench_app(bencher, 100, |app| setup_cubes(app, 1, 20)); | ||
} | ||
|
||
#[divan::bench(sample_count = 1, sample_size = 1)] | ||
fn pyramid_2_with_height_20(bencher: divan::Bencher) { | ||
bench_app(bencher, 100, |app| setup_cubes(app, 2, 20)); | ||
} | ||
|
||
fn main() { | ||
// Run registered benchmarks. | ||
divan::main(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
[package] | ||
name = "custom_benches" | ||
version = "0.1.0" | ||
description = "Custom benchmarks for bevy_rapier." | ||
readme = "./README.md" | ||
license = "Apache-2.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
rapier3d = { features = ["profiler"], version = "0.21" } | ||
bevy_rapier3d = { version = "0.27.0-rc.1", path = "../bevy_rapier3d" } | ||
bevy = { version = "0.14.0-rc.3", default-features = false } | ||
benches_common = { version = "0.1", path = "../benches_common" } |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. side note: I imagine we could use the bench approach (with our own custom bench harness) for better control over custom benchmarks, i.e. run specific custom benches, but I'd need to research that a bit, that can probably be a follow up. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
|
||
## cargo bench | ||
|
||
```sh | ||
cargo bench -p bevy_rapier3d | ||
``` | ||
|
||
## Custom benches | ||
|
||
```sh | ||
cargo run --release -p custom_benches | ||
``` | ||
|
||
## Flamegraph | ||
|
||
```sh | ||
CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph | ||
``` |
Vrixyz marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
//! Translated from rapier benchmark. | ||
//! | ||
use benches_common::default_app; | ||
use benches_common::wait_app_start; | ||
use bevy::prelude::*; | ||
use bevy_rapier3d::dynamics::RigidBody; | ||
use bevy_rapier3d::geometry::Collider; | ||
use bevy_rapier3d::math::Vect; | ||
use bevy_rapier3d::plugin::RapierContext; | ||
|
||
pub fn create_pyramid(commands: &mut Commands, offset: Vect, stack_height: usize, rad: f32) { | ||
let shift = rad * 2.0; | ||
|
||
for i in 0usize..stack_height { | ||
for j in i..stack_height { | ||
let fj = j as f32; | ||
let fi = i as f32; | ||
let x = (fi * shift / 2.0) + (fj - fi) * shift; | ||
let y = fi * shift; | ||
|
||
// Build the rigid body. | ||
commands.spawn(( | ||
RigidBody::Dynamic, | ||
Transform::from_translation(Vec3::new(x, y, 0.0) + offset), | ||
Collider::cuboid(1.0, 1.0, 1.0), | ||
)); | ||
} | ||
} | ||
} | ||
|
||
pub fn setup_cubes(app: &mut App, pyramid_count: usize, stack_height: usize) { | ||
app.add_systems(Startup, move |mut commands: Commands| { | ||
let rad = 0.5; | ||
let spacing = 4.0; | ||
|
||
/* | ||
* Ground | ||
*/ | ||
let ground_size = 50.0; | ||
let ground_height = 0.1; | ||
|
||
commands.spawn(( | ||
RigidBody::Fixed, | ||
Transform::from_translation(Vect::new(0.0, -ground_height, 0.0)), | ||
Collider::cuboid( | ||
ground_size, | ||
ground_height, | ||
pyramid_count as f32 * spacing / 2.0 + ground_size, | ||
), | ||
)); | ||
|
||
/* | ||
* Create the cubes | ||
*/ | ||
for pyramid_index in 0..pyramid_count { | ||
let bottomy = rad; | ||
create_pyramid( | ||
&mut commands, | ||
Vect::new( | ||
0.0, | ||
bottomy, | ||
(pyramid_index as f32 - pyramid_count as f32 / 2.0) * spacing, | ||
), | ||
stack_height, | ||
rad, | ||
); | ||
} | ||
}); | ||
} | ||
|
||
pub fn custom_bencher(steps: usize, setup: impl Fn(&mut App)) { | ||
let mut app = default_app(); | ||
setup(&mut app); | ||
wait_app_start(&mut app); | ||
|
||
let mut timer_total = rapier3d::counters::Timer::new(); | ||
let mut timer_full_update = rapier3d::counters::Timer::new(); | ||
let mut rapier_step_times = vec![]; | ||
let mut total_update_times = vec![]; | ||
timer_total.start(); | ||
for _ in 0..steps { | ||
timer_full_update.start(); | ||
app.update(); | ||
timer_full_update.pause(); | ||
let elapsed_time = timer_full_update.time() as f32; | ||
let rc = app.world().resource::<RapierContext>(); | ||
rapier_step_times.push(rc.pipeline.counters.step_time.time() as f32); | ||
total_update_times.push(elapsed_time); | ||
} | ||
timer_total.pause(); | ||
let average_total = total_update_times.iter().sum::<f32>() / total_update_times.len() as f32; | ||
println!("average total time: {}", average_total); | ||
let average_rapier_step = | ||
rapier_step_times.iter().sum::<f32>() / rapier_step_times.len() as f32; | ||
println!("average rapier step time: {}", average_rapier_step); | ||
let average_rapier_overhead = average_total - average_rapier_step; | ||
println!("average bevy overhead: {}", average_rapier_overhead); | ||
println!("total time: {}", timer_total.time()); | ||
} | ||
|
||
fn pyramid_1_with_height_2() { | ||
custom_bencher(1000, |app| setup_cubes(app, 1, 2)); | ||
} | ||
|
||
fn pyramid_2_with_height_20() { | ||
custom_bencher(100, |app| setup_cubes(app, 3, 20)); | ||
} | ||
|
||
fn main() { | ||
pyramid_1_with_height_2(); | ||
pyramid_2_with_height_20(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of
benches_common
be its own create, it should just be a subdirectory in thebevy_rapier3d/benches
folder.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I followed the same hierarchy of avian : https://github.com/Jondolf/avian/tree/main/crates ; I'd have preferred a way to enable feature on benches_common to support either 2d or 3d, but I didn't try that.
Either way, it's not too much code, and we can probably focus on 3d for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That actually allows me to share some logic with the custom benchmark.
I did add this logic in the custom benchmark, and call into this custom benchmark library from the "simpler" benches.