From 663eb9e18a32d2627c83a9b8c55ea4e0d2903405 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sun, 25 Aug 2024 11:00:23 +0800 Subject: [PATCH 1/2] remove reference-counting --- crates/core/block-entity/src/lib.rs | 2 +- crates/core/block/src/lib.rs | 12 ++-- crates/core/state/src/lib.rs | 84 ++++++++++------------ crates/core/world/src/chunk/section.rs | 4 +- crates/core/world/src/chunk/world_chunk.rs | 2 +- 5 files changed, 47 insertions(+), 57 deletions(-) diff --git a/crates/core/block-entity/src/lib.rs b/crates/core/block-entity/src/lib.rs index 0bd8491d..fca33a4f 100644 --- a/crates/core/block-entity/src/lib.rs +++ b/crates/core/block-entity/src/lib.rs @@ -236,7 +236,7 @@ where /// A trait for providing block entities. /// /// This should be implemented for [`ProvideBlockStateExtTy::BlockStateExt`]s. -pub trait ProvideBlockEntity<'w, Cx> +pub trait ProvideBlockEntity<'w, Cx>: 'w where Cx: ProvideBlockStateExtTy, { diff --git a/crates/core/block/src/lib.rs b/crates/core/block/src/lib.rs index c16af432..b8cfca78 100644 --- a/crates/core/block/src/lib.rs +++ b/crates/core/block/src/lib.rs @@ -5,7 +5,7 @@ use rimecraft_global_cx::{GlobalContext, ProvideIdTy}; use rimecraft_registry::{ProvideRegistry, Reg}; use rimecraft_state::{State, States, StatesMut}; -use std::{fmt::Debug, hash::Hash, marker::PhantomData, sync::Arc}; +use std::{fmt::Debug, hash::Hash, marker::PhantomData}; pub mod behave; @@ -123,7 +123,7 @@ where pub block: Block<'w, Cx>, /// The state. - pub state: Arc>, + pub state: &'w State<'w, Cx::BlockStateExt>, } impl BlockState<'_, Cx> @@ -134,7 +134,7 @@ where /// Returns the luminance level of this block state. #[inline] pub fn luminance(&self) -> u32 { - self.state.data().luminance(&self.state) + self.state.data().luminance(self.state) } } @@ -160,7 +160,7 @@ where fn clone(&self) -> Self { Self { block: self.block, - state: self.state.clone(), + state: self.state, } } } @@ -171,7 +171,7 @@ where { #[inline] fn eq(&self, other: &Self) -> bool { - self.block == other.block && Arc::ptr_eq(&self.state, &other.state) + self.block == other.block && std::ptr::eq(self.state, other.state) } } @@ -183,6 +183,6 @@ where { fn hash(&self, state: &mut H) { self.block.hash(state); - Arc::as_ptr(&self.state).hash(state); + (self.state as *const State<'_, Cx::BlockStateExt>).hash(state); } } diff --git a/crates/core/state/src/lib.rs b/crates/core/state/src/lib.rs index c7941b80..3b73396a 100644 --- a/crates/core/state/src/lib.rs +++ b/crates/core/state/src/lib.rs @@ -5,7 +5,8 @@ use std::{ collections::{BTreeMap, HashMap}, fmt::{Debug, Display}, - sync::{Arc, OnceLock, Weak}, + ptr::NonNull, + sync::OnceLock, }; use property::{BiIndex, ErasedProperty, Property, Wrap}; @@ -14,16 +15,13 @@ use property::{BiIndex, ErasedProperty, Property, Wrap}; use regex::Regex; #[cfg(not(feature = "regex"))] use regex_lite::Regex; -use rimecraft_maybe::Maybe; use crate::property::ErasedWrap; pub mod property; // -> < -> > -type Table<'a, T> = HashMap, HashMap>>; - -type MaybeArc<'a, T> = Maybe<'a, T, Arc>; +type Table<'a, T> = HashMap, HashMap>>; /// State of an object. pub struct State<'a, T> { @@ -54,7 +52,7 @@ impl State<'_, T> { /// /// - Panics if the target state was dropped. /// - Panics if this state is not fully initialized. - pub fn cycle(&self, prop: &Property<'_, W>) -> Result, Error> + pub fn cycle(&self, prop: &Property<'_, W>) -> Result<&Self, Error> where W: BiIndex, for<'w> &'w W: IntoIterator, @@ -69,10 +67,10 @@ impl State<'_, T> { .into_iter() .filter_map(|value| prop.wrap.index_of(&value)), ) else { - return Ok(MaybeArc::Borrowed(self)); + return Ok(self); }; if next == index { - Ok(MaybeArc::Borrowed(self)) + Ok(self) } else { self.table .get() @@ -80,7 +78,7 @@ impl State<'_, T> { .get(prop.name()) .ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned())) .and_then(|map| map.get(&next).ok_or(Error::ValueNotFound(index))) - .map(|weak| MaybeArc::Owned(weak.upgrade().expect("state was dropped"))) + .map(|ptr| unsafe { ptr.as_ref() }) } } @@ -95,7 +93,7 @@ impl State<'_, T> { /// /// - Panics if the target state was dropped. /// - Panics if this state is not fully initialized. - pub fn with(&self, prop: &Property<'_, W>, value: V) -> Result, Error> + pub fn with(&self, prop: &Property<'_, W>, value: V) -> Result<&Self, Error> where W: BiIndex, { @@ -105,7 +103,7 @@ impl State<'_, T> { .ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned()))?; let value = prop.wrap.index_of(&value).ok_or(Error::InvalidValue)?; if value == index { - Ok(MaybeArc::Borrowed(self)) + Ok(self) } else { self.table .get() @@ -113,7 +111,7 @@ impl State<'_, T> { .get(prop.name()) .ok_or_else(|| Error::PropertyNotFound(prop.name().to_owned())) .and_then(|map| map.get(&value).ok_or(Error::ValueNotFound(index))) - .map(|weak| MaybeArc::Owned(weak.upgrade().expect("state was dropped"))) + .map(|ptr| unsafe { ptr.as_ref() }) } } @@ -157,9 +155,8 @@ impl Debug for State<'_, T> { /// See [`StatesMut`] for creating a new instance. #[derive(Debug)] #[doc(alias = "StateManager")] -#[doc(alias = "StateDefinition")] pub struct States<'a, T> { - states: Vec>>, + states: Vec>>, #[allow(unused)] props: BTreeMap<&'a str, ErasedProperty<'a>>, } @@ -193,21 +190,24 @@ where .into_iter() .map(|vec| vec.into_iter().collect::>()) .map(|entries| { - Arc::new(State { + NonNull::new(Box::into_raw(Box::new(State { entries, table: OnceLock::new(), data: data.clone(), - }) + }))) + .expect("failed to allocate state") }) .collect::>(); // Initialize tables for state in list.iter() { + let state = unsafe { state.as_ref() }; let mut table: Table<'a, State<'a, T>> = Table::new(); for (prop, s_val) in state.entries.iter() { let mut row = HashMap::new(); for val in prop.wrap.erased_iter().filter(|v| v != s_val) { let Some(s) = list.iter().find(|s| { + let s = unsafe { s.as_ref() }; s.entries.iter().all(|(p, v)| { if p == prop { *v == val @@ -218,7 +218,7 @@ where }) else { continue; }; - row.insert(val, Arc::downgrade(s)); + row.insert(val, *s); } table.insert(prop.clone(), row); } @@ -235,7 +235,7 @@ where impl<'a, T> States<'a, T> { /// Gets all states of this state. #[inline] - pub fn states(&self) -> &[Arc>] { + pub fn states(&self) -> &[NonNull>] { &self.states } @@ -245,8 +245,8 @@ impl<'a, T> States<'a, T> { /// /// Panics if no state is available. #[inline] - pub fn default_state(&self) -> &Arc> { - self.states.first().expect("no state available") + pub fn default_state(&self) -> &State<'a, T> { + unsafe { self.states.first().expect("no state available").as_ref() } } /// Gets the length of states. @@ -262,6 +262,14 @@ impl<'a, T> States<'a, T> { } } +impl Drop for States<'_, T> { + fn drop(&mut self) { + for state in self.states.iter() { + drop(unsafe { Box::from_raw(state.as_ptr()) }); + } + } +} + /// Mutable instance of [`States`]. #[derive(Debug)] pub struct StatesMut<'a, T> { @@ -391,11 +399,8 @@ impl Display for Error { impl std::error::Error for Error {} -/// Serde support for `State`s. #[cfg(feature = "serde")] -pub mod serde { - use std::sync::Arc; - +mod _serde { use rimecraft_serde_update::Update; use serde::{ser::SerializeMap, Serialize}; @@ -422,21 +427,7 @@ pub mod serde { } } - /// Updatable state newtype wrapper. - #[derive(Debug)] - pub struct Upd<'a, T>(pub Arc>); - - impl Serialize for Upd<'_, T> { - #[inline] - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - self.0.serialize(serializer) - } - } - - impl<'de, T> Update<'de> for Upd<'_, T> { + impl<'de, T> Update<'de> for &State<'_, T> { #[inline] fn update( &mut self, @@ -445,10 +436,10 @@ pub mod serde { where D: serde::Deserializer<'de>, { - struct Visitor<'a, T>(Arc>); + struct Visitor<'a, T>(*const State<'a, T>); impl<'de, 'a, T> serde::de::Visitor<'de> for Visitor<'a, T> { - type Value = Arc>; + type Value = *const State<'a, T>; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { formatter.write_str("a map") @@ -459,8 +450,7 @@ pub mod serde { A: serde::de::MapAccess<'de>, { while let Some((prop, val)) = map.next_entry::()? { - self.0 = self - .0 + self.0 = unsafe { &*self.0 } .table .get() .expect("state not initialized") @@ -474,16 +464,16 @@ pub mod serde { "value {val} not found in property {prop}" )) })? - .upgrade() - .expect("state was dropped"); + .as_ptr() + .cast_const(); } Ok(self.0) } } deserializer - .deserialize_map(Visitor(self.0.clone())) - .map(|state| self.0 = state) + .deserialize_map(Visitor(*self)) + .map(|state| *self = unsafe { &*state }) } } } diff --git a/crates/core/world/src/chunk/section.rs b/crates/core/world/src/chunk/section.rs index d0c34e22..bef77f2b 100644 --- a/crates/core/world/src/chunk/section.rs +++ b/crates/core/world/src/chunk/section.rs @@ -235,13 +235,13 @@ where /// /// Panics if the biome registry doesn't contains a default entry. fn from(registry: &'w Registry) -> Self { - let default_block = Block::default(); + let default_block = Block::<'w, Cx>::default(); Self { bsc: PalettedContainer::of_single( Cx::state_ids(), BlockState { block: default_block, - state: default_block.states().default_state().clone(), + state: Block::into_inner(default_block).states().default_state(), }, ), bic: PalettedContainer::of_single( diff --git a/crates/core/world/src/chunk/world_chunk.rs b/crates/core/world/src/chunk/world_chunk.rs index dd79585c..5497d70b 100644 --- a/crates/core/world/src/chunk/world_chunk.rs +++ b/crates/core/world/src/chunk/world_chunk.rs @@ -410,7 +410,7 @@ where if bs .as_ref() - .map_or(false, |s| Arc::ptr_eq(&s.state, &state.state)) + .map_or(false, |s| std::ptr::eq(s.state, state.state)) { return None; } From 45811fc21eebc3e75a06212840dd34b1f6430e60 Mon Sep 17 00:00:00 2001 From: JieningYu Date: Sun, 25 Aug 2024 11:23:51 +0800 Subject: [PATCH 2/2] optimizations to several `erased_iter` contexts --- crates/core/state/Cargo.toml | 2 ++ crates/core/state/src/lib.rs | 19 +++++------ crates/core/state/src/property.rs | 53 ++++++++++++++++++++----------- 3 files changed, 46 insertions(+), 28 deletions(-) diff --git a/crates/core/state/Cargo.toml b/crates/core/state/Cargo.toml index 03df7316..8eb7acb2 100644 --- a/crates/core/state/Cargo.toml +++ b/crates/core/state/Cargo.toml @@ -17,6 +17,8 @@ regex-lite = "0.1" regex = { version = "1.10", optional = true } serde = { version = "1.0", optional = true } rimecraft-serde-update = { path = "../../util/serde-update", optional = true } +ahash = "0.8" +typeid = "1.0" [features] default = ["serde"] diff --git a/crates/core/state/src/lib.rs b/crates/core/state/src/lib.rs index 3b73396a..aa0d915c 100644 --- a/crates/core/state/src/lib.rs +++ b/crates/core/state/src/lib.rs @@ -3,13 +3,14 @@ //! This corresponds to `net.minecraft.state` in `yarn`. use std::{ - collections::{BTreeMap, HashMap}, + collections::BTreeMap, fmt::{Debug, Display}, ptr::NonNull, sync::OnceLock, }; -use property::{BiIndex, ErasedProperty, Property, Wrap}; +use ahash::AHashMap; +use property::{BiIndex, ErasedProperty, Property, UnObjSafeErasedWrap as _, Wrap}; #[cfg(feature = "regex")] use regex::Regex; @@ -21,11 +22,11 @@ use crate::property::ErasedWrap; pub mod property; // -> < -> > -type Table<'a, T> = HashMap, HashMap>>; +type Table<'a, T> = AHashMap, AHashMap>>; /// State of an object. pub struct State<'a, T> { - pub(crate) entries: HashMap, isize>, + pub(crate) entries: AHashMap, isize>, table: OnceLock>, data: T, } @@ -188,7 +189,7 @@ where } let list = iter .into_iter() - .map(|vec| vec.into_iter().collect::>()) + .map(|vec| vec.into_iter().collect::>()) .map(|entries| { NonNull::new(Box::into_raw(Box::new(State { entries, @@ -204,7 +205,7 @@ where let state = unsafe { state.as_ref() }; let mut table: Table<'a, State<'a, T>> = Table::new(); for (prop, s_val) in state.entries.iter() { - let mut row = HashMap::new(); + let mut row = AHashMap::new(); for val in prop.wrap.erased_iter().filter(|v| v != s_val) { let Some(s) = list.iter().find(|s| { let s = unsafe { s.as_ref() }; @@ -296,9 +297,9 @@ impl<'a, T> StatesMut<'a, T> { /// - Errors if the states contains duplicated properties. /// - Errors if any of the value name is invalid. #[allow(clippy::missing_panics_doc)] - pub fn add(&mut self, prop: &'a Property<'_, W>) -> Result<(), Error> + pub fn add<'p, W, G>(&mut self, prop: &'a Property<'p, W>) -> Result<(), Error> where - W: Wrap + BiIndex + Eq + Send + Sync + 'static, + W: Wrap + BiIndex + Eq + Send + Sync + 'p, for<'w> &'w W: IntoIterator, { static NAME_PAT: OnceLock = OnceLock::new(); @@ -307,7 +308,7 @@ impl<'a, T> StatesMut<'a, T> { return Err(Error::InvalidPropertyName(prop.name().to_owned())); } let mut len = 0; - for val in prop.wrap.erased_iter() { + for val in prop.wrap.erased_iter_typed() { len += 1; let name = prop.wrap.erased_to_name(val).expect("invalid value"); if !reg.is_match(&name) { diff --git a/crates/core/state/src/property.rs b/crates/core/state/src/property.rs index 6de079e3..168c2bed 100644 --- a/crates/core/state/src/property.rs +++ b/crates/core/state/src/property.rs @@ -13,7 +13,7 @@ use std::{ pub(crate) struct ErasedProperty<'a> { pub name: &'a str, pub ty: TypeId, - pub wrap: &'a (dyn ErasedWrap + Send + Sync), + pub wrap: &'a (dyn ErasedWrap + Send + Sync + 'a), } impl Debug for ErasedProperty<'_> { @@ -83,13 +83,13 @@ impl<'a, W> Property<'a, W> { impl<'a, 'p, W> From<&'a Property<'p, W>> for ErasedProperty<'a> where - W: ErasedWrap + Send + Sync + 'static, + W: ErasedWrap + Send + Sync + 'p, { #[inline] fn from(prop: &'a Property<'p, W>) -> Self { ErasedProperty { name: &prop.name, - ty: TypeId::of::(), + ty: typeid::of::(), wrap: &prop.wrap, } } @@ -111,27 +111,20 @@ pub(crate) trait ErasedWrap { #[allow(dead_code)] fn erased_parse_name(&self, name: &str) -> Option; fn erased_to_name(&self, index: isize) -> Option>; - fn erased_iter<'a>(&'a self) -> Box + 'a>; + fn erased_iter(&self) -> Box + '_>; } -impl ErasedWrap for T +pub(crate) trait UnObjSafeErasedWrap { + fn erased_iter_typed(&self) -> impl Iterator + '_; +} + +impl UnObjSafeErasedWrap for T where T: Wrap + BiIndex, for<'a> &'a T: IntoIterator, { #[inline] - fn erased_parse_name(&self, name: &str) -> Option { - self.parse_name(name) - .and_then(|value| self.index_of(&value)) - } - - #[inline] - fn erased_to_name(&self, index: isize) -> Option> { - BiIndex::index(self, index).and_then(|val| self.to_name(&val)) - } - - #[inline] - fn erased_iter<'a>(&'a self) -> Box + 'a> { + fn erased_iter_typed(&self) -> impl Iterator + '_ { struct Iter where I: IntoIterator, @@ -158,10 +151,32 @@ where } } - Box::new(Iter { + Iter { iter: self.into_iter(), wrap: self, - }) + } + } +} + +impl ErasedWrap for T +where + T: Wrap + BiIndex, + for<'a> &'a T: IntoIterator, +{ + #[inline] + fn erased_parse_name(&self, name: &str) -> Option { + self.parse_name(name) + .and_then(|value| self.index_of(&value)) + } + + #[inline] + fn erased_to_name(&self, index: isize) -> Option> { + BiIndex::index(self, index).and_then(|val| self.to_name(&val)) + } + + #[inline] + fn erased_iter(&self) -> Box + '_> { + Box::new(self.erased_iter_typed()) } }