Pack Scope data more tightly.

This commit is contained in:
Stephen Chung 2022-02-28 14:00:55 +08:00
parent 15448bf660
commit 54a61bfe00
2 changed files with 43 additions and 46 deletions

View File

@ -27,7 +27,7 @@ fn check_struct_sizes() {
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
{ {
assert_eq!(size_of::<Scope>(), 400); assert_eq!(size_of::<Scope>(), 536);
assert_eq!(size_of::<FnPtr>(), 80); assert_eq!(size_of::<FnPtr>(), 80);
assert_eq!(size_of::<LexError>(), 56); assert_eq!(size_of::<LexError>(), 56);
assert_eq!( assert_eq!(

View File

@ -1,7 +1,7 @@
//! Module that defines the [`Scope`] type representing a function call-stack scope. //! Module that defines the [`Scope`] type representing a function call-stack scope.
use super::dynamic::{AccessMode, Variant}; use super::dynamic::{AccessMode, Variant};
use crate::{Dynamic, Identifier, StaticVec}; use crate::{Dynamic, Identifier};
use smallvec::SmallVec; use smallvec::SmallVec;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -53,20 +53,20 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
// //
// # Implementation Notes // # Implementation Notes
// //
// [`Scope`] is implemented as two arrays of exactly the same length. Variables data (name, type, // [`Scope`] is implemented as three arrays of exactly the same length. That's because variable
// etc.) is manually split into two equal-length arrays. That's because variable names take up the // names take up the most space, with [`Identifier`] being three words long, but in the vast
// most space, with [`Identifier`] being three words long, but in the vast majority of cases the // majority of cases the name is NOT used to look up a variable. Variable lookup is usually via
// name is NOT used to look up a variable. Variable lookup is usually via direct indexing, // direct indexing, by-passing the name altogether.
// by-passing the name altogether.
// //
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables // [`Dynamic`] is reasonably small so packing it tightly improves cache performance.
// are accessed.
#[derive(Debug, Clone, Hash, Default)] #[derive(Debug, Clone, Hash, Default)]
pub struct Scope<'a> { pub struct Scope<'a> {
/// Current value of the entry. /// Current value of the entry.
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
/// (Name, aliases) of the entry. /// Name of the entry.
names: SmallVec<[(Identifier, Option<Box<StaticVec<Identifier>>>); SCOPE_ENTRIES_INLINED]>, names: SmallVec<[Identifier; SCOPE_ENTRIES_INLINED]>,
/// Aliases of the entry.
aliases: SmallVec<[Vec<Identifier>; SCOPE_ENTRIES_INLINED]>,
/// Phantom to keep the lifetime parameter in order not to break existing code. /// Phantom to keep the lifetime parameter in order not to break existing code.
phantom: PhantomData<&'a ()>, phantom: PhantomData<&'a ()>,
} }
@ -77,15 +77,12 @@ impl IntoIterator for Scope<'_> {
#[inline] #[inline]
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
Box::new(self.values.into_iter().zip(self.names.into_iter()).map( Box::new(
|(value, (name, alias))| { self.values
( .into_iter()
name.into(), .zip(self.names.into_iter().zip(self.aliases.into_iter()))
value, .map(|(value, (name, alias))| (name.into(), value, alias)),
alias.map(|a| a.to_vec()).unwrap_or_default(),
) )
},
))
} }
} }
@ -108,6 +105,7 @@ impl Scope<'_> {
Self { Self {
values: SmallVec::new_const(), values: SmallVec::new_const(),
names: SmallVec::new_const(), names: SmallVec::new_const(),
aliases: SmallVec::new_const(),
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -134,6 +132,7 @@ impl Scope<'_> {
pub fn clear(&mut self) -> &mut Self { pub fn clear(&mut self) -> &mut Self {
self.names.clear(); self.names.clear();
self.values.clear(); self.values.clear();
self.aliases.clear();
self self
} }
/// Get the number of entries inside the [`Scope`]. /// Get the number of entries inside the [`Scope`].
@ -258,7 +257,8 @@ impl Scope<'_> {
access: AccessMode, access: AccessMode,
mut value: Dynamic, mut value: Dynamic,
) -> &mut Self { ) -> &mut Self {
self.names.push((name.into(), None)); self.names.push(name.into());
self.aliases.push(Vec::new());
value.set_access_mode(access); value.set_access_mode(access);
self.values.push(value); self.values.push(value);
self self
@ -293,6 +293,7 @@ impl Scope<'_> {
pub fn rewind(&mut self, size: usize) -> &mut Self { pub fn rewind(&mut self, size: usize) -> &mut Self {
self.names.truncate(size); self.names.truncate(size);
self.values.truncate(size); self.values.truncate(size);
self.aliases.truncate(size);
self self
} }
/// Does the [`Scope`] contain the entry? /// Does the [`Scope`] contain the entry?
@ -311,7 +312,7 @@ impl Scope<'_> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn contains(&self, name: &str) -> bool { pub fn contains(&self, name: &str) -> bool {
self.names.iter().any(|(key, ..)| name == key) self.names.iter().any(|key| name == key)
} }
/// Find an entry in the [`Scope`], starting from the last. /// Find an entry in the [`Scope`], starting from the last.
#[inline] #[inline]
@ -323,7 +324,7 @@ impl Scope<'_> {
.iter() .iter()
.rev() // Always search a Scope in reverse order .rev() // Always search a Scope in reverse order
.enumerate() .enumerate()
.find_map(|(i, (key, ..))| { .find_map(|(i, key)| {
if name == key { if name == key {
let index = len - 1 - i; let index = len - 1 - i;
Some((index, self.values[index].access_mode())) Some((index, self.values[index].access_mode()))
@ -353,7 +354,7 @@ impl Scope<'_> {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(.., (key, ..))| name == key) .find(|(.., key)| &name == key)
.and_then(|(index, ..)| self.values[len - 1 - index].flatten_clone().try_cast()) .and_then(|(index, ..)| self.values[len - 1 - index].flatten_clone().try_cast())
} }
/// Check if the named entry in the [`Scope`] is constant. /// Check if the named entry in the [`Scope`] is constant.
@ -514,15 +515,9 @@ impl Scope<'_> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline] #[inline]
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
let (.., aliases) = self.names.get_mut(index).unwrap(); let aliases = self.aliases.get_mut(index).unwrap();
match aliases { if !aliases.contains(&alias) {
None => { aliases.push(alias);
let mut list = StaticVec::new_const();
list.push(alias);
*aliases = Some(list.into());
}
Some(aliases) if !aliases.iter().any(|a| a == &alias) => aliases.push(alias),
Some(_) => (),
} }
self self
} }
@ -533,20 +528,23 @@ impl Scope<'_> {
pub fn clone_visible(&self) -> Self { pub fn clone_visible(&self) -> Self {
let len = self.len(); let len = self.len();
self.names.iter().rev().enumerate().fold( self.names
Self::new(), .iter()
|mut entries, (index, (name, alias))| { .rev()
if !entries.names.iter().any(|(key, ..)| key == name) { .enumerate()
.fold(Self::new(), |mut entries, (index, name)| {
if !entries.names.contains(name) {
let orig_value = &self.values[len - 1 - index]; let orig_value = &self.values[len - 1 - index];
let alias = &self.aliases[len - 1 - index];
let mut value = orig_value.clone(); let mut value = orig_value.clone();
value.set_access_mode(orig_value.access_mode()); value.set_access_mode(orig_value.access_mode());
entries.names.push((name.clone(), alias.clone())); entries.names.push(name.clone());
entries.values.push(value); entries.values.push(value);
entries.aliases.push(alias.clone());
} }
entries entries
}, })
)
} }
/// Get an iterator to entries in the [`Scope`]. /// Get an iterator to entries in the [`Scope`].
#[inline] #[inline]
@ -554,10 +552,8 @@ impl Scope<'_> {
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> { pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> {
self.names self.names
.into_iter() .into_iter()
.zip(self.values.into_iter()) .zip(self.values.into_iter().zip(self.aliases.into_iter()))
.map(|((name, alias), value)| { .map(|(name, (value, alias))| (name, value, alias))
(name, value, alias.map(|a| a.to_vec()).unwrap_or_default())
})
} }
/// Get an iterator to entries in the [`Scope`]. /// Get an iterator to entries in the [`Scope`].
/// Shared values are flatten-cloned. /// Shared values are flatten-cloned.
@ -596,7 +592,7 @@ impl Scope<'_> {
self.names self.names
.iter() .iter()
.zip(self.values.iter()) .zip(self.values.iter())
.map(|((name, ..), value)| (name.as_ref(), value.is_read_only(), value)) .map(|(name, value)| (name.as_ref(), value.is_read_only(), value))
} }
/// Get a reverse iterator to entries in the [`Scope`]. /// Get a reverse iterator to entries in the [`Scope`].
/// Shared values are not expanded. /// Shared values are not expanded.
@ -606,7 +602,7 @@ impl Scope<'_> {
.iter() .iter()
.rev() .rev()
.zip(self.values.iter().rev()) .zip(self.values.iter().rev())
.map(|((name, ..), value)| (name.as_ref(), value.is_read_only(), value)) .map(|(name, value)| (name.as_ref(), value.is_read_only(), value))
} }
/// Remove a range of entries within the [`Scope`]. /// Remove a range of entries within the [`Scope`].
/// ///
@ -618,6 +614,7 @@ impl Scope<'_> {
pub(crate) fn remove_range(&mut self, start: usize, len: usize) { pub(crate) fn remove_range(&mut self, start: usize, len: usize) {
self.values.drain(start..start + len).for_each(|_| {}); self.values.drain(start..start + len).for_each(|_| {});
self.names.drain(start..start + len).for_each(|_| {}); self.names.drain(start..start + len).for_each(|_| {});
self.aliases.drain(start..start + len).for_each(|_| {});
} }
} }