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<ArchetypeComponentId>` 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.
This commit is contained in:
James Liu 2022-11-15 21:39:21 +00:00
parent 51aab032ed
commit 11c544c29a
3 changed files with 39 additions and 48 deletions

View file

@ -182,8 +182,6 @@ pub struct Archetype {
table_id: TableId, table_id: TableId,
edges: Edges, edges: Edges,
entities: Vec<ArchetypeEntity>, entities: Vec<ArchetypeEntity>,
table_components: Box<[ComponentId]>,
sparse_set_components: Box<[ComponentId]>,
components: SparseSet<ComponentId, ArchetypeComponentInfo>, components: SparseSet<ComponentId, ArchetypeComponentInfo>,
} }
@ -191,18 +189,15 @@ impl Archetype {
pub fn new( pub fn new(
id: ArchetypeId, id: ArchetypeId,
table_id: TableId, table_id: TableId,
table_components: Box<[ComponentId]>, table_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
sparse_set_components: Box<[ComponentId]>, sparse_set_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
table_archetype_components: Vec<ArchetypeComponentId>,
sparse_set_archetype_components: Vec<ArchetypeComponentId>,
) -> Self { ) -> Self {
let mut components = let (min_table, _) = table_components.size_hint();
SparseSet::with_capacity(table_components.len() + sparse_set_components.len()); let (min_sparse, _) = sparse_set_components.size_hint();
for (component_id, archetype_component_id) in let mut components = SparseSet::with_capacity(min_table + min_sparse);
table_components.iter().zip(table_archetype_components) for (component_id, archetype_component_id) in table_components {
{
components.insert( components.insert(
*component_id, component_id,
ArchetypeComponentInfo { ArchetypeComponentInfo {
storage_type: StorageType::Table, storage_type: StorageType::Table,
archetype_component_id, archetype_component_id,
@ -210,12 +205,9 @@ impl Archetype {
); );
} }
for (component_id, archetype_component_id) in sparse_set_components for (component_id, archetype_component_id) in sparse_set_components {
.iter()
.zip(sparse_set_archetype_components)
{
components.insert( components.insert(
*component_id, component_id,
ArchetypeComponentInfo { ArchetypeComponentInfo {
storage_type: StorageType::SparseSet, storage_type: StorageType::SparseSet,
archetype_component_id, archetype_component_id,
@ -225,10 +217,8 @@ impl Archetype {
Self { Self {
id, id,
table_id, table_id,
entities: Vec::new(),
components, components,
table_components, entities: Default::default(),
sparse_set_components,
edges: Default::default(), edges: Default::default(),
} }
} }
@ -249,13 +239,19 @@ impl Archetype {
} }
#[inline] #[inline]
pub fn table_components(&self) -> &[ComponentId] { pub fn table_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
&self.table_components self.components
.iter()
.filter(|(_, component)| component.storage_type == StorageType::Table)
.map(|(id, _)| *id)
} }
#[inline] #[inline]
pub fn sparse_set_components(&self) -> &[ComponentId] { pub fn sparse_set_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
&self.sparse_set_components self.components
.iter()
.filter(|(_, component)| component.storage_type == StorageType::SparseSet)
.map(|(id, _)| *id)
} }
#[inline] #[inline]
@ -484,38 +480,33 @@ impl Archetypes {
table_components: Vec<ComponentId>, table_components: Vec<ComponentId>,
sparse_set_components: Vec<ComponentId>, sparse_set_components: Vec<ComponentId>,
) -> ArchetypeId { ) -> ArchetypeId {
let table_components = table_components.into_boxed_slice();
let sparse_set_components = sparse_set_components.into_boxed_slice();
let archetype_identity = ArchetypeIdentity { let archetype_identity = ArchetypeIdentity {
sparse_set_components: sparse_set_components.clone(), sparse_set_components: sparse_set_components.clone().into_boxed_slice(),
table_components: table_components.clone(), table_components: table_components.clone().into_boxed_slice(),
}; };
let archetypes = &mut self.archetypes; let archetypes = &mut self.archetypes;
let archetype_component_count = &mut self.archetype_component_count; 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 *self
.archetype_ids .archetype_ids
.entry(archetype_identity) .entry(archetype_identity)
.or_insert_with(move || { .or_insert_with(move || {
let id = ArchetypeId(archetypes.len()); let id = ArchetypeId(archetypes.len());
let table_archetype_components = (0..table_components.len()) let table_start = *archetype_component_count;
.map(|_| next_archetype_component_id()) *archetype_component_count += table_components.len();
.collect(); let table_archetype_components =
let sparse_set_archetype_components = (0..sparse_set_components.len()) (table_start..*archetype_component_count).map(ArchetypeComponentId);
.map(|_| next_archetype_component_id()) let sparse_start = *archetype_component_count;
.collect(); *archetype_component_count += sparse_set_components.len();
let sparse_set_archetype_components =
(sparse_start..*archetype_component_count).map(ArchetypeComponentId);
archetypes.push(Archetype::new( archetypes.push(Archetype::new(
id, id,
table_id, table_id,
table_components, table_components.into_iter().zip(table_archetype_components),
sparse_set_components, sparse_set_components
table_archetype_components, .into_iter()
sparse_set_archetype_components, .zip(sparse_set_archetype_components),
)); ));
id id
}) })

View file

@ -461,7 +461,7 @@ impl BundleInfo {
table_components = if new_table_components.is_empty() { table_components = if new_table_components.is_empty() {
// if there are no new table components, we can keep using this table // if there are no new table components, we can keep using this table
table_id = current_archetype.table_id(); table_id = current_archetype.table_id();
current_archetype.table_components().to_vec() current_archetype.table_components().collect()
} else { } else {
new_table_components.extend(current_archetype.table_components()); new_table_components.extend(current_archetype.table_components());
// sort to ignore order while hashing // sort to ignore order while hashing
@ -477,7 +477,7 @@ impl BundleInfo {
}; };
sparse_set_components = if new_sparse_set_components.is_empty() { sparse_set_components = if new_sparse_set_components.is_empty() {
current_archetype.sparse_set_components().to_vec() current_archetype.sparse_set_components().collect()
} else { } else {
new_sparse_set_components.extend(current_archetype.sparse_set_components()); new_sparse_set_components.extend(current_archetype.sparse_set_components());
// sort to ignore order while hashing // sort to ignore order while hashing

View file

@ -505,7 +505,7 @@ impl<'w> EntityMut<'w> {
table_row = remove_result.table_row; table_row = remove_result.table_row;
for component_id in archetype.sparse_set_components() { 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); sparse_set.remove(self.entity);
} }
// SAFETY: table rows stored in archetypes always exist // SAFETY: table rows stored in archetypes always exist
@ -843,8 +843,8 @@ unsafe fn remove_bundle_from_archetype(
// components are already sorted // components are already sorted
removed_table_components.sort(); removed_table_components.sort();
removed_sparse_set_components.sort(); removed_sparse_set_components.sort();
next_table_components = current_archetype.table_components().to_vec(); next_table_components = current_archetype.table_components().collect();
next_sparse_set_components = current_archetype.sparse_set_components().to_vec(); next_sparse_set_components = current_archetype.sparse_set_components().collect();
sorted_remove(&mut next_table_components, &removed_table_components); sorted_remove(&mut next_table_components, &removed_table_components);
sorted_remove( sorted_remove(
&mut next_sparse_set_components, &mut next_sparse_set_components,