Skip to content

Commit

Permalink
Merge pull request #373 from schungx/master
Browse files Browse the repository at this point in the history
Further optimizations.
  • Loading branch information
schungx committed Mar 14, 2021
2 parents 7a30030 + d9df6aa commit b1ec871
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 462 deletions.
60 changes: 26 additions & 34 deletions Cargo.toml
@@ -1,8 +1,5 @@
[workspace]
members = [
".",
"codegen"
]
members = [".", "codegen"]

[package]
name = "rhai"
Expand All @@ -14,14 +11,9 @@ homepage = "https://rhai.rs"
repository = "https://github.com/rhaiscript"
readme = "README.md"
license = "MIT OR Apache-2.0"
include = [
"**/*.rs",
"scripts/*.rhai",
"**/*.md",
"Cargo.toml"
]
keywords = [ "scripting", "scripting-engine", "scripting-language", "embedded" ]
categories = [ "no-std", "embedded", "wasm", "parser-implementations" ]
include = ["**/*.rs", "scripts/*.rhai", "**/*.md", "Cargo.toml"]
keywords = ["scripting", "scripting-engine", "scripting-language", "embedded"]
categories = ["no-std", "embedded", "wasm", "parser-implementations"]

[dependencies]
smallvec = { version = "1.6", default-features = false, features = ["union"] }
Expand All @@ -31,29 +23,29 @@ rhai_codegen = { version = "0.3.3", path = "codegen" }

[features]
default = []
unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync
no_optimize = [] # no script optimizer
no_float = [] # no floating-point
f32_float = [] # set FLOAT=f32
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
decimal = [ "rust_decimal" ] # add the Decimal number type
no_index = [] # no arrays and indexing
no_object = [] # no custom objects
no_function = [ "no_closure" ] # no script-defined functions (meaning no closures)
no_closure = [] # no automatic sharing and capture of anonymous functions to external variables
no_module = [] # no modules
internals = [] # expose internal data structures
unicode-xid-ident = [ "unicode-xid" ] # allow Unicode Standard Annex #31 for identifiers.
metadata = [ "serde", "serde_json" ] # enables exporting functions metadata to JSON
unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync
no_optimize = [] # no script optimizer
no_float = [] # no floating-point
f32_float = [] # set FLOAT=f32
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
decimal = ["rust_decimal"] # add the Decimal number type
no_index = [] # no arrays and indexing
no_object = [] # no custom objects
no_function = ["no_closure"] # no script-defined functions (meaning no closures)
no_closure = [] # no automatic sharing and capture of anonymous functions to external variables
no_module = [] # no modules
internals = [] # expose internal data structures
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
metadata = ["serde", "serde_json"] # enables exporting functions metadata to JSON

# compiling for no-std
no_std = [ "smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash/compile-time-rng" ]
no_std = ["smallvec/union", "num-traits/libm", "hashbrown", "core-error", "libm", "ahash/compile-time-rng"]

# compiling for WASM
wasm-bindgen = [ "instant/wasm-bindgen" ]
stdweb = [ "instant/stdweb" ]
wasm-bindgen = ["instant/wasm-bindgen"]
stdweb = ["instant/stdweb"]

[profile.release]
lto = "fat"
Expand Down Expand Up @@ -101,10 +93,10 @@ default_features = false
optional = true

[target.'cfg(target_arch = "wasm32")'.dependencies]
instant= { version = "0.1" } # WASM implementation of std::time::Instant
instant = { version = "0.1" } # WASM implementation of std::time::Instant

[target.'cfg(target_arch = "wasm64")'.dependencies]
instant= { version = "0.1" } # WASM implementation of std::time::Instant
instant = { version = "0.1" } # WASM implementation of std::time::Instant

[package.metadata.docs.rs]
features = [ "metadata", "internals", "decimal" ]
features = ["metadata", "internals", "decimal"]
6 changes: 3 additions & 3 deletions src/ast.rs
Expand Up @@ -658,7 +658,7 @@ impl AST {
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def)
.map(|(_, _, _, _, fn_def)| fn_def.as_ref())
}
/// Iterate through all function definitions.
///
Expand All @@ -668,7 +668,7 @@ impl AST {
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
self.functions
.iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.into())
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into())
}
/// Clear all function definitions in the [`AST`].
///
Expand Down Expand Up @@ -1847,7 +1847,7 @@ mod tests {
assert_eq!(size_of::<ast::Stmt>(), 40);
assert_eq!(size_of::<Option<ast::Stmt>>(), 40);
assert_eq!(size_of::<FnPtr>(), 32);
assert_eq!(size_of::<Scope>(), 48);
assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16);
assert_eq!(size_of::<EvalAltResult>(), 72);
Expand Down
90 changes: 53 additions & 37 deletions src/engine.rs
Expand Up @@ -17,9 +17,10 @@ use crate::stdlib::{
collections::{HashMap, HashSet},
fmt, format,
hash::{Hash, Hasher},
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
num::{NonZeroU8, NonZeroUsize},
ops::DerefMut,
string::{String, ToString},
vec::Vec,
};
use crate::syntax::CustomSyntax;
use crate::utils::{get_hasher, StraightHasherBuilder};
Expand All @@ -40,6 +41,8 @@ use crate::Map;
#[cfg(not(feature = "no_object"))]
pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical

pub type Precedence = NonZeroU8;

/// _(INTERNALS)_ A stack of imported [modules][Module].
/// Exported under the `internals` feature only.
///
Expand All @@ -53,7 +56,7 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical
// the module name will live beyond the AST of the eval script text.
// The best we can do is a shared reference.
#[derive(Debug, Clone, Default)]
pub struct Imports(StaticVec<(ImmutableString, Shared<Module>)>);
pub struct Imports(StaticVec<ImmutableString>, StaticVec<Shared<Module>>);

impl Imports {
/// Get the length of this stack of imported [modules][Module].
Expand All @@ -69,7 +72,7 @@ impl Imports {
/// Get the imported [modules][Module] at a particular index.
#[inline(always)]
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.0.get(index).map(|(_, m)| m).cloned()
self.1.get(index).cloned()
}
/// Get the index of an imported [modules][Module] by name.
#[inline(always)]
Expand All @@ -78,78 +81,72 @@ impl Imports {
.iter()
.enumerate()
.rev()
.find(|(_, (key, _))| key.as_str() == name)
.map(|(index, _)| index)
.find_map(|(i, key)| if key.as_str() == name { Some(i) } else { None })
}
/// Push an imported [modules][Module] onto the stack.
#[inline(always)]
pub fn push(&mut self, name: impl Into<ImmutableString>, module: impl Into<Shared<Module>>) {
self.0.push((name.into(), module.into()));
self.0.push(name.into());
self.1.push(module.into());
}
/// Truncate the stack of imported [modules][Module] to a particular length.
#[inline(always)]
pub fn truncate(&mut self, size: usize) {
self.0.truncate(size);
self.1.truncate(size);
}
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)]
#[inline(always)]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.0
.iter()
.zip(self.1.iter())
.rev()
.map(|(name, module)| (name.as_str(), module.as_ref()))
}
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
self.0.iter().rev().map(|(n, m)| (n, m))
self.0.iter().rev().zip(self.1.iter().rev())
}
/// Get an iterator to this stack of imported [modules][Module] in forward order.
#[allow(dead_code)]
#[inline(always)]
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
self.0.iter().map(|(n, m)| (n, m))
self.0.iter().zip(self.1.iter())
}
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
#[inline(always)]
pub fn into_iter(self) -> impl Iterator<Item = (ImmutableString, Shared<Module>)> {
self.0.into_iter().rev()
}
/// Add a stream of imported [modules][Module].
#[inline(always)]
pub fn extend(&mut self, stream: impl Iterator<Item = (ImmutableString, Shared<Module>)>) {
self.0.extend(stream)
self.0.into_iter().rev().zip(self.1.into_iter().rev())
}
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
#[allow(dead_code)]
#[inline(always)]
pub fn contains_fn(&self, hash: u64) -> bool {
self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash))
self.1.iter().any(|m| m.contains_qualified_fn(hash))
}
/// Get specified function via its hash key.
#[inline(always)]
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&ImmutableString>)> {
self.0
self.1
.iter()
.rev()
.find_map(|(_, m)| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
}
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of
/// imported [modules][Module]?
#[allow(dead_code)]
#[inline(always)]
pub fn contains_iter(&self, id: TypeId) -> bool {
self.0.iter().any(|(_, m)| m.contains_qualified_iter(id))
self.1.iter().any(|m| m.contains_qualified_iter(id))
}
/// Get the specified [`TypeId`][std::any::TypeId] iterator.
#[inline(always)]
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.0
.iter()
.rev()
.find_map(|(_, m)| m.get_qualified_iter(id))
self.1.iter().rev().find_map(|m| m.get_qualified_iter(id))
}
}

Expand Down Expand Up @@ -494,6 +491,18 @@ impl<T: Into<Dynamic>> From<T> for Target<'_> {
}
}

/// An entry in a function resolution cache.
#[derive(Debug, Clone)]
pub struct FnResolutionCacheEntry {
/// Function.
pub func: CallableFunction,
/// Optional source.
pub source: Option<ImmutableString>,
}

/// A function resolution cache.
pub type FnResolutionCache = HashMap<u64, Option<FnResolutionCacheEntry>, StraightHasherBuilder>;

/// _(INTERNALS)_ A type that holds all the current states of the [`Engine`].
/// Exported under the `internals` feature only.
///
Expand All @@ -518,10 +527,10 @@ pub struct State {
/// Embedded module resolver.
#[cfg(not(feature = "no_module"))]
pub resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
/// Functions resolution cache.
fn_resolution_caches: StaticVec<
HashMap<u64, Option<(CallableFunction, Option<ImmutableString>)>, StraightHasherBuilder>,
>,
/// function resolution cache.
fn_resolution_caches: StaticVec<FnResolutionCache>,
/// Free resolution caches.
fn_resolution_caches_free_list: Vec<FnResolutionCache>,
}

impl State {
Expand All @@ -530,25 +539,32 @@ impl State {
pub fn is_global(&self) -> bool {
self.scope_level == 0
}
/// Get a mutable reference to the current functions resolution cache.
pub fn fn_resolution_cache_mut(
&mut self,
) -> &mut HashMap<u64, Option<(CallableFunction, Option<ImmutableString>)>, StraightHasherBuilder>
{
/// Get a mutable reference to the current function resolution cache.
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.fn_resolution_caches.is_empty() {
self.fn_resolution_caches
.push(HashMap::with_capacity_and_hasher(16, StraightHasherBuilder));
}
self.fn_resolution_caches.last_mut().unwrap()
}
/// Push an empty functions resolution cache onto the stack and make it current.
/// Push an empty function resolution cache onto the stack and make it current.
#[allow(dead_code)]
pub fn push_fn_resolution_cache(&mut self) {
self.fn_resolution_caches.push(Default::default());
self.fn_resolution_caches.push(
self.fn_resolution_caches_free_list
.pop()
.unwrap_or_default(),
);
}
/// Remove the current functions resolution cache and make the last one current.
/// Remove the current function resolution cache from the stack and make the last one current.
///
/// # Panics
///
/// Panics if there are no more function resolution cache in the stack.
pub fn pop_fn_resolution_cache(&mut self) {
self.fn_resolution_caches.pop();
let mut cache = self.fn_resolution_caches.pop().unwrap();
cache.clear();
self.fn_resolution_caches_free_list.push(cache);
}
}

Expand Down Expand Up @@ -576,7 +592,7 @@ pub struct Limits {
#[cfg(not(feature = "no_function"))]
pub max_function_expr_depth: Option<NonZeroUsize>,
/// Maximum number of operations allowed to run.
pub max_operations: Option<NonZeroU64>,
pub max_operations: Option<crate::stdlib::num::NonZeroU64>,
/// Maximum number of [modules][Module] allowed to load.
///
/// Set to zero to effectively disable loading any [module][Module].
Expand Down Expand Up @@ -710,7 +726,7 @@ pub struct Engine {
/// A hashset containing symbols to disable.
pub(crate) disabled_symbols: HashSet<String>,
/// A hashmap containing custom keywords and precedence to recognize.
pub(crate) custom_keywords: HashMap<String, Option<NonZeroU8>>,
pub(crate) custom_keywords: HashMap<String, Option<Precedence>>,
/// Custom syntax.
pub(crate) custom_syntax: HashMap<ImmutableString, CustomSyntax>,
/// Callback closure for resolving variable access.
Expand Down
5 changes: 3 additions & 2 deletions src/engine_settings.rs
@@ -1,6 +1,7 @@
//! Configuration settings for [`Engine`].

use crate::stdlib::{format, num::NonZeroU8, string::String};
use crate::engine::Precedence;
use crate::stdlib::{format, string::String};
use crate::token::Token;
use crate::Engine;

Expand Down Expand Up @@ -272,7 +273,7 @@ impl Engine {
keyword: &str,
precedence: u8,
) -> Result<&mut Self, String> {
let precedence = NonZeroU8::new(precedence);
let precedence = Precedence::new(precedence);

if precedence.is_none() {
return Err("precedence cannot be zero".into());
Expand Down

0 comments on commit b1ec871

Please sign in to comment.