mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Inline world get (#2520)
# Objective While looking at the code of `World`, I noticed two basic functions (`get` and `get_mut`) that are probably called a lot and with simple code that are not `inline` ## Solution - Add benchmark to check impact - Add `#[inline]` ``` group this pr main ----- ---- ---- world_entity/50000_entities 1.00 115.9±11.90µs ? ?/sec 1.71 198.5±29.54µs ? ?/sec world_get/50000_entities_SparseSet 1.00 409.9±46.96µs ? ?/sec 1.18 483.5±36.41µs ? ?/sec world_get/50000_entities_Table 1.00 391.3±29.83µs ? ?/sec 1.16 455.6±57.85µs ? ?/sec world_query_for_each/50000_entities_SparseSet 1.02 121.3±18.36µs ? ?/sec 1.00 119.4±13.88µs ? ?/sec world_query_for_each/50000_entities_Table 1.03 13.8±0.96µs ? ?/sec 1.00 13.3±0.54µs ? ?/sec world_query_get/50000_entities_SparseSet 1.00 666.9±54.36µs ? ?/sec 1.03 687.1±57.77µs ? ?/sec world_query_get/50000_entities_Table 1.01 584.4±55.12µs ? ?/sec 1.00 576.3±36.13µs ? ?/sec world_query_iter/50000_entities_SparseSet 1.01 169.7±19.50µs ? ?/sec 1.00 168.6±32.56µs ? ?/sec world_query_iter/50000_entities_Table 1.00 26.2±1.38µs ? ?/sec 1.91 50.0±4.40µs ? ?/sec ``` I didn't add benchmarks for the mutable path but I don't see how it could hurt to make it inline too...
This commit is contained in:
parent
6d6bc2a8b4
commit
234b2efa71
3 changed files with 168 additions and 0 deletions
|
@ -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"
|
||||
|
|
161
benches/benches/bevy_ecs/world_get.rs
Normal file
161
benches/benches/bevy_ecs/world_get.rs
Normal file
|
@ -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<u32> = 5..6;
|
||||
|
||||
fn setup(entity_count: u32, storage: StorageType) -> World {
|
||||
let mut world = World::default();
|
||||
world
|
||||
.register_component(ComponentDescriptor::new::<A>(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::<A>(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();
|
||||
}
|
|
@ -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<EntityLocation> {
|
||||
if (entity.id as usize) < self.meta.len() {
|
||||
let meta = &self.meta[entity.id as usize];
|
||||
|
|
Loading…
Reference in a new issue