From ca3c3dd5e6521eedea18cc0503a6ae51334e797d Mon Sep 17 00:00:00 2001 From: lisk77 Date: Sat, 8 Mar 2025 14:55:23 +0100 Subject: [PATCH] feat: added pagination to the `SparseSet` to make it more memory efficient --- crates/comet_structs/src/column.rs | 6 +- crates/comet_structs/src/component_storage.rs | 2 +- crates/comet_structs/src/sparse_set.rs | 78 +++++++++++++------ 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/crates/comet_structs/src/column.rs b/crates/comet_structs/src/column.rs index 807571e..3ba016b 100644 --- a/crates/comet_structs/src/column.rs +++ b/crates/comet_structs/src/column.rs @@ -5,7 +5,6 @@ use std::{ }, any::TypeId, hash::{ - DefaultHasher, Hash, Hasher }, @@ -14,6 +13,9 @@ use std::{ }; use std::ptr; +// The following two structs are just blatantly stolen from Bevy - another Rust game engine. +// I just need them for the ComponentStorage system, and I was too lazy to write them myself. + #[derive(Debug, Clone)] pub struct BlobVec { item_layout: Layout, @@ -24,7 +26,6 @@ pub struct BlobVec { drop: unsafe fn(*mut u8) } - impl BlobVec { pub fn new(item_layout: Layout, drop: unsafe fn(*mut u8), capacity: usize) -> Self { if item_layout.size() == 0 { @@ -84,7 +85,6 @@ impl BlobVec { self.capacity = new_capacity; } - #[inline] pub fn len(&self) -> usize { self.len diff --git a/crates/comet_structs/src/component_storage.rs b/crates/comet_structs/src/component_storage.rs index 99371e8..86ce173 100644 --- a/crates/comet_structs/src/component_storage.rs +++ b/crates/comet_structs/src/component_storage.rs @@ -8,7 +8,7 @@ impl ComponentStorage { pub fn register_component(&mut self, capacity: usize) { if !self.contains(&TypeId::of::()) { - self.insert(TypeId::of::(), SparseSet::new::(capacity)); + self.insert(TypeId::of::(), SparseSet::new::(capacity, 1000)); } else { error!("Component {:?} already exists", TypeId::of::()); diff --git a/crates/comet_structs/src/sparse_set.rs b/crates/comet_structs/src/sparse_set.rs index 4c39fac..0e6eb11 100644 --- a/crates/comet_structs/src/sparse_set.rs +++ b/crates/comet_structs/src/sparse_set.rs @@ -5,55 +5,83 @@ use std::hash::{ #[derive(Debug, Clone)] pub struct SparseSet { - sparse: Vec>, + sparse: Vec>>>, dense: Column, + page_size: usize } impl SparseSet { - pub fn new(capacity: usize) -> Self { + pub fn new(capacity: usize, page_size: usize) -> Self { Self { sparse: Vec::new(), dense: Column::new::(capacity), + page_size } } pub fn insert(&mut self, index: usize, value: T) { - if index >= self.sparse.len() { - self.sparse.resize(index + 1, None); + let page = index / self.page_size; + + if page >= self.sparse.len() { + self.sparse.resize(page + 1, None); } - self.sparse[index] = Some(self.dense.data.len()); + + if self.sparse[page].is_none() { + self.sparse[page] = Some(vec![None; self.page_size]); + } + + if let Some(page_vec) = &mut self.sparse[page] { + page_vec[index % self.page_size] = Some(self.dense.data.len()); + } + self.dense.push(value); } - pub fn remove(&mut self, index: usize) -> Option{ - if let Some(sparse_index) = self.sparse.get(index).and_then(|x| x.as_ref()) { - let dense_index = *sparse_index; - let last_index = self.dense.data.len() - 1; - if dense_index != last_index { - self.dense.swap(dense_index, last_index); - if let Some(sparse) = self.sparse.get_mut(last_index) { - *sparse = Some(dense_index); + pub fn remove(&mut self, index: usize) -> Option { + if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) { + if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) { + let dense_index = *sparse_index; + let last_index = self.dense.data.len() - 1; + if dense_index != last_index { + self.dense.swap(dense_index, last_index); + if let Some(page_vec) = self.sparse.get_mut(last_index / self.page_size).and_then(|x| x.as_mut()) { + page_vec[last_index % self.page_size] = Some(dense_index); + } } + if let Some(page_vec) = self.sparse.get_mut(index / self.page_size).and_then(|x| x.as_mut()) { + page_vec[index % self.page_size] = None; + } + return self.dense.remove::(last_index); + } + } + None + } + + pub fn get(&self, index: usize) -> Option<&T> { + if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) { + if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) { + self.dense.get::(*sparse_index) + } + else { + None } - self.sparse[index] = None; - self.dense.remove::(last_index) } else { None } } - pub fn get(&self, index: usize) -> Option<&T> { - match self.sparse.get(index).and_then(|x| x.as_ref()) { - Some(sparse_index) => self.dense.get::(*sparse_index), - None => None, - } - } - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { - match self.sparse.get(index).and_then(|x| x.as_ref()) { - Some(sparse_index) => self.dense.get_mut::(*sparse_index), - None => None, + if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) { + if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) { + self.dense.get_mut::(*sparse_index) + } + else { + None + } + } + else { + None } } } \ No newline at end of file