Skip to content

Commit 0f0b8b6

Browse files
committed
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.
1 parent 51aab03 commit 0f0b8b6

File tree

3 files changed

+39
-48
lines changed

3 files changed

+39
-48
lines changed

crates/bevy_ecs/src/archetype.rs

+34-43
Original file line numberDiff line numberDiff line change
@@ -182,40 +182,32 @@ pub struct Archetype {
182182
table_id: TableId,
183183
edges: Edges,
184184
entities: Vec<ArchetypeEntity>,
185-
table_components: Box<[ComponentId]>,
186-
sparse_set_components: Box<[ComponentId]>,
187185
components: SparseSet<ComponentId, ArchetypeComponentInfo>,
188186
}
189187

190188
impl Archetype {
191189
pub fn new(
192190
id: ArchetypeId,
193191
table_id: TableId,
194-
table_components: Box<[ComponentId]>,
195-
sparse_set_components: Box<[ComponentId]>,
196-
table_archetype_components: Vec<ArchetypeComponentId>,
197-
sparse_set_archetype_components: Vec<ArchetypeComponentId>,
192+
table_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
193+
sparse_set_components: impl Iterator<Item = (ComponentId, ArchetypeComponentId)>,
198194
) -> Self {
199-
let mut components =
200-
SparseSet::with_capacity(table_components.len() + sparse_set_components.len());
201-
for (component_id, archetype_component_id) in
202-
table_components.iter().zip(table_archetype_components)
203-
{
195+
let (min_table, _) = table_components.size_hint();
196+
let (min_sparse, _) = sparse_set_components.size_hint();
197+
let mut components = SparseSet::with_capacity(min_table + min_sparse);
198+
for (component_id, archetype_component_id) in table_components {
204199
components.insert(
205-
*component_id,
200+
component_id,
206201
ArchetypeComponentInfo {
207202
storage_type: StorageType::Table,
208203
archetype_component_id,
209204
},
210205
);
211206
}
212207

213-
for (component_id, archetype_component_id) in sparse_set_components
214-
.iter()
215-
.zip(sparse_set_archetype_components)
216-
{
208+
for (component_id, archetype_component_id) in sparse_set_components {
217209
components.insert(
218-
*component_id,
210+
component_id,
219211
ArchetypeComponentInfo {
220212
storage_type: StorageType::SparseSet,
221213
archetype_component_id,
@@ -225,10 +217,8 @@ impl Archetype {
225217
Self {
226218
id,
227219
table_id,
228-
entities: Vec::new(),
229220
components,
230-
table_components,
231-
sparse_set_components,
221+
entities: Default::default(),
232222
edges: Default::default(),
233223
}
234224
}
@@ -249,13 +239,19 @@ impl Archetype {
249239
}
250240

251241
#[inline]
252-
pub fn table_components(&self) -> &[ComponentId] {
253-
&self.table_components
242+
pub fn table_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
243+
self.components
244+
.iter()
245+
.filter(|(_, component)| component.storage_type == StorageType::Table)
246+
.map(|(id, _)| *id)
254247
}
255248

256249
#[inline]
257-
pub fn sparse_set_components(&self) -> &[ComponentId] {
258-
&self.sparse_set_components
250+
pub fn sparse_set_components(&self) -> impl Iterator<Item = ComponentId> + '_ {
251+
self.components
252+
.iter()
253+
.filter(|(_, component)| component.storage_type == StorageType::SparseSet)
254+
.map(|(id, _)| *id)
259255
}
260256

261257
#[inline]
@@ -484,38 +480,33 @@ impl Archetypes {
484480
table_components: Vec<ComponentId>,
485481
sparse_set_components: Vec<ComponentId>,
486482
) -> ArchetypeId {
487-
let table_components = table_components.into_boxed_slice();
488-
let sparse_set_components = sparse_set_components.into_boxed_slice();
489483
let archetype_identity = ArchetypeIdentity {
490-
sparse_set_components: sparse_set_components.clone(),
491-
table_components: table_components.clone(),
484+
sparse_set_components: sparse_set_components.clone().into_boxed_slice(),
485+
table_components: table_components.clone().into_boxed_slice(),
492486
};
493487

494488
let archetypes = &mut self.archetypes;
495489
let archetype_component_count = &mut self.archetype_component_count;
496-
let mut next_archetype_component_id = move || {
497-
let id = ArchetypeComponentId(*archetype_component_count);
498-
*archetype_component_count += 1;
499-
id
500-
};
501490
*self
502491
.archetype_ids
503492
.entry(archetype_identity)
504493
.or_insert_with(move || {
505494
let id = ArchetypeId(archetypes.len());
506-
let table_archetype_components = (0..table_components.len())
507-
.map(|_| next_archetype_component_id())
508-
.collect();
509-
let sparse_set_archetype_components = (0..sparse_set_components.len())
510-
.map(|_| next_archetype_component_id())
511-
.collect();
495+
let table_start = *archetype_component_count;
496+
*archetype_component_count += table_components.len();
497+
let table_archetype_components =
498+
(table_start..*archetype_component_count).map(ArchetypeComponentId);
499+
let sparse_start = *archetype_component_count;
500+
*archetype_component_count += sparse_set_components.len();
501+
let sparse_set_archetype_components =
502+
(sparse_start..*archetype_component_count).map(ArchetypeComponentId);
512503
archetypes.push(Archetype::new(
513504
id,
514505
table_id,
515-
table_components,
516-
sparse_set_components,
517-
table_archetype_components,
518-
sparse_set_archetype_components,
506+
table_components.into_iter().zip(table_archetype_components),
507+
sparse_set_components
508+
.into_iter()
509+
.zip(sparse_set_archetype_components),
519510
));
520511
id
521512
})

crates/bevy_ecs/src/bundle.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ impl BundleInfo {
461461
table_components = if new_table_components.is_empty() {
462462
// if there are no new table components, we can keep using this table
463463
table_id = current_archetype.table_id();
464-
current_archetype.table_components().to_vec()
464+
current_archetype.table_components().collect()
465465
} else {
466466
new_table_components.extend(current_archetype.table_components());
467467
// sort to ignore order while hashing
@@ -477,7 +477,7 @@ impl BundleInfo {
477477
};
478478

479479
sparse_set_components = if new_sparse_set_components.is_empty() {
480-
current_archetype.sparse_set_components().to_vec()
480+
current_archetype.sparse_set_components().collect()
481481
} else {
482482
new_sparse_set_components.extend(current_archetype.sparse_set_components());
483483
// sort to ignore order while hashing

crates/bevy_ecs/src/world/entity_ref.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ impl<'w> EntityMut<'w> {
505505
table_row = remove_result.table_row;
506506

507507
for component_id in archetype.sparse_set_components() {
508-
let sparse_set = world.storages.sparse_sets.get_mut(*component_id).unwrap();
508+
let sparse_set = world.storages.sparse_sets.get_mut(component_id).unwrap();
509509
sparse_set.remove(self.entity);
510510
}
511511
// SAFETY: table rows stored in archetypes always exist
@@ -843,8 +843,8 @@ unsafe fn remove_bundle_from_archetype(
843843
// components are already sorted
844844
removed_table_components.sort();
845845
removed_sparse_set_components.sort();
846-
next_table_components = current_archetype.table_components().to_vec();
847-
next_sparse_set_components = current_archetype.sparse_set_components().to_vec();
846+
next_table_components = current_archetype.table_components().collect();
847+
next_sparse_set_components = current_archetype.sparse_set_components().collect();
848848
sorted_remove(&mut next_table_components, &removed_table_components);
849849
sorted_remove(
850850
&mut next_sparse_set_components,

0 commit comments

Comments
 (0)