From 8da430f4c95f8e31aef00863a34531377e23f814 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 3 Feb 2026 17:30:27 -0800 Subject: [PATCH] speed up LazyInterned by redoing caching using RwLock and add a thread-local cache --- crates/fayalite/src/array.rs | 32 +++- crates/fayalite/src/intern.rs | 244 +++++++++++++++---------- crates/fayalite/src/intern/type_map.rs | 4 +- crates/fayalite/src/phantom_const.rs | 16 +- 4 files changed, 189 insertions(+), 107 deletions(-) diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 4e2b223..6ca6809 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -109,14 +109,42 @@ impl Default for ArrayType { } } +struct MakeType(Interned); + +impl From> for Interned { + fn from(value: MakeType) -> Self { + value.0 + } +} + +impl Default for MakeType { + fn default() -> Self { + Self(T::TYPE.intern_sized()) + } +} + +struct MakeMaskType(Interned); + +impl From> for Interned { + fn from(value: MakeMaskType) -> Self { + value.0 + } +} + +impl Default for MakeMaskType { + fn default() -> Self { + Self(T::MASK_TYPE.intern_sized()) + } +} + impl StaticType for ArrayType { const TYPE: Self = Self { - element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()), + element: LazyInterned::new_const::>(), len: Len::SIZE, type_properties: Self::TYPE_PROPERTIES, }; const MASK_TYPE: Self::MaskType = ArrayType:: { - element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()), + element: LazyInterned::new_const::>(), len: Len::SIZE, type_properties: Self::MASK_TYPE_PROPERTIES, }; diff --git a/crates/fayalite/src/intern.rs b/crates/fayalite/src/intern.rs index 9ffd251..b78aa59 100644 --- a/crates/fayalite/src/intern.rs +++ b/crates/fayalite/src/intern.rs @@ -9,12 +9,12 @@ use serde::{Deserialize, Serialize}; use std::{ any::{Any, TypeId}, borrow::{Borrow, Cow}, + cell::RefCell, cmp::Ordering, ffi::{OsStr, OsString}, fmt, hash::{BuildHasher, Hash, Hasher}, iter::FusedIterator, - marker::PhantomData, ops::Deref, path::{Path, PathBuf}, sync::RwLock, @@ -23,51 +23,172 @@ use std::{ mod interner; mod type_map; -pub trait LazyInternedTrait: Send + Sync + Any { - fn get(&self) -> Interned; +/// invariant: T must be zero-sized, `type_id` is unique for every possible T value. +struct LazyInternedLazyInner { + type_id: TypeId, + value: T, } -impl Interned + Send + Sync + Any> - LazyInternedTrait for F -{ - fn get(&self) -> Interned { - self() +impl Hash for LazyInternedLazyInner { + fn hash(&self, state: &mut H) { + let Self { type_id, value: _ } = self; + type_id.hash(state); } } -#[repr(transparent)] -pub struct LazyInternedFn(pub &'static dyn LazyInternedTrait); +impl PartialEq for LazyInternedLazyInner { + fn eq(&self, other: &Self) -> bool { + let Self { type_id, value: _ } = self; + *type_id == other.type_id + } +} -impl Copy for LazyInternedFn {} +impl Eq for LazyInternedLazyInner {} -impl Clone for LazyInternedFn { +impl fmt::Debug for LazyInternedLazyInner { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("LazyInternedLazyInner") + .finish_non_exhaustive() + } +} + +impl LazyInternedLazyInner { + const fn new(value: T) -> Self + where + T: Sized, + { + const { assert!(size_of::() == 0) }; + Self { + type_id: TypeId::of::(), + value, + } + } +} + +pub struct LazyInternedLazy( + &'static LazyInternedLazyInner Interned + Send + Sync>, +); + +impl LazyInternedLazy { + pub const fn new_const>>() -> Self { + Self(&const { LazyInternedLazyInner::new(|| V::default().into()) }) + } + pub const fn new_const_default() -> Self + where + Interned: Default, + { + Self::new_const::>() + } + pub fn interned(self) -> Interned { + struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>); + impl Map { + const EMPTY: Self = Self(hashbrown::HashTable::new()); + fn get( + &self, + lazy_interned_lazy: LazyInternedLazy, + hash: u64, + ) -> Option<&'static Interned> { + let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?; + let Some(retval) = v.downcast_ref::>() else { + unreachable!(); + }; + Some(retval) + } + fn get_or_insert( + &mut self, + lazy_interned_lazy: LazyInternedLazy, + hash: u64, + v: &'static Interned, + ) -> &'static Interned { + let entry = self + .0 + .entry( + hash, + |&(k, _)| k == lazy_interned_lazy.0.type_id, + |&(k, _)| type_map::TypeIdBuildHasher.hash_one(k), + ) + .or_insert_with(|| (lazy_interned_lazy.0.type_id, v)); + let &(_, v) = entry.get(); + let Some(retval) = v.downcast_ref::>() else { + unreachable!(); + }; + retval + } + } + static GLOBAL_CACHE: RwLock = RwLock::new(Map::EMPTY); + #[cold] + fn insert_in_thread_local_cache( + cache: &RefCell, + this: LazyInternedLazy, + hash: u64, + ) -> Interned { + let read_lock = GLOBAL_CACHE.read().unwrap(); + let v = read_lock.get(this, hash); + drop(read_lock); + let v = v.unwrap_or_else(|| { + let v = Box::leak(Box::new((this.0.value)())); + GLOBAL_CACHE.write().unwrap().get_or_insert(this, hash, v) + }); + *cache.borrow_mut().get_or_insert(this, hash, v) + } + thread_local! { + static THREAD_LOCAL_CACHE: RefCell = const { RefCell::new(Map::EMPTY) }; + } + let hash = type_map::TypeIdBuildHasher.hash_one(self.0.type_id); + THREAD_LOCAL_CACHE.with(|cache| { + let borrow = cache.borrow(); + if let Some(v) = borrow.get(self, hash) { + *v + } else { + drop(borrow); + insert_in_thread_local_cache(cache, self, hash) + } + }) + } +} + +impl Copy for LazyInternedLazy {} + +impl Clone for LazyInternedLazy { fn clone(&self) -> Self { *self } } -impl Hash for LazyInternedFn { +impl Hash for LazyInternedLazy { fn hash(&self, state: &mut H) { - self.0.get_ptr_eq_with_type_id().hash(state); + self.0.hash(state); } } -impl Eq for LazyInternedFn {} +impl Eq for LazyInternedLazy {} -impl PartialEq for LazyInternedFn { +impl PartialEq for LazyInternedLazy { fn eq(&self, other: &Self) -> bool { - self.0.get_ptr_eq_with_type_id() == other.0.get_ptr_eq_with_type_id() + self.0 == other.0 } } pub enum LazyInterned { Interned(Interned), - Lazy(LazyInternedFn), + Lazy(LazyInternedLazy), } impl LazyInterned { - pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { - Self::Lazy(LazyInternedFn(v)) + pub const fn new_const>>() -> Self { + Self::Lazy(LazyInternedLazy::new_const::()) + } + pub const fn new_const_default() -> Self + where + Interned: Default, + { + Self::new_const::>() + } + pub fn interned(self) -> Interned { + match self { + Self::Interned(retval) => retval, + Self::Lazy(retval) => retval.interned(), + } } } @@ -79,7 +200,7 @@ impl Clone for LazyInterned { impl Copy for LazyInterned {} -impl Deref for LazyInterned { +impl Deref for LazyInterned { type Target = T; fn deref(&self) -> &Self::Target { @@ -87,9 +208,9 @@ impl Deref for LazyInterned { } } -impl Eq for LazyInterned where Interned: Eq {} +impl Eq for LazyInterned where Interned: Eq {} -impl PartialEq for LazyInterned +impl PartialEq for LazyInterned where Interned: PartialEq, { @@ -98,7 +219,7 @@ where } } -impl Ord for LazyInterned +impl Ord for LazyInterned where Interned: Ord, { @@ -107,7 +228,7 @@ where } } -impl PartialOrd for LazyInterned +impl PartialOrd for LazyInterned where Interned: PartialOrd, { @@ -116,7 +237,7 @@ where } } -impl Hash for LazyInterned +impl Hash for LazyInterned where Interned: Hash, { @@ -125,77 +246,6 @@ where } } -impl LazyInterned { - pub fn interned(self) -> Interned - where - T: Intern, - { - struct MemoizeInterned(PhantomData); - - impl Hash for MemoizeInterned { - fn hash(&self, _state: &mut H) {} - } - - impl PartialEq for MemoizeInterned { - fn eq(&self, _other: &Self) -> bool { - true - } - } - - impl Eq for MemoizeInterned {} - - impl Clone for MemoizeInterned { - fn clone(&self) -> Self { - *self - } - } - - impl Copy for MemoizeInterned {} - - impl MemoizeGeneric for MemoizeInterned { - type InputRef<'a> = LazyInternedFn; - - type InputOwned = LazyInternedFn; - - type InputCow<'a> = LazyInternedFn; - - type Output = Interned; - - fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool { - a == b - } - - fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { - *input - } - - fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned { - input - } - - fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> { - *input - } - - fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> { - input - } - - fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> { - input - } - - fn inner(self, input: Self::InputRef<'_>) -> Self::Output { - input.0.get() - } - } - match self { - Self::Interned(retval) => retval, - Self::Lazy(retval) => MemoizeInterned(PhantomData).get(retval), - } - } -} - pub trait InternedCompare { type InternedCompareKey: Ord + Hash; fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey; diff --git a/crates/fayalite/src/intern/type_map.rs b/crates/fayalite/src/intern/type_map.rs index 03b68e3..e31a5bf 100644 --- a/crates/fayalite/src/intern/type_map.rs +++ b/crates/fayalite/src/intern/type_map.rs @@ -6,7 +6,7 @@ use std::{ sync::RwLock, }; -struct TypeIdHasher(u64); +pub(crate) struct TypeIdHasher(u64); // assumes TypeId has at least 64 bits that is a good hash impl Hasher for TypeIdHasher { @@ -63,7 +63,7 @@ impl Hasher for TypeIdHasher { } } -struct TypeIdBuildHasher; +pub(crate) struct TypeIdBuildHasher; impl BuildHasher for TypeIdBuildHasher { type Hasher = TypeIdHasher; diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index 32e9d6b..fb7be6f 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -4,7 +4,7 @@ use crate::{ expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType}, int::Bool, - intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize}, + intern::{Intern, Interned, InternedCompare, LazyInterned, Memoize}, sim::value::{SimValue, ToSimValue, ToSimValueWithType}, source_location::SourceLocation, ty::{ @@ -240,11 +240,17 @@ impl PhantomConst { { Self::new_interned(value.intern_deref()) } - pub const fn new_lazy(v: &'static dyn LazyInternedTrait) -> Self { + pub const fn new_const>>() -> Self { Self { - value: LazyInterned::new_lazy(v), + value: LazyInterned::new_const::(), } } + pub const fn new_const_default() -> Self + where + Interned: Default, + { + Self::new_const::>() + } pub fn get(self) -> Interned { self.value.interned() } @@ -334,9 +340,7 @@ impl StaticType for PhantomConst where Interned: Default, { - const TYPE: Self = PhantomConst { - value: LazyInterned::new_lazy(&Interned::::default), - }; + const TYPE: Self = Self::new_const_default(); const MASK_TYPE: Self::MaskType = (); const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES; const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;