Skip to content

Commit 2ec38d1

Browse files
authored
Inline more ECS functions (#8083)
# Objective Upon closer inspection, there are a few functions in the ECS that are not being inlined, even with the highest optimizations and LTO enabled: - Almost all [WorldQuery::init_fetch](https://github.com/james7132/bevy_asm_tests/blob/9fd5f20e252f6e4401d352a0b675098d7e010aff/results/query_get.s#L57) calls. Affects `Query::get` calls in hot loops. In particular, the `WorldQuery` implementation for `()` is used *everywhere* as the default filter and is effectively a no-op. - [Entities::get](https://github.com/james7132/bevy_asm_test/blob/9fd5f20e252f6e4401d352a0b675098d7e010aff/results/query_get.s#L39). Affects `Query::get`, `World::get`, and any component insertion or removal. - [Entities::set](https://github.com/james7132/bevy_asm_tests/blob/9fd5f20e252f6e4401d352a0b675098d7e010aff/results/entity_remove.s#L2487). Affects any component insertion or removal. - [Tick::new](https://github.com/james7132/bevy_asm_tests/blob/9fd5f20e252f6e4401d352a0b675098d7e010aff/results/entity_insert.s#L1368). I've only seen this in component insertion and spawning. - ArchetypeRow::new - BlobVec::set_len Almost all of these have trivial or even empty implementations or have significant opportunity to be optimized into surrounding code when inlined with LTO enabled. ## Solution Inline them
1 parent f3c7cce commit 2ec38d1

File tree

11 files changed

+40
-1
lines changed

11 files changed

+40
-1
lines changed

crates/bevy_ecs/src/archetype.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl ArchetypeRow {
4747
pub const INVALID: ArchetypeRow = ArchetypeRow(u32::MAX);
4848

4949
/// Creates a `ArchetypeRow`.
50+
#[inline]
5051
pub const fn new(index: usize) -> Self {
5152
Self(index as u32)
5253
}
@@ -433,6 +434,7 @@ impl Archetype {
433434
/// # Safety
434435
/// valid component values must be immediately written to the relevant storages
435436
/// `table_row` must be valid
437+
#[inline]
436438
pub(crate) unsafe fn allocate(
437439
&mut self,
438440
entity: Entity,
@@ -449,6 +451,7 @@ impl Archetype {
449451
}
450452
}
451453

454+
#[inline]
452455
pub(crate) fn reserve(&mut self, additional: usize) {
453456
self.entities.reserve(additional);
454457
}
@@ -458,6 +461,7 @@ impl Archetype {
458461
///
459462
/// # Panics
460463
/// This function will panic if `index >= self.len()`
464+
#[inline]
461465
pub(crate) fn swap_remove(&mut self, row: ArchetypeRow) -> ArchetypeSwapRemoveResult {
462466
let is_last = row.index() == self.entities.len() - 1;
463467
let entity = self.entities.swap_remove(row.index());

crates/bevy_ecs/src/bundle.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ impl SparseSetIndex for BundleId {
263263
self.index()
264264
}
265265

266+
#[inline]
266267
fn get_sparse_set_index(value: usize) -> Self {
267268
Self(value)
268269
}

crates/bevy_ecs/src/component.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ impl SparseSetIndex for ComponentId {
283283
self.index()
284284
}
285285

286+
#[inline]
286287
fn get_sparse_set_index(value: usize) -> Self {
287288
Self(value)
288289
}
@@ -601,6 +602,7 @@ impl Tick {
601602
/// ticks are periodically scanned to ensure their relative values are below this.
602603
pub const MAX: Self = Self::new(MAX_CHANGE_AGE);
603604

605+
#[inline]
604606
pub const fn new(tick: u32) -> Self {
605607
Self { tick }
606608
}
@@ -617,10 +619,10 @@ impl Tick {
617619
self.tick = tick;
618620
}
619621

620-
#[inline]
621622
/// Returns `true` if this `Tick` occurred since the system's `last_run`.
622623
///
623624
/// `this_run` is the current tick of the system, used as a reference to help deal with wraparound.
625+
#[inline]
624626
pub fn is_newer_than(self, last_run: Tick, this_run: Tick) -> bool {
625627
// This works even with wraparound because the world tick (`this_run`) is always "newer" than
626628
// `last_run` and `self.tick`, and we scan periodically to clamp `ComponentTicks` values
@@ -634,6 +636,7 @@ impl Tick {
634636
}
635637

636638
/// Returns a change tick representing the relationship between `self` and `other`.
639+
#[inline]
637640
pub(crate) fn relative_to(self, other: Self) -> Self {
638641
let tick = self.tick.wrapping_sub(other.tick);
639642
Self { tick }
@@ -642,6 +645,7 @@ impl Tick {
642645
/// Wraps this change tick's value if it exceeds [`Tick::MAX`].
643646
///
644647
/// Returns `true` if wrapping was performed. Otherwise, returns `false`.
648+
#[inline]
645649
pub(crate) fn check_tick(&mut self, tick: Tick) -> bool {
646650
let age = tick.relative_to(*self);
647651
// This comparison assumes that `age` has not overflowed `u32::MAX` before, which will be true

crates/bevy_ecs/src/entity/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,10 +234,12 @@ impl fmt::Debug for Entity {
234234
}
235235

236236
impl SparseSetIndex for Entity {
237+
#[inline]
237238
fn sparse_set_index(&self) -> usize {
238239
self.index() as usize
239240
}
240241

242+
#[inline]
241243
fn get_sparse_set_index(value: usize) -> Self {
242244
Entity::from_raw(value as u32)
243245
}
@@ -570,6 +572,7 @@ impl Entities {
570572

571573
/// Returns the location of an [`Entity`].
572574
/// Note: for pending entities, returns `Some(EntityLocation::INVALID)`.
575+
#[inline]
573576
pub fn get(&self, entity: Entity) -> Option<EntityLocation> {
574577
if let Some(meta) = self.meta.get(entity.index as usize) {
575578
if meta.generation != entity.generation
@@ -590,6 +593,7 @@ impl Entities {
590593
/// - `index` must be a valid entity index.
591594
/// - `location` must be valid for the entity at `index` or immediately made valid afterwards
592595
/// before handing control to unknown code.
596+
#[inline]
593597
pub(crate) unsafe fn set(&mut self, index: u32, location: EntityLocation) {
594598
// SAFETY: Caller guarantees that `index` a valid entity index
595599
self.meta.get_unchecked_mut(index as usize).location = location;

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ unsafe impl<T: Component> WorldQuery for &T {
550550

551551
const IS_ARCHETYPAL: bool = true;
552552

553+
#[inline]
553554
unsafe fn init_fetch<'w>(
554555
world: &'w World,
555556
&component_id: &ComponentId,
@@ -695,6 +696,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
695696

696697
const IS_ARCHETYPAL: bool = true;
697698

699+
#[inline]
698700
unsafe fn init_fetch<'w>(
699701
world: &'w World,
700702
&component_id: &ComponentId,
@@ -856,6 +858,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
856858

857859
const IS_ARCHETYPAL: bool = true;
858860

861+
#[inline]
859862
unsafe fn init_fetch<'w>(
860863
world: &'w World,
861864
&component_id: &ComponentId,
@@ -1000,6 +1003,7 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
10001003

10011004
const IS_ARCHETYPAL: bool = T::IS_ARCHETYPAL;
10021005

1006+
#[inline]
10031007
unsafe fn init_fetch<'w>(
10041008
world: &'w World,
10051009
state: &T::State,
@@ -1104,6 +1108,7 @@ macro_rules! impl_tuple_fetch {
11041108
)*)
11051109
}
11061110

1111+
#[inline]
11071112
#[allow(clippy::unused_unit)]
11081113
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
11091114
let ($($name,)*) = state;
@@ -1213,6 +1218,7 @@ macro_rules! impl_anytuple_fetch {
12131218
)*)
12141219
}
12151220

1221+
#[inline]
12161222
#[allow(clippy::unused_unit)]
12171223
unsafe fn init_fetch<'w>(_world: &'w World, state: &Self::State, _last_run: Tick, _this_run: Tick) -> Self::Fetch<'w> {
12181224
let ($($name,)*) = state;

crates/bevy_ecs/src/query/filter.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
5050

5151
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
5252

53+
#[inline]
5354
unsafe fn init_fetch(_world: &World, _state: &ComponentId, _last_run: Tick, _this_run: Tick) {}
5455

5556
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
@@ -146,6 +147,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
146147

147148
fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}
148149

150+
#[inline]
149151
unsafe fn init_fetch(_world: &World, _state: &ComponentId, _last_run: Tick, _this_run: Tick) {}
150152

151153
unsafe fn clone_fetch<'w>(_fetch: &Self::Fetch<'w>) -> Self::Fetch<'w> {}
@@ -265,6 +267,7 @@ macro_rules! impl_query_filter_tuple {
265267

266268
const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*;
267269

270+
#[inline]
268271
unsafe fn init_fetch<'w>(world: &'w World, state: &Self::State, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
269272
let ($($filter,)*) = state;
270273
($(OrFetch {
@@ -420,6 +423,7 @@ macro_rules! impl_tick_filter {
420423
item
421424
}
422425

426+
#[inline]
423427
unsafe fn init_fetch<'w>(world: &'w World, &id: &ComponentId, last_run: Tick, this_run: Tick) -> Self::Fetch<'w> {
424428
Self::Fetch::<'w> {
425429
table_ticks: None,

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ impl BlobVec {
244244
/// Newly added items must be immediately populated with valid values and length must be
245245
/// increased. For better unwind safety, call [`BlobVec::set_len`] _after_ populating a new
246246
/// value.
247+
#[inline]
247248
pub unsafe fn set_len(&mut self, len: usize) {
248249
debug_assert!(len <= self.capacity());
249250
self.len = len;

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,10 +546,12 @@ pub trait SparseSetIndex: Clone + PartialEq + Eq + Hash {
546546
macro_rules! impl_sparse_set_index {
547547
($($ty:ty),+) => {
548548
$(impl SparseSetIndex for $ty {
549+
#[inline]
549550
fn sparse_set_index(&self) -> usize {
550551
*self as usize
551552
}
552553

554+
#[inline]
553555
fn get_sparse_set_index(value: usize) -> Self {
554556
value as $ty
555557
}
@@ -587,6 +589,7 @@ impl SparseSets {
587589
}
588590

589591
/// Gets a reference to the [`ComponentSparseSet`] of a [`ComponentId`].
592+
#[inline]
590593
pub fn get(&self, component_id: ComponentId) -> Option<&ComponentSparseSet> {
591594
self.sets.get(component_id)
592595
}

crates/bevy_ecs/src/world/identifier.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl SparseSetIndex for WorldId {
7171
self.0
7272
}
7373

74+
#[inline]
7475
fn get_sparse_set_index(value: usize) -> Self {
7576
Self(value)
7677
}

crates/bevy_ecs/src/world/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,21 @@ impl World {
109109
}
110110

111111
/// Creates a new [`UnsafeWorldCell`] view with complete read+write access.
112+
#[inline]
112113
pub fn as_unsafe_world_cell(&mut self) -> UnsafeWorldCell<'_> {
113114
UnsafeWorldCell::new_mutable(self)
114115
}
115116

116117
/// Creates a new [`UnsafeWorldCell`] view with only read access to everything.
118+
#[inline]
117119
pub fn as_unsafe_world_cell_readonly(&self) -> UnsafeWorldCell<'_> {
118120
UnsafeWorldCell::new_readonly(self)
119121
}
120122

121123
/// Creates a new [`UnsafeWorldCell`] with read+write access from a [&World](World).
122124
/// This is only a temporary measure until every `&World` that is semantically a [`UnsafeWorldCell`]
123125
/// has been replaced.
126+
#[inline]
124127
pub(crate) fn as_unsafe_world_cell_migration_internal(&self) -> UnsafeWorldCell<'_> {
125128
UnsafeWorldCell::new_readonly(self)
126129
}

crates/bevy_ecs/src/world/unsafe_world_cell.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,13 @@ unsafe impl Sync for UnsafeWorldCell<'_> {}
8181

8282
impl<'w> UnsafeWorldCell<'w> {
8383
/// Creates a [`UnsafeWorldCell`] that can be used to access everything immutably
84+
#[inline]
8485
pub(crate) fn new_readonly(world: &'w World) -> Self {
8586
UnsafeWorldCell(world as *const World as *mut World, PhantomData)
8687
}
8788

8889
/// Creates [`UnsafeWorldCell`] that can be used to access everything mutably
90+
#[inline]
8991
pub(crate) fn new_mutable(world: &'w mut World) -> Self {
9092
Self(world as *mut World, PhantomData)
9193
}
@@ -99,6 +101,7 @@ impl<'w> UnsafeWorldCell<'w> {
99101
/// - there must be no other borrows on world
100102
/// - returned borrow must not be used in any way if the world is accessed
101103
/// through other means than the `&mut World` after this call.
104+
#[inline]
102105
pub unsafe fn world_mut(self) -> &'w mut World {
103106
// SAFETY:
104107
// - caller ensures the created `&mut World` is the only borrow of world
@@ -112,6 +115,7 @@ impl<'w> UnsafeWorldCell<'w> {
112115
/// - must have permission to access the whole world immutably
113116
/// - there must be no live exclusive borrows on world data
114117
/// - there must be no live exclusive borrow of world
118+
#[inline]
115119
pub unsafe fn world(self) -> &'w World {
116120
// SAFETY:
117121
// - caller ensures there is no `&mut World` this makes it okay to make a `&World`
@@ -128,6 +132,7 @@ impl<'w> UnsafeWorldCell<'w> {
128132
///
129133
/// # Safety
130134
/// - must only be used to access world metadata
135+
#[inline]
131136
pub unsafe fn world_metadata(self) -> &'w World {
132137
// SAFETY: caller ensures that returned reference is not used to violate aliasing rules
133138
unsafe { self.unsafe_world() }
@@ -144,6 +149,7 @@ impl<'w> UnsafeWorldCell<'w> {
144149
/// # Safety
145150
/// - must not be used in a way that would conflict with any
146151
/// live exclusive borrows on world data
152+
#[inline]
147153
pub(crate) unsafe fn unsafe_world(self) -> &'w World {
148154
// SAFETY:
149155
// - caller ensures that the returned `&World` is not used in a way that would conflict
@@ -240,6 +246,7 @@ impl<'w> UnsafeWorldCell<'w> {
240246

241247
/// Retrieves an [`UnsafeEntityCell`] that exposes read and write operations for the given `entity`.
242248
/// Similar to the [`UnsafeWorldCell`], you are in charge of making sure that no aliasing rules are violated.
249+
#[inline]
243250
pub fn get_entity(self, entity: Entity) -> Option<UnsafeEntityCell<'w>> {
244251
let location = self.entities().get(entity)?;
245252
Some(UnsafeEntityCell::new(self, entity, location))
@@ -502,6 +509,7 @@ pub struct UnsafeEntityCell<'w> {
502509
}
503510

504511
impl<'w> UnsafeEntityCell<'w> {
512+
#[inline]
505513
pub(crate) fn new(
506514
world: UnsafeWorldCell<'w>,
507515
entity: Entity,

0 commit comments

Comments
 (0)