Skip to content

Commit

Permalink
rune: Performance improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Nov 3, 2024
1 parent 209cc47 commit 4b1918a
Show file tree
Hide file tree
Showing 14 changed files with 303 additions and 168 deletions.
8 changes: 4 additions & 4 deletions crates/rune/src/compile/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate as rune;
use crate::alloc::borrow::Cow;
use crate::alloc::path::Path;
use crate::alloc::prelude::*;
use crate::alloc::{self, Box, HashMap, Vec};
use crate::alloc::{self, Box, Vec};
use crate::ast;
use crate::ast::{Span, Spanned};
use crate::compile::attrs::Parser;
Expand All @@ -15,7 +15,7 @@ use crate::compile::meta;
use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility};
use crate::module::{DocFunction, ModuleItemCommon};
use crate::parse::ResolveContext;
use crate::runtime::{Call, Protocol};
use crate::runtime::{Call, FieldMap, Protocol};
use crate::{Hash, Item, ItemBuf};

/// A meta reference to an item being compiled.
Expand Down Expand Up @@ -300,8 +300,8 @@ pub struct FieldsNamed {

impl FieldsNamed {
/// Coerce into a hashmap of fields.
pub(crate) fn to_fields(&self) -> alloc::Result<HashMap<Box<str>, usize>> {
let mut fields = HashMap::new();
pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.fields.len())?;

for f in self.fields.iter() {
fields.try_insert(f.name.try_clone()?, f.position)?;
Expand Down
10 changes: 5 additions & 5 deletions crates/rune/src/compile/unit_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ impl UnitBuilder {
hash,
variant_hash: Hash::EMPTY,
item: pool.item(meta.item_meta.item).try_to_owned()?,
fields: HashMap::new(),
fields: HashMap::default(),
});

self.constants
Expand Down Expand Up @@ -350,7 +350,7 @@ impl UnitBuilder {
hash: meta.hash,
variant_hash: Hash::EMPTY,
item: pool.item(meta.item_meta.item).try_to_owned()?,
fields: HashMap::new(),
fields: HashMap::default(),
});

if self
Expand Down Expand Up @@ -409,7 +409,7 @@ impl UnitBuilder {
hash: meta.hash,
variant_hash: Hash::EMPTY,
item: pool.item(meta.item_meta.item).try_to_owned()?,
fields: HashMap::new(),
fields: HashMap::default(),
});

if self
Expand Down Expand Up @@ -487,7 +487,7 @@ impl UnitBuilder {
hash: enum_hash,
variant_hash: meta.hash,
item: pool.item(meta.item_meta.item).try_to_owned()?,
fields: HashMap::new(),
fields: HashMap::default(),
});

if self
Expand Down Expand Up @@ -537,7 +537,7 @@ impl UnitBuilder {
hash: enum_hash,
variant_hash: meta.hash,
item: pool.item(meta.item_meta.item).try_to_owned()?,
fields: HashMap::new(),
fields: HashMap::default(),
});

if self
Expand Down
17 changes: 13 additions & 4 deletions crates/rune/src/module/module_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ use ::rust_alloc::sync::Arc;
use crate as rune;
use crate::alloc;
use crate::alloc::prelude::*;
use crate::alloc::HashMap;
use crate::compile::context::{AttributeMacroHandler, MacroHandler, TraitHandler};
use crate::compile::{meta, Docs};
use crate::function_meta::AssociatedName;
use crate::runtime::{ConstValue, FunctionHandler, TypeInfo};
use crate::runtime::{ConstValue, FieldMap, FunctionHandler, TypeInfo};
use crate::{Hash, ItemBuf};

#[doc(hidden)]
Expand Down Expand Up @@ -89,9 +88,19 @@ pub(crate) enum Fields {
}

impl Fields {
/// Get the number of named fields.
#[inline]
fn size(&self) -> usize {
match self {
Fields::Named(fields) => fields.len(),
_ => 0,
}
}

/// Coerce into fields hash map.
pub(crate) fn to_fields(&self) -> alloc::Result<HashMap<Box<str>, usize>> {
let mut fields = HashMap::new();
#[inline]
pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.size())?;

if let Fields::Named(names) = self {
for (index, name) in names.iter().copied().enumerate() {
Expand Down
72 changes: 72 additions & 0 deletions crates/rune/src/modules/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub fn module() -> Result<Module, ContextError> {
m.function_meta(Snapshot::debug)?;
m.function_meta(Snapshot::shared)?;
m.function_meta(Snapshot::is_exclusive)?;
m.function_meta(Snapshot::is_readable)?;
m.function_meta(Snapshot::is_writable)?;

Ok(m)
}
Expand Down Expand Up @@ -67,6 +69,8 @@ impl Snapshot {
/// let s = snapshot(v)?;
/// assert_eq!(s.shared(), 0);
/// assert!(!s.is_exclusive());
/// assert!(s.is_readable());
/// assert!(s.is_writable());
///
/// // Assign to a separate variable since the compiler will notice that `v` is moved.
/// let u = v;
Expand All @@ -78,12 +82,80 @@ impl Snapshot {
///
/// let s = snapshot(u)?;
/// assert!(s.is_exclusive());
/// assert!(!s.is_readable());
/// assert!(!s.is_wriable());
/// ```
#[rune::function]
fn is_exclusive(&self) -> bool {
self.inner.is_exclusive()
}

/// Test if the snapshot indicates that the value is readable.
///
/// # Examples
///
/// ```rune
/// use std::mem::snapshot;
///
/// let v = [1, 2, 3];
///
/// let s = snapshot(v)?;
/// assert_eq!(s.shared(), 0);
/// assert!(!s.is_exclusive());
/// assert!(s.is_readable());
/// assert!(s.is_writable());
///
/// // Assign to a separate variable since the compiler will notice that `v` is moved.
/// let u = v;
///
/// // Move the value into a closure, causing the original reference to become exclusively held.
/// let closure = move || {
/// v
/// };
///
/// let s = snapshot(u)?;
/// assert!(s.is_exclusive());
/// assert!(!s.is_readable());
/// assert!(!s.is_wriable());
/// ```
#[rune::function]
fn is_readable(&self) -> bool {
self.inner.is_readable()
}

/// Test if the snapshot indicates that the value is writable.
///
/// # Examples
///
/// ```rune
/// use std::mem::snapshot;
///
/// let v = [1, 2, 3];
///
/// let s = snapshot(v)?;
/// assert_eq!(s.shared(), 0);
/// assert!(!s.is_exclusive());
/// assert!(s.is_readable());
/// assert!(s.is_writable());
///
/// // Assign to a separate variable since the compiler will notice that `v` is moved.
/// let u = v;
///
/// // Move the value into a closure, causing the original reference to become exclusively held.
/// let closure = move || {
/// v
/// };
///
/// let s = snapshot(u)?;
/// assert!(s.is_exclusive());
/// assert!(!s.is_readable());
/// assert!(!s.is_wriable());
/// ```
#[rune::function]
fn is_writable(&self) -> bool {
self.inner.is_writable()
}

#[rune::function(protocol = DISPLAY_FMT)]
fn display(&self, f: &mut Formatter) -> VmResult<()> {
vm_write!(f, "{}", self.inner)
Expand Down
5 changes: 5 additions & 0 deletions crates/rune/src/runtime/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ impl Snapshot {
self.0 & MASK == 0
}

/// Test if the snapshot indicates that the value is writable.
pub(crate) fn is_writable(&self) -> bool {
self.0 & MASK == 0
}

/// Test if access is exclusively held.
pub(crate) fn is_exclusive(&self) -> bool {
self.0 & MASK != 0
Expand Down
2 changes: 2 additions & 0 deletions crates/rune/src/runtime/any_obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ struct Shared<T = ()> {

impl Shared {
/// Increment the reference count of the inner value.
#[inline]
unsafe fn inc(this: NonNull<Self>) {
let count_ref = &*addr_of!((*this.as_ptr()).count);
let count = count_ref.get();
Expand All @@ -726,6 +727,7 @@ impl Shared {
/// # Safety
///
/// ProtocolCaller needs to ensure that `this` is a valid pointer.
#[inline]
unsafe fn dec(this: NonNull<Self>) {
let count_ref = &*addr_of!((*this.as_ptr()).count);
let count = count_ref.get();
Expand Down
4 changes: 4 additions & 0 deletions crates/rune/src/runtime/borrow_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct BorrowMut<'a, T: ?Sized> {

impl<'a, T: ?Sized> BorrowMut<'a, T> {
/// Construct a borrow mut from static data.
#[inline]
pub(crate) fn from_static(data: &mut T) -> Self {
Self {
data: NonNull::from(data),
Expand All @@ -36,6 +37,7 @@ impl<'a, T: ?Sized> BorrowMut<'a, T> {
/// ensure that access has been acquired correctly using e.g.
/// [Access::exclusive]. Otherwise access can be release incorrectly once
/// this guard is dropped.
#[inline]
pub(crate) unsafe fn new(data: NonNull<T>, guard: RawAccessGuard) -> Self {
Self {
data,
Expand All @@ -60,6 +62,7 @@ impl<'a, T: ?Sized> BorrowMut<'a, T> {
/// assert_eq!(&mut bytes[..], &mut [1u8, 2u8][..]);
/// # Ok::<_, rune::support::Error>(())
/// ```
#[inline]
pub fn map<U: ?Sized>(mut this: Self, m: impl FnOnce(&mut T) -> &mut U) -> BorrowMut<'a, U> {
// SAFETY: This is safe per construction.
unsafe {
Expand Down Expand Up @@ -89,6 +92,7 @@ impl<'a, T: ?Sized> BorrowMut<'a, T> {
/// assert_eq!(&mut bytes[..], &mut [1u8, 2u8][..]);
/// # Ok::<_, rune::support::Error>(())
/// ```
#[inline]
pub fn try_map<U: ?Sized>(
mut this: Self,
m: impl FnOnce(&mut T) -> Option<&mut U>,
Expand Down
14 changes: 14 additions & 0 deletions crates/rune/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,17 @@ pub use self::control_flow::ControlFlow;
mod hasher;
#[cfg(feature = "alloc")]
pub use self::hasher::Hasher;

pub(crate) type FieldMap<K, V> = crate::alloc::HashMap<K, V>;

#[inline(always)]
pub(crate) fn new_field_map<K, V>() -> FieldMap<K, V> {
FieldMap::new()
}

#[inline(always)]
pub(crate) fn new_field_hash_map_with_capacity<K, V>(
cap: usize,
) -> crate::alloc::Result<FieldMap<K, V>> {
FieldMap::try_with_capacity(cap)
}
16 changes: 6 additions & 10 deletions crates/rune/src/runtime/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use core::hash;
use core::iter;

use crate as rune;
use crate::alloc::hash_map;
use crate::alloc::hashbrown::raw::RawIter;
use crate::alloc::prelude::*;
use crate::alloc::{self, String};
use crate::alloc::{hash_map, HashMap};
use crate::runtime::{
FromValue, ProtocolCaller, RawAnyGuard, Ref, ToValue, Value, VmError, VmResult,
FieldMap, FromValue, ProtocolCaller, RawAnyGuard, Ref, ToValue, Value, VmError, VmResult,
};
use crate::Any;

Expand Down Expand Up @@ -82,7 +82,7 @@ pub type Values<'a> = hash_map::Values<'a, String, Value>;
#[repr(transparent)]
#[rune(item = ::std::object)]
pub struct Object {
inner: HashMap<String, Value>,
inner: FieldMap<String, Value>,
}

impl Object {
Expand All @@ -98,7 +98,7 @@ impl Object {
#[rune::function(keep, path = Self::new)]
pub fn new() -> Self {
Self {
inner: HashMap::new(),
inner: crate::runtime::new_field_map(),
}
}

Expand All @@ -121,7 +121,7 @@ impl Object {
// BTreeMap doesn't support setting capacity on creation but we keep
// this here in case we want to switch store later.
Ok(Self {
inner: HashMap::try_with_capacity(capacity)?,
inner: crate::runtime::new_field_hash_map_with_capacity(capacity)?,
})
}

Expand Down Expand Up @@ -247,6 +247,7 @@ impl Object {
/// Inserts a key-value pair into the map.
///
/// If the map did not have this key present, `None` is returned.
#[inline]
pub fn insert(&mut self, k: String, v: Value) -> alloc::Result<Option<Value>> {
self.inner.try_insert(k, v)
}
Expand All @@ -259,11 +260,6 @@ impl Object {
self.inner.clear();
}

/// Convert into inner.
pub fn into_inner(self) -> HashMap<String, Value> {
self.inner
}

/// An iterator visiting all key-value pairs in arbitrary order.
/// The iterator element type is `(&'a String, &'a Value)`.
pub fn iter(&self) -> Iter<'_> {
Expand Down
Loading

0 comments on commit 4b1918a

Please sign in to comment.