
# Objective - fix #12853 - Make `Table::allocate` faster ## Solution The PR consists of multiple steps: 1) For the component data: create a new data-structure that's similar to `BlobVec` but doesn't store `len` & `capacity` inside of it: "BlobArray" (name suggestions welcome) 2) For the `Tick` data: create a new data-structure that's similar to `ThinSlicePtr` but supports dynamic reallocation: "ThinArrayPtr" (name suggestions welcome) 3) Create a new data-structure that's very similar to `Column` that doesn't store `len` & `capacity` inside of it: "ThinColumn" 4) Adjust the `Table` implementation to use `ThinColumn` instead of `Column` The result is that only one set of `len` & `capacity` is stored in `Table`, in `Table::entities` ### Notes Regarding Performance Apart from shaving off some excess memory in `Table`, the changes have also brought noteworthy performance improvements: The previous implementation relied on `Vec::reserve` & `BlobVec::reserve`, but that redundantly repeated the same if statement (`capacity` == `len`). Now that check could be made at the `Table` level because the capacity and length of all the columns are synchronized; saving N branches per allocation. The result is a respectable performance improvement per every `Table::reserve` (and subsequently `Table::allocate`) call. I'm hesitant to give exact numbers because I don't have a lot of experience in profiling and benchmarking, but these are the results I got so far: *`add_remove_big/table` benchmark after the implementation:*  *`add_remove_big/table` benchmark in main branch (measured in comparison to the implementation):*  *`add_remove_very_big/table` benchmark after the implementation:*  *`add_remove_very_big/table` benchmark in main branch (measured in comparison to the implementation):*  cc @james7132 to verify --- ## Changelog - New data-structure that's similar to `BlobVec` but doesn't store `len` & `capacity` inside of it: `BlobArray` - New data-structure that's similar to `ThinSlicePtr` but supports dynamic allocation:`ThinArrayPtr` - New data-structure that's very similar to `Column` that doesn't store `len` & `capacity` inside of it: `ThinColumn` - Adjust the `Table` implementation to use `ThinColumn` instead of `Column` - New benchmark: `add_remove_very_big` to benchmark the performance of spawning a lot of entities with a lot of components (15) each ## Migration Guide `Table` now uses `ThinColumn` instead of `Column`. That means that methods that previously returned `Column`, will now return `ThinColumn` instead. `ThinColumn` has a much more limited and low-level API, but you can still achieve the same things in `ThinColumn` as you did in `Column`. For example, instead of calling `Column::get_added_tick`, you'd call `ThinColumn::get_added_ticks_slice` and index it to get the specific added tick. --------- Co-authored-by: James Liu <contact@jamessliu.com>
46 lines
1.7 KiB
Rust
46 lines
1.7 KiB
Rust
//! Storage layouts for ECS data.
|
|
//!
|
|
//! This module implements the low-level collections that store data in a [`World`]. These all offer minimal and often
|
|
//! unsafe APIs, and have been made `pub` primarily for debugging and monitoring purposes.
|
|
//!
|
|
//! # Fetching Storages
|
|
//! Each of the below data stores can be fetched via [`Storages`], which can be fetched from a
|
|
//! [`World`] via [`World::storages`]. It exposes a top level container for each class of storage within
|
|
//! ECS:
|
|
//!
|
|
//! - [`Tables`] - columnar contiguous blocks of memory, optimized for fast iteration.
|
|
//! - [`SparseSets`] - sparse `HashMap`-like mappings from entities to components, optimized for random
|
|
//! lookup and regular insertion/removal of components.
|
|
//! - [`Resources`] - singleton storage for the resources in the world
|
|
//!
|
|
//! # Safety
|
|
//! To avoid trivially unsound use of the APIs in this module, it is explicitly impossible to get a mutable
|
|
//! reference to [`Storages`] from [`World`], and none of the types publicly expose a mutable interface.
|
|
//!
|
|
//! [`World`]: crate::world::World
|
|
//! [`World::storages`]: crate::world::World::storages
|
|
|
|
mod blob_array;
|
|
mod blob_vec;
|
|
mod resource;
|
|
mod sparse_set;
|
|
mod table;
|
|
mod thin_array_ptr;
|
|
|
|
pub use resource::*;
|
|
pub use sparse_set::*;
|
|
pub use table::*;
|
|
|
|
/// The raw data stores of a [`World`](crate::world::World)
|
|
#[derive(Default)]
|
|
pub struct Storages {
|
|
/// Backing storage for [`SparseSet`] components.
|
|
pub sparse_sets: SparseSets,
|
|
/// Backing storage for [`Table`] components.
|
|
pub tables: Tables,
|
|
/// Backing storage for resources.
|
|
pub resources: Resources<true>,
|
|
/// Backing storage for `!Send` resources.
|
|
pub non_send_resources: Resources<false>,
|
|
}
|