diff --git a/benches/Cargo.toml b/benches/Cargo.toml index 3d13d202ef..318bd83fe2 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -21,6 +21,11 @@ name = "commands" path = "benches/bevy_ecs/commands.rs" harness = false +[[bench]] +name = "world_get" +path = "benches/bevy_ecs/world_get.rs" +harness = false + [[bench]] name = "iter" path = "benches/bevy_tasks/iter.rs" diff --git a/benches/benches/bevy_ecs/world_get.rs b/benches/benches/bevy_ecs/world_get.rs new file mode 100644 index 0000000000..21aae5a8f4 --- /dev/null +++ b/benches/benches/bevy_ecs/world_get.rs @@ -0,0 +1,161 @@ +use bevy::ecs::{ + component::{ComponentDescriptor, StorageType}, + entity::Entity, + world::World, +}; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +criterion_group!( + benches, + world_entity, + world_get, + world_query_get, + world_query_iter, + world_query_for_each, +); +criterion_main!(benches); + +struct A(f32); + +const RANGE: std::ops::Range = 5..6; + +fn setup(entity_count: u32, storage: StorageType) -> World { + let mut world = World::default(); + world + .register_component(ComponentDescriptor::new::(storage)) + .unwrap(); + world.spawn_batch((0..entity_count).map(|_| (A(0.0),))); + world +} + +fn world_entity(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_entity"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + group.bench_function(format!("{}_entities", entity_count), |bencher| { + let world = setup(entity_count, StorageType::Table); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + black_box(world.entity(entity)); + } + }); + }); + } + + group.finish(); +} + +fn world_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_get"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + for storage in [StorageType::Table, StorageType::SparseSet] { + group.bench_function( + format!("{}_entities_{:?}", entity_count, storage), + |bencher| { + let world = setup(entity_count, storage); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(world.get::(entity).is_some()); + } + }); + }, + ); + } + } + + group.finish(); +} + +fn world_query_get(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_get"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + for storage in [StorageType::Table, StorageType::SparseSet] { + group.bench_function( + format!("{}_entities_{:?}", entity_count, storage), + |bencher| { + let mut world = setup(entity_count, storage); + let mut query = world.query::<&A>(); + + bencher.iter(|| { + for i in 0..entity_count { + let entity = Entity::new(i); + assert!(query.get(&world, entity).is_ok()); + } + }); + }, + ); + } + } + + group.finish(); +} + +fn world_query_iter(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_iter"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + for storage in [StorageType::Table, StorageType::SparseSet] { + group.bench_function( + format!("{}_entities_{:?}", entity_count, storage), + |bencher| { + let mut world = setup(entity_count, storage); + let mut query = world.query::<&A>(); + + bencher.iter(|| { + let mut count = 0; + for comp in query.iter(&world) { + black_box(comp); + count += 1; + } + assert_eq!(black_box(count), entity_count); + }); + }, + ); + } + } + + group.finish(); +} + +fn world_query_for_each(criterion: &mut Criterion) { + let mut group = criterion.benchmark_group("world_query_for_each"); + group.warm_up_time(std::time::Duration::from_millis(500)); + group.measurement_time(std::time::Duration::from_secs(4)); + + for entity_count in RANGE.map(|i| i * 10_000) { + for storage in [StorageType::Table, StorageType::SparseSet] { + group.bench_function( + format!("{}_entities_{:?}", entity_count, storage), + |bencher| { + let mut world = setup(entity_count, storage); + let mut query = world.query::<&A>(); + + bencher.iter(|| { + let mut count = 0; + query.for_each(&world, |comp| { + black_box(comp); + count += 1; + }); + assert_eq!(black_box(count), entity_count); + }); + }, + ); + } + } + + group.finish(); +} diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index a1fd3e4bf4..df8b3abb48 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -344,6 +344,7 @@ impl Entities { /// Access the location storage of an entity. /// /// Must not be called on pending entities. + #[inline] pub fn get_mut(&mut self, entity: Entity) -> Option<&mut EntityLocation> { let meta = &mut self.meta[entity.id as usize]; if meta.generation == entity.generation { @@ -354,6 +355,7 @@ impl Entities { } /// Returns `Ok(Location { archetype: 0, index: undefined })` for pending entities. + #[inline] pub fn get(&self, entity: Entity) -> Option { if (entity.id as usize) < self.meta.len() { let meta = &self.meta[entity.id as usize];