speed up LazyInterned by redoing caching using RwLock and add a thread-local cache
Some checks failed
/ test (pull_request) Failing after 1m56s

This commit is contained in:
Jacob Lifshay 2026-02-03 17:30:27 -08:00
parent caa097db0b
commit 8da430f4c9
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
4 changed files with 189 additions and 107 deletions

View file

@ -109,14 +109,42 @@ impl<T: StaticType, Len: KnownSize> Default for ArrayType<T, Len> {
}
}
struct MakeType<T: StaticType>(Interned<T>);
impl<T: StaticType> From<MakeType<T>> for Interned<T> {
fn from(value: MakeType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeType<T> {
fn default() -> Self {
Self(T::TYPE.intern_sized())
}
}
struct MakeMaskType<T: StaticType>(Interned<T::MaskType>);
impl<T: StaticType> From<MakeMaskType<T>> for Interned<T::MaskType> {
fn from(value: MakeMaskType<T>) -> Self {
value.0
}
}
impl<T: StaticType> Default for MakeMaskType<T> {
fn default() -> Self {
Self(T::MASK_TYPE.intern_sized())
}
}
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
const TYPE: Self = Self {
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
element: LazyInterned::new_const::<MakeType<T>>(),
len: Len::SIZE,
type_properties: Self::TYPE_PROPERTIES,
};
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
element: LazyInterned::new_const::<MakeMaskType<T>>(),
len: Len::SIZE,
type_properties: Self::MASK_TYPE_PROPERTIES,
};

View file

@ -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<T: ?Sized + Send + Sync + 'static>: Send + Sync + Any {
fn get(&self) -> Interned<T>;
/// invariant: T must be zero-sized, `type_id` is unique for every possible T value.
struct LazyInternedLazyInner<T: ?Sized + 'static> {
type_id: TypeId,
value: T,
}
impl<T: ?Sized + Send + Sync + 'static, F: ?Sized + Fn() -> Interned<T> + Send + Sync + Any>
LazyInternedTrait<T> for F
{
fn get(&self) -> Interned<T> {
self()
impl<T: ?Sized + 'static> Hash for LazyInternedLazyInner<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
let Self { type_id, value: _ } = self;
type_id.hash(state);
}
}
#[repr(transparent)]
pub struct LazyInternedFn<T: ?Sized + Send + Sync + 'static>(pub &'static dyn LazyInternedTrait<T>);
impl<T: ?Sized + 'static> PartialEq for LazyInternedLazyInner<T> {
fn eq(&self, other: &Self) -> bool {
let Self { type_id, value: _ } = self;
*type_id == other.type_id
}
}
impl<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedFn<T> {}
impl<T: ?Sized + 'static> Eq for LazyInternedLazyInner<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedFn<T> {
impl<T: ?Sized + 'static> fmt::Debug for LazyInternedLazyInner<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("LazyInternedLazyInner")
.finish_non_exhaustive()
}
}
impl<T: ?Sized + 'static> LazyInternedLazyInner<T> {
const fn new(value: T) -> Self
where
T: Sized,
{
const { assert!(size_of::<T>() == 0) };
Self {
type_id: TypeId::of::<T>(),
value,
}
}
}
pub struct LazyInternedLazy<T: ?Sized + Send + Sync + 'static>(
&'static LazyInternedLazyInner<dyn Fn() -> Interned<T> + Send + Sync>,
);
impl<T: ?Sized + Send + Sync + 'static> LazyInternedLazy<T> {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self(&const { LazyInternedLazyInner::new(|| V::default().into()) })
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
struct Map(hashbrown::HashTable<(TypeId, &'static (dyn Any + Send + Sync))>);
impl Map {
const EMPTY: Self = Self(hashbrown::HashTable::new());
fn get<T: ?Sized + Send + Sync + 'static>(
&self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
) -> Option<&'static Interned<T>> {
let &(_, v) = self.0.find(hash, |v| v.0 == lazy_interned_lazy.0.type_id)?;
let Some(retval) = v.downcast_ref::<Interned<T>>() else {
unreachable!();
};
Some(retval)
}
fn get_or_insert<T: ?Sized + Send + Sync + 'static>(
&mut self,
lazy_interned_lazy: LazyInternedLazy<T>,
hash: u64,
v: &'static Interned<T>,
) -> &'static Interned<T> {
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::<Interned<T>>() else {
unreachable!();
};
retval
}
}
static GLOBAL_CACHE: RwLock<Map> = RwLock::new(Map::EMPTY);
#[cold]
fn insert_in_thread_local_cache<T: ?Sized + Send + Sync + 'static>(
cache: &RefCell<Map>,
this: LazyInternedLazy<T>,
hash: u64,
) -> Interned<T> {
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<Map> = 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<T: ?Sized + Send + Sync + 'static> Copy for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> Clone for LazyInternedLazy<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedFn<T> {
impl<T: ?Sized + Send + Sync + 'static> Hash for LazyInternedLazy<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.get_ptr_eq_with_type_id().hash(state);
self.0.hash(state);
}
}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedFn<T> {}
impl<T: ?Sized + Send + Sync + 'static> Eq for LazyInternedLazy<T> {}
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedFn<T> {
impl<T: ?Sized + Send + Sync + 'static> PartialEq for LazyInternedLazy<T> {
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<T: ?Sized + Send + Sync + 'static> {
Interned(Interned<T>),
Lazy(LazyInternedFn<T>),
Lazy(LazyInternedLazy<T>),
}
impl<T: ?Sized + Send + Sync + 'static> LazyInterned<T> {
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
Self::Lazy(LazyInternedFn(v))
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self::Lazy(LazyInternedLazy::new_const::<V>())
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn interned(self) -> Interned<T> {
match self {
Self::Interned(retval) => retval,
Self::Lazy(retval) => retval.interned(),
}
}
}
@ -79,7 +200,7 @@ impl<T: ?Sized + Sync + Send + 'static> Clone for LazyInterned<T> {
impl<T: ?Sized + Sync + Send + 'static> Copy for LazyInterned<T> {}
impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
impl<T: ?Sized + Sync + Send + 'static> Deref for LazyInterned<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -87,9 +208,9 @@ impl<T: ?Sized + Sync + Send + 'static + Intern> Deref for LazyInterned<T> {
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static> Eq for LazyInterned<T> where Interned<T>: Eq {}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialEq for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> PartialEq for LazyInterned<T>
where
Interned<T>: PartialEq,
{
@ -98,7 +219,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Ord for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> Ord for LazyInterned<T>
where
Interned<T>: Ord,
{
@ -107,7 +228,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> PartialOrd for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> PartialOrd for LazyInterned<T>
where
Interned<T>: PartialOrd,
{
@ -116,7 +237,7 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static + Intern> Hash for LazyInterned<T>
impl<T: ?Sized + Sync + Send + 'static> Hash for LazyInterned<T>
where
Interned<T>: Hash,
{
@ -125,77 +246,6 @@ where
}
}
impl<T: ?Sized + Sync + Send + 'static> LazyInterned<T> {
pub fn interned(self) -> Interned<T>
where
T: Intern,
{
struct MemoizeInterned<T: ?Sized + Intern>(PhantomData<T>);
impl<T: ?Sized + Intern> Hash for MemoizeInterned<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: ?Sized + Intern> PartialEq for MemoizeInterned<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: ?Sized + Intern> Eq for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> Clone for MemoizeInterned<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Intern> Copy for MemoizeInterned<T> {}
impl<T: ?Sized + Intern> MemoizeGeneric for MemoizeInterned<T> {
type InputRef<'a> = LazyInternedFn<T>;
type InputOwned = LazyInternedFn<T>;
type InputCow<'a> = LazyInternedFn<T>;
type Output = Interned<T>;
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;

View file

@ -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;

View file

@ -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<T: ?Sized + PhantomConstValue> PhantomConst<T> {
{
Self::new_interned(value.intern_deref())
}
pub const fn new_lazy(v: &'static dyn LazyInternedTrait<T>) -> Self {
pub const fn new_const<V: Default + Into<Interned<T>>>() -> Self {
Self {
value: LazyInterned::new_lazy(v),
value: LazyInterned::new_const::<V>(),
}
}
pub const fn new_const_default() -> Self
where
Interned<T>: Default,
{
Self::new_const::<Interned<T>>()
}
pub fn get(self) -> Interned<T> {
self.value.interned()
}
@ -334,9 +340,7 @@ impl<T: ?Sized + PhantomConstValue> StaticType for PhantomConst<T>
where
Interned<T>: Default,
{
const TYPE: Self = PhantomConst {
value: LazyInterned::new_lazy(&Interned::<T>::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;