From 08ef2f0a2841bd5aa0ef01ed711744a7ede9eb42 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 19 Mar 2022 04:41:30 +0000 Subject: [PATCH] Add cart's fork of ecs_bench_suite (#4225) # Objective Better benchmarking for ECS. Fix #2062. ## Solution Port @cart's fork of ecs_bench_suite to the official bench suite for bevy_ecs, replace cgmath with glam, update to latest bevy. --- benches/Cargo.toml | 6 + .../bevy_ecs/ecs_bench_suite/add_remove.rs | 30 +++ .../add_remove_big_sparse_set.rs | 55 ++++++ .../ecs_bench_suite/add_remove_big_table.rs | 54 ++++++ .../ecs_bench_suite/add_remove_sparse_set.rs | 31 ++++ .../ecs_bench_suite/add_remove_table.rs | 30 +++ .../bevy_ecs/ecs_bench_suite/frag_iter.rs | 35 ++++ .../ecs_bench_suite/frag_iter_foreach.rs | 35 ++++ .../bevy_ecs/ecs_bench_suite/get_component.rs | 23 +++ .../ecs_bench_suite/get_component_system.rs | 31 ++++ .../bevy_ecs/ecs_bench_suite/heavy_compute.rs | 55 ++++++ .../benches/bevy_ecs/ecs_bench_suite/mod.rs | 174 ++++++++++++++++++ .../bevy_ecs/ecs_bench_suite/schedule.rs | 58 ++++++ .../bevy_ecs/ecs_bench_suite/simple_insert.rs | 34 ++++ .../simple_insert_unbatched.rs | 34 ++++ .../bevy_ecs/ecs_bench_suite/simple_iter.rs | 41 +++++ .../ecs_bench_suite/simple_iter_foreach.rs | 42 +++++ .../ecs_bench_suite/simple_iter_sparse.rs | 43 +++++ .../simple_iter_sparse_foreach.rs | 44 +++++ .../ecs_bench_suite/simple_iter_system.rs | 49 +++++ .../ecs_bench_suite/sparse_frag_iter.rs | 46 +++++ .../sparse_frag_iter_foreach.rs | 46 +++++ 22 files changed, 996 insertions(+) create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/add_remove.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_sparse_set.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_table.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/add_remove_sparse_set.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/add_remove_table.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/frag_iter.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/get_component.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/mod.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/schedule.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_insert.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_insert_unbatched.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter.rs create mode 100644 benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach.rs diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 1eac8d1d19794..a124d8d197b6d 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -7,10 +7,16 @@ publish = false license = "MIT OR Apache-2.0" [dev-dependencies] +glam = "0.20" criterion = "0.3" bevy_ecs = { path = "../crates/bevy_ecs" } bevy_tasks = { path = "../crates/bevy_tasks" } +[[bench]] +name = "ecs_bench_suite" +path = "benches/bevy_ecs/ecs_bench_suite/mod.rs" +harness = false + [[bench]] name = "system_stage" path = "benches/bevy_ecs/stages.rs" diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/add_remove.rs b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove.rs new file mode 100644 index 0000000000000..0c5bc56b45b8e --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove.rs @@ -0,0 +1,30 @@ +use bevy::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + + let entities = world + .spawn_batch((0..10000).map(|_| (A(0.0),))) + .collect::>(); + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.insert_one(*entity, B(0.0)).unwrap(); + } + + for entity in &self.1 { + self.0.remove_one::(*entity).unwrap(); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_sparse_set.rs b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_sparse_set.rs new file mode 100644 index 0000000000000..db4f83d99edfb --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_sparse_set.rs @@ -0,0 +1,55 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct A(Mat4); +#[derive(Component, Copy, Clone)] +struct B(Mat4); + +#[derive(Component, Copy, Clone)] +struct C(Mat4); +#[derive(Component, Copy, Clone)] +struct D(Mat4); + +#[derive(Component, Copy, Clone)] +struct E(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct F(Mat4); +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push( + world + .spawn() + .insert_bundle(( + A(Mat4::from_scale(Vec3::ONE)), + B(Mat4::from_scale(Vec3::ONE)), + C(Mat4::from_scale(Vec3::ONE)), + D(Mat4::from_scale(Vec3::ONE)), + E(Mat4::from_scale(Vec3::ONE)), + )) + .id(), + ); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0 + .entity_mut(*entity) + .insert(F(Mat4::from_scale(Vec3::ONE))); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_table.rs b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_table.rs new file mode 100644 index 0000000000000..0d2ba46c73a6a --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_big_table.rs @@ -0,0 +1,54 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct A(Mat4); +#[derive(Component, Copy, Clone)] +struct B(Mat4); + +#[derive(Component, Copy, Clone)] +struct C(Mat4); +#[derive(Component, Copy, Clone)] +struct D(Mat4); + +#[derive(Component, Copy, Clone)] +struct E(Mat4); + +#[derive(Component, Copy, Clone)] +struct F(Mat4); +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push( + world + .spawn() + .insert_bundle(( + A(Mat4::from_scale(Vec3::ONE)), + B(Mat4::from_scale(Vec3::ONE)), + C(Mat4::from_scale(Vec3::ONE)), + D(Mat4::from_scale(Vec3::ONE)), + E(Mat4::from_scale(Vec3::ONE)), + )) + .id(), + ); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0 + .entity_mut(*entity) + .insert(F(Mat4::from_scale(Vec3::ONE))); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_sparse_set.rs b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_sparse_set.rs new file mode 100644 index 0000000000000..6cb90f641c788 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_sparse_set.rs @@ -0,0 +1,31 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +#[component(storage = "SparseSet")] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push(world.spawn().insert(A(0.0)).id()); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.entity_mut(*entity).insert(B(0.0)); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_table.rs b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_table.rs new file mode 100644 index 0000000000000..67043a5bbd8c2 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/add_remove_table.rs @@ -0,0 +1,30 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); + +pub struct Benchmark(World, Vec); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + let mut entities = Vec::with_capacity(10_000); + for _ in 0..10_000 { + entities.push(world.spawn().insert(A(0.0)).id()); + } + + Self(world, entities) + } + + pub fn run(&mut self) { + for entity in &self.1 { + self.0.entity_mut(*entity).insert(B(0.0)); + } + + for entity in &self.1 { + self.0.entity_mut(*entity).remove::(); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter.rs b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter.rs new file mode 100644 index 0000000000000..dcc675f6551fa --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter.rs @@ -0,0 +1,35 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(($variants(0.0), Data(1.0))); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach.rs b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach.rs new file mode 100644 index 0000000000000..16cade377f333 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/frag_iter_foreach.rs @@ -0,0 +1,35 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..20 { + $world.spawn().insert_bundle(($variants(0.0), Data(1.0))); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + create_entities!(world; A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z); + + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 *= 2.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/get_component.rs b/benches/benches/bevy_ecs/ecs_bench_suite/get_component.rs new file mode 100644 index 0000000000000..42e20a10923df --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/get_component.rs @@ -0,0 +1,23 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); + +pub struct Benchmark<'w>(World, Entity, QueryState<&'w mut A>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + let entity = world.spawn().insert(A(0.0)).id(); + let query = world.query::<&mut A>(); + Self(world, entity, query) + } + + pub fn run(&mut self) { + for _x in 0..100000 { + let mut a = unsafe { self.2.get_unchecked(&mut self.0, self.1).unwrap() }; + a.0 += 1.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs b/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs new file mode 100644 index 0000000000000..97fd004b9b1e1 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/get_component_system.rs @@ -0,0 +1,31 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); + +pub struct Benchmark(World, Entity, Box>); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::new(); + + let entity = world.spawn().insert(A(0.0)).id(); + fn query_system(In(entity): In, mut query: Query<&mut A>) { + for _ in 0..100_000 { + let mut a = query.get_mut(entity).unwrap(); + a.0 += 1.0; + } + } + + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + for archetype in world.archetypes().iter() { + system.new_archetype(archetype); + } + Self(world, entity, Box::new(system)) + } + + pub fn run(&mut self) { + self.2.run(self.1, &mut self.0); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs new file mode 100644 index 0000000000000..1fde2ce6988cb --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/heavy_compute.rs @@ -0,0 +1,55 @@ +use bevy_ecs::prelude::*; +use bevy_tasks::TaskPool; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +pub struct Benchmark(World, Box>); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + + world.spawn_batch((0..1000).map(|_| { + ( + Transform(Mat4::from_axis_angle(Vec3::X, 1.2)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + ) + })); + + fn sys(task_pool: Res, mut query: Query<(&mut Position, &mut Transform)>) { + query.par_for_each_mut(&task_pool, 128, |(mut pos, mut mat)| { + for _ in 0..100 { + mat.0 = mat.0.inverse(); + } + + pos.0 = mat.0.transform_vector3(pos.0); + }); + } + + world.insert_resource(TaskPool::default()); + let mut system = IntoSystem::into_system(sys); + system.initialize(&mut world); + for archetype in world.archetypes().iter() { + system.new_archetype(archetype); + } + + Self(world, Box::new(system)) + } + + pub fn run(&mut self) { + self.1.run((), &mut self.0); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs b/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs new file mode 100644 index 0000000000000..bb127d63a7cca --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/mod.rs @@ -0,0 +1,174 @@ +use criterion::*; + +mod add_remove_big_sparse_set; +mod add_remove_big_table; +mod add_remove_sparse_set; +mod add_remove_table; +mod frag_iter; +mod frag_iter_foreach; +mod get_component; +mod get_component_system; +mod heavy_compute; +mod schedule; +mod simple_insert; +mod simple_insert_unbatched; +mod simple_iter; +mod simple_iter_foreach; +mod simple_iter_sparse; +mod simple_iter_sparse_foreach; +mod simple_iter_system; +mod sparse_frag_iter; +mod sparse_frag_iter_foreach; + +fn bench_simple_insert(c: &mut Criterion) { + let mut group = c.benchmark_group("simple_insert"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = simple_insert::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("unbatched", |b| { + let mut bench = simple_insert_unbatched::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_simple_iter(c: &mut Criterion) { + let mut group = c.benchmark_group("simple_iter"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = simple_iter::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("system", |b| { + let mut bench = simple_iter_system::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse", |b| { + let mut bench = simple_iter_sparse::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = simple_iter_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_foreach", |b| { + let mut bench = simple_iter_sparse_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_frag_iter_bc(c: &mut Criterion) { + let mut group = c.benchmark_group("fragmented_iter"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = frag_iter::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = frag_iter_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_sparse_frag_iter(c: &mut Criterion) { + let mut group = c.benchmark_group("sparse_fragmented_iter"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = sparse_frag_iter::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("foreach", |b| { + let mut bench = sparse_frag_iter_foreach::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_schedule(c: &mut Criterion) { + let mut group = c.benchmark_group("schedule"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = schedule::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_heavy_compute(c: &mut Criterion) { + let mut group = c.benchmark_group("heavy_compute"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = heavy_compute::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_add_remove(c: &mut Criterion) { + let mut group = c.benchmark_group("add_remove_component"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("table", |b| { + let mut bench = add_remove_table::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_set", |b| { + let mut bench = add_remove_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_add_remove_big(c: &mut Criterion) { + let mut group = c.benchmark_group("add_remove_component_big"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("table", |b| { + let mut bench = add_remove_big_table::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("sparse_set", |b| { + let mut bench = add_remove_big_sparse_set::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +fn bench_get_component(c: &mut Criterion) { + let mut group = c.benchmark_group("get_component"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + group.bench_function("base", |b| { + let mut bench = get_component::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.bench_function("system", |b| { + let mut bench = get_component_system::Benchmark::new(); + b.iter(move || bench.run()); + }); + group.finish(); +} + +criterion_group!( + benchmarks, + bench_simple_insert, + bench_simple_iter, + bench_frag_iter_bc, + bench_sparse_frag_iter, + bench_schedule, + bench_heavy_compute, + bench_add_remove, + bench_add_remove_big, + bench_get_component, +); +criterion_main!(benchmarks); diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/schedule.rs b/benches/benches/bevy_ecs/ecs_bench_suite/schedule.rs new file mode 100644 index 0000000000000..dcd1fc7d3ac0a --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/schedule.rs @@ -0,0 +1,58 @@ +use bevy_ecs::prelude::*; + +#[derive(Component)] +struct A(f32); +#[derive(Component)] +struct B(f32); +#[derive(Component)] +struct C(f32); +#[derive(Component)] +struct D(f32); +#[derive(Component)] +struct E(f32); + +fn ab(mut query: Query<(&mut A, &mut B)>) { + query.for_each_mut(|(mut a, mut b)| { + std::mem::swap(&mut a.0, &mut b.0); + }); +} + +fn cd(mut query: Query<(&mut C, &mut D)>) { + query.for_each_mut(|(mut c, mut d)| { + std::mem::swap(&mut c.0, &mut d.0); + }); +} + +fn ce(mut query: Query<(&mut C, &mut E)>) { + query.for_each_mut(|(mut c, mut e)| { + std::mem::swap(&mut c.0, &mut e.0); + }); +} + +pub struct Benchmark(World, SystemStage); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::default(); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0), D(0.0)))); + + world.spawn_batch((0..10000).map(|_| (A(0.0), B(0.0), C(0.0), E(0.0)))); + + let mut stage = SystemStage::parallel(); + stage.add_system(ab); + stage.add_system(cd); + stage.add_system(ce); + stage.run(&mut world); + + Self(world, stage) + } + + pub fn run(&mut self) { + self.1.run(&mut self.0); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert.rs new file mode 100644 index 0000000000000..17ebc24593e3c --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert.rs @@ -0,0 +1,34 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark; + +impl Benchmark { + pub fn new() -> Self { + Self + } + + pub fn run(&mut self) { + let mut world = World::new(); + world.spawn_batch((0..10_000).map(|_| { + ( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + ) + })); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert_unbatched.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert_unbatched.rs new file mode 100644 index 0000000000000..daca82a18c895 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_insert_unbatched.rs @@ -0,0 +1,34 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark; + +impl Benchmark { + pub fn new() -> Self { + Self + } + + pub fn run(&mut self) { + let mut world = World::new(); + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter.rs new file mode 100644 index 0000000000000..5d99e19dba1f0 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter.rs @@ -0,0 +1,41 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + for (velocity, mut position) in self.1.iter_mut(&mut self.0) { + position.0 += velocity.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach.rs new file mode 100644 index 0000000000000..ae5b5c8757676 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_foreach.rs @@ -0,0 +1,42 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1 + .for_each_mut(&mut self.0, |(velocity, mut position)| { + position.0 += velocity.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse.rs new file mode 100644 index 0000000000000..676d4c4c4eff9 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse.rs @@ -0,0 +1,43 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + for (velocity, mut position) in self.1.iter_mut(&mut self.0) { + position.0 += velocity.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach.rs new file mode 100644 index 0000000000000..98e7ea3f74fa7 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_sparse_foreach.rs @@ -0,0 +1,44 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +#[component(storage = "SparseSet")] +struct Velocity(Vec3); + +pub struct Benchmark<'w>(World, QueryState<(&'w Velocity, &'w mut Position)>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + let query = world.query::<(&Velocity, &mut Position)>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1 + .for_each_mut(&mut self.0, |(velocity, mut position)| { + position.0 += velocity.0; + }); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs new file mode 100644 index 0000000000000..88b76e70c7a33 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/simple_iter_system.rs @@ -0,0 +1,49 @@ +use bevy_ecs::prelude::*; +use glam::*; + +#[derive(Component, Copy, Clone)] +struct Transform(Mat4); + +#[derive(Component, Copy, Clone)] +struct Position(Vec3); + +#[derive(Component, Copy, Clone)] +struct Rotation(Vec3); + +#[derive(Component, Copy, Clone)] +struct Velocity(Vec3); + +pub struct Benchmark(World, Box>); + +impl Benchmark { + pub fn new() -> Self { + let mut world = World::new(); + + // TODO: batch this + for _ in 0..10_000 { + world.spawn().insert_bundle(( + Transform(Mat4::from_scale(Vec3::ONE)), + Position(Vec3::X), + Rotation(Vec3::X), + Velocity(Vec3::X), + )); + } + + fn query_system(mut query: Query<(&Velocity, &mut Position)>) { + for (velocity, mut position) in query.iter_mut() { + position.0 += velocity.0; + } + } + + let mut system = IntoSystem::into_system(query_system); + system.initialize(&mut world); + for archetype in world.archetypes().iter() { + system.new_archetype(archetype); + } + Self(world, Box::new(system)) + } + + pub fn run(&mut self) { + self.1.run((), &mut self.0); + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter.rs b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter.rs new file mode 100644 index 0000000000000..0778b65429b61 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter.rs @@ -0,0 +1,46 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + + for _ in 0..5 { + world.spawn().insert(Data(1.0)); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + for mut data in self.1.iter_mut(&mut self.0) { + data.0 *= 2.0; + } + } +} diff --git a/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach.rs b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach.rs new file mode 100644 index 0000000000000..4cec9fe20c739 --- /dev/null +++ b/benches/benches/bevy_ecs/ecs_bench_suite/sparse_frag_iter_foreach.rs @@ -0,0 +1,46 @@ +use bevy_ecs::prelude::*; + +macro_rules! create_entities { + ($world:ident; $( $variants:ident ),*) => { + $( + #[derive(Component)] + struct $variants(f32); + for _ in 0..5 { + $world.spawn().insert($variants(0.0)); + } + )* + }; +} + +#[derive(Component)] +struct Data(f32); + +pub struct Benchmark<'w>(World, QueryState<&'w mut Data>); + +impl<'w> Benchmark<'w> { + pub fn new() -> Self { + let mut world = World::new(); + for _ in 0..5 { + world.spawn().insert(Data(1.0)); + } + + create_entities!(world; C00, C01, C02, C03, C04, C05, C06, C07, C08, C09); + create_entities!(world; C10, C11, C12, C13, C14, C15, C16, C17, C18, C19); + create_entities!(world; C20, C21, C22, C23, C24, C25, C26, C27, C28, C29); + create_entities!(world; C30, C31, C32, C33, C34, C35, C36, C37, C38, C39); + create_entities!(world; C40, C41, C42, C43, C44, C45, C46, C47, C48, C49); + create_entities!(world; C50, C51, C52, C53, C54, C55, C56, C57, C58, C59); + create_entities!(world; C60, C61, C62, C63, C64, C65, C66, C67, C68, C69); + create_entities!(world; C70, C71, C72, C73, C74, C75, C76, C77, C78, C79); + create_entities!(world; C80, C81, C82, C83, C84, C85, C86, C87, C88, C89); + create_entities!(world; C90, C91, C92, C93, C94, C95, C96, C97, C98, C99); + let query = world.query::<&mut Data>(); + Self(world, query) + } + + pub fn run(&mut self) { + self.1.for_each_mut(&mut self.0, |mut data| { + data.0 *= 2.0; + }); + } +}