Skip to content

Commit 234b2ef

Browse files
committed
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...
1 parent 6d6bc2a commit 234b2ef

File tree

3 files changed

+168
-0
lines changed

3 files changed

+168
-0
lines changed

benches/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ name = "commands"
2121
path = "benches/bevy_ecs/commands.rs"
2222
harness = false
2323

24+
[[bench]]
25+
name = "world_get"
26+
path = "benches/bevy_ecs/world_get.rs"
27+
harness = false
28+
2429
[[bench]]
2530
name = "iter"
2631
path = "benches/bevy_tasks/iter.rs"

benches/benches/bevy_ecs/world_get.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
use bevy::ecs::{
2+
component::{ComponentDescriptor, StorageType},
3+
entity::Entity,
4+
world::World,
5+
};
6+
use criterion::{black_box, criterion_group, criterion_main, Criterion};
7+
8+
criterion_group!(
9+
benches,
10+
world_entity,
11+
world_get,
12+
world_query_get,
13+
world_query_iter,
14+
world_query_for_each,
15+
);
16+
criterion_main!(benches);
17+
18+
struct A(f32);
19+
20+
const RANGE: std::ops::Range<u32> = 5..6;
21+
22+
fn setup(entity_count: u32, storage: StorageType) -> World {
23+
let mut world = World::default();
24+
world
25+
.register_component(ComponentDescriptor::new::<A>(storage))
26+
.unwrap();
27+
world.spawn_batch((0..entity_count).map(|_| (A(0.0),)));
28+
world
29+
}
30+
31+
fn world_entity(criterion: &mut Criterion) {
32+
let mut group = criterion.benchmark_group("world_entity");
33+
group.warm_up_time(std::time::Duration::from_millis(500));
34+
group.measurement_time(std::time::Duration::from_secs(4));
35+
36+
for entity_count in RANGE.map(|i| i * 10_000) {
37+
group.bench_function(format!("{}_entities", entity_count), |bencher| {
38+
let world = setup(entity_count, StorageType::Table);
39+
40+
bencher.iter(|| {
41+
for i in 0..entity_count {
42+
let entity = Entity::new(i);
43+
black_box(world.entity(entity));
44+
}
45+
});
46+
});
47+
}
48+
49+
group.finish();
50+
}
51+
52+
fn world_get(criterion: &mut Criterion) {
53+
let mut group = criterion.benchmark_group("world_get");
54+
group.warm_up_time(std::time::Duration::from_millis(500));
55+
group.measurement_time(std::time::Duration::from_secs(4));
56+
57+
for entity_count in RANGE.map(|i| i * 10_000) {
58+
for storage in [StorageType::Table, StorageType::SparseSet] {
59+
group.bench_function(
60+
format!("{}_entities_{:?}", entity_count, storage),
61+
|bencher| {
62+
let world = setup(entity_count, storage);
63+
64+
bencher.iter(|| {
65+
for i in 0..entity_count {
66+
let entity = Entity::new(i);
67+
assert!(world.get::<A>(entity).is_some());
68+
}
69+
});
70+
},
71+
);
72+
}
73+
}
74+
75+
group.finish();
76+
}
77+
78+
fn world_query_get(criterion: &mut Criterion) {
79+
let mut group = criterion.benchmark_group("world_query_get");
80+
group.warm_up_time(std::time::Duration::from_millis(500));
81+
group.measurement_time(std::time::Duration::from_secs(4));
82+
83+
for entity_count in RANGE.map(|i| i * 10_000) {
84+
for storage in [StorageType::Table, StorageType::SparseSet] {
85+
group.bench_function(
86+
format!("{}_entities_{:?}", entity_count, storage),
87+
|bencher| {
88+
let mut world = setup(entity_count, storage);
89+
let mut query = world.query::<&A>();
90+
91+
bencher.iter(|| {
92+
for i in 0..entity_count {
93+
let entity = Entity::new(i);
94+
assert!(query.get(&world, entity).is_ok());
95+
}
96+
});
97+
},
98+
);
99+
}
100+
}
101+
102+
group.finish();
103+
}
104+
105+
fn world_query_iter(criterion: &mut Criterion) {
106+
let mut group = criterion.benchmark_group("world_query_iter");
107+
group.warm_up_time(std::time::Duration::from_millis(500));
108+
group.measurement_time(std::time::Duration::from_secs(4));
109+
110+
for entity_count in RANGE.map(|i| i * 10_000) {
111+
for storage in [StorageType::Table, StorageType::SparseSet] {
112+
group.bench_function(
113+
format!("{}_entities_{:?}", entity_count, storage),
114+
|bencher| {
115+
let mut world = setup(entity_count, storage);
116+
let mut query = world.query::<&A>();
117+
118+
bencher.iter(|| {
119+
let mut count = 0;
120+
for comp in query.iter(&world) {
121+
black_box(comp);
122+
count += 1;
123+
}
124+
assert_eq!(black_box(count), entity_count);
125+
});
126+
},
127+
);
128+
}
129+
}
130+
131+
group.finish();
132+
}
133+
134+
fn world_query_for_each(criterion: &mut Criterion) {
135+
let mut group = criterion.benchmark_group("world_query_for_each");
136+
group.warm_up_time(std::time::Duration::from_millis(500));
137+
group.measurement_time(std::time::Duration::from_secs(4));
138+
139+
for entity_count in RANGE.map(|i| i * 10_000) {
140+
for storage in [StorageType::Table, StorageType::SparseSet] {
141+
group.bench_function(
142+
format!("{}_entities_{:?}", entity_count, storage),
143+
|bencher| {
144+
let mut world = setup(entity_count, storage);
145+
let mut query = world.query::<&A>();
146+
147+
bencher.iter(|| {
148+
let mut count = 0;
149+
query.for_each(&world, |comp| {
150+
black_box(comp);
151+
count += 1;
152+
});
153+
assert_eq!(black_box(count), entity_count);
154+
});
155+
},
156+
);
157+
}
158+
}
159+
160+
group.finish();
161+
}

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ impl Entities {
344344
/// Access the location storage of an entity.
345345
///
346346
/// Must not be called on pending entities.
347+
#[inline]
347348
pub fn get_mut(&mut self, entity: Entity) -> Option<&mut EntityLocation> {
348349
let meta = &mut self.meta[entity.id as usize];
349350
if meta.generation == entity.generation {
@@ -354,6 +355,7 @@ impl Entities {
354355
}
355356

356357
/// Returns `Ok(Location { archetype: 0, index: undefined })` for pending entities.
358+
#[inline]
357359
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
358360
if (entity.id as usize) < self.meta.len() {
359361
let meta = &self.meta[entity.id as usize];

0 commit comments

Comments
 (0)