From 846c22d6e9455655d5d2f675bf279586e9be5c59 Mon Sep 17 00:00:00 2001 From: James Liu Date: Tue, 15 Nov 2022 21:39:21 +0000 Subject: [PATCH] Remove redundant table and sparse set component IDs from Archetype (#4927) # Objective Archetype is a deceptively large type in memory. It stores metadata about which components are in which storage in multiple locations, which is only used when creating new Archetypes while moving entities. ## Solution Remove the redundant `Box<[ComponentId]>`s and iterate over the sparse set of component metadata instead. Reduces Archetype's size by 4 usizes (32 bytes on 64-bit systems), as well as the additional allocations for holding these slices. It'd seem like there's a downside that the origin archetype has it's component metadata iterated over twice when creating a new archetype, but this change also removes the extra `Vec` allocations when creating a new archetype which may amortize out to a net gain here. This change likely negatively impacts creating new archetypes with a large number of components, but that's a cost mitigated by the fact that these archetypal relationships are cached in Edges and is incurred only once for each edge created. ## Additional Context There are several other in-flight PRs that shrink Archetype: - #4800 merges the entities and rows Vecs together (shaves off 24 bytes per archetype) - #4809 removes unique_components and moves it to it's own dedicated storage (shaves off 72 bytes per archetype) --- ## Changelog Changed: `Archetype::table_components` and `Archetype::sparse_set_components` return iterators instead of slices. `Archetype::new` requires iterators instead of parallel slices/vecs. ## Migration Guide Do I still need to do this? I really hope people were not relying on the public facing APIs changed here. --- crates/bevy_ecs/src/archetype.rs | 77 +++++++++++-------------- crates/bevy_ecs/src/bundle.rs | 4 +- crates/bevy_ecs/src/world/entity_ref.rs | 6 +- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 0fc9271a42c6c..f72ac28784a47 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -182,8 +182,6 @@ pub struct Archetype { table_id: TableId, edges: Edges, entities: Vec, - table_components: Box<[ComponentId]>, - sparse_set_components: Box<[ComponentId]>, components: SparseSet, } @@ -191,18 +189,15 @@ impl Archetype { pub fn new( id: ArchetypeId, table_id: TableId, - table_components: Box<[ComponentId]>, - sparse_set_components: Box<[ComponentId]>, - table_archetype_components: Vec, - sparse_set_archetype_components: Vec, + table_components: impl Iterator, + sparse_set_components: impl Iterator, ) -> Self { - let mut components = - SparseSet::with_capacity(table_components.len() + sparse_set_components.len()); - for (component_id, archetype_component_id) in - table_components.iter().zip(table_archetype_components) - { + let (min_table, _) = table_components.size_hint(); + let (min_sparse, _) = sparse_set_components.size_hint(); + let mut components = SparseSet::with_capacity(min_table + min_sparse); + for (component_id, archetype_component_id) in table_components { components.insert( - *component_id, + component_id, ArchetypeComponentInfo { storage_type: StorageType::Table, archetype_component_id, @@ -210,12 +205,9 @@ impl Archetype { ); } - for (component_id, archetype_component_id) in sparse_set_components - .iter() - .zip(sparse_set_archetype_components) - { + for (component_id, archetype_component_id) in sparse_set_components { components.insert( - *component_id, + component_id, ArchetypeComponentInfo { storage_type: StorageType::SparseSet, archetype_component_id, @@ -225,10 +217,8 @@ impl Archetype { Self { id, table_id, - entities: Vec::new(), components, - table_components, - sparse_set_components, + entities: Default::default(), edges: Default::default(), } } @@ -249,13 +239,19 @@ impl Archetype { } #[inline] - pub fn table_components(&self) -> &[ComponentId] { - &self.table_components + pub fn table_components(&self) -> impl Iterator + '_ { + self.components + .iter() + .filter(|(_, component)| component.storage_type == StorageType::Table) + .map(|(id, _)| *id) } #[inline] - pub fn sparse_set_components(&self) -> &[ComponentId] { - &self.sparse_set_components + pub fn sparse_set_components(&self) -> impl Iterator + '_ { + self.components + .iter() + .filter(|(_, component)| component.storage_type == StorageType::SparseSet) + .map(|(id, _)| *id) } #[inline] @@ -484,38 +480,33 @@ impl Archetypes { table_components: Vec, sparse_set_components: Vec, ) -> ArchetypeId { - let table_components = table_components.into_boxed_slice(); - let sparse_set_components = sparse_set_components.into_boxed_slice(); let archetype_identity = ArchetypeIdentity { - sparse_set_components: sparse_set_components.clone(), - table_components: table_components.clone(), + sparse_set_components: sparse_set_components.clone().into_boxed_slice(), + table_components: table_components.clone().into_boxed_slice(), }; let archetypes = &mut self.archetypes; let archetype_component_count = &mut self.archetype_component_count; - let mut next_archetype_component_id = move || { - let id = ArchetypeComponentId(*archetype_component_count); - *archetype_component_count += 1; - id - }; *self .archetype_ids .entry(archetype_identity) .or_insert_with(move || { let id = ArchetypeId(archetypes.len()); - let table_archetype_components = (0..table_components.len()) - .map(|_| next_archetype_component_id()) - .collect(); - let sparse_set_archetype_components = (0..sparse_set_components.len()) - .map(|_| next_archetype_component_id()) - .collect(); + let table_start = *archetype_component_count; + *archetype_component_count += table_components.len(); + let table_archetype_components = + (table_start..*archetype_component_count).map(ArchetypeComponentId); + let sparse_start = *archetype_component_count; + *archetype_component_count += sparse_set_components.len(); + let sparse_set_archetype_components = + (sparse_start..*archetype_component_count).map(ArchetypeComponentId); archetypes.push(Archetype::new( id, table_id, - table_components, - sparse_set_components, - table_archetype_components, - sparse_set_archetype_components, + table_components.into_iter().zip(table_archetype_components), + sparse_set_components + .into_iter() + .zip(sparse_set_archetype_components), )); id }) diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 8dd5b5225d5c3..91f29e4ac57e2 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -461,7 +461,7 @@ impl BundleInfo { table_components = if new_table_components.is_empty() { // if there are no new table components, we can keep using this table table_id = current_archetype.table_id(); - current_archetype.table_components().to_vec() + current_archetype.table_components().collect() } else { new_table_components.extend(current_archetype.table_components()); // sort to ignore order while hashing @@ -477,7 +477,7 @@ impl BundleInfo { }; sparse_set_components = if new_sparse_set_components.is_empty() { - current_archetype.sparse_set_components().to_vec() + current_archetype.sparse_set_components().collect() } else { new_sparse_set_components.extend(current_archetype.sparse_set_components()); // sort to ignore order while hashing diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index c9f9321d5a7b3..66be80be0840c 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -505,7 +505,7 @@ impl<'w> EntityMut<'w> { table_row = remove_result.table_row; for component_id in archetype.sparse_set_components() { - let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap(); + let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap(); sparse_set.remove(self.entity); } // SAFETY: table rows stored in archetypes always exist @@ -843,8 +843,8 @@ unsafe fn remove_bundle_from_archetype( // components are already sorted removed_table_components.sort(); removed_sparse_set_components.sort(); - next_table_components = current_archetype.table_components().to_vec(); - next_sparse_set_components = current_archetype.sparse_set_components().to_vec(); + next_table_components = current_archetype.table_components().collect(); + next_sparse_set_components = current_archetype.sparse_set_components().collect(); sorted_remove(&mut next_table_components, &removed_table_components); sorted_remove( &mut next_sparse_set_components,