From fcdd2eb143dd4e39d9099dd708868462878eed30 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 3 Sep 2022 11:27:13 +0800 Subject: [PATCH] Use caching for fast operators. --- src/eval/expr.rs | 58 +++++++++++++++++++++++++++++++------ src/func/call.rs | 28 +++++++++--------- src/packages/array_basic.rs | 2 +- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/eval/expr.rs b/src/eval/expr.rs index 7fd7027b..251032cd 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -242,17 +242,57 @@ impl Engine { let arg_values = &mut [&mut lhs, &mut rhs]; - return if let Some(f) = - crate::func::get_builtin_binary_op_fn(&name, arg_values[0], arg_values[1]) - { - let context = (self, name, None, &*global, lib, pos, level).into(); - (f)(context, arg_values) + let hash = crate::func::combine_hashes( + hashes.native, + crate::func::calc_fn_params_hash(arg_values.iter().map(|a| a.type_id())), + ); + + let c = caches.fn_resolution_cache_mut(); + + let entry = if c.contains_key(&hash) { + match c.get(&hash).unwrap() { + Some(entry) => entry, + None => { + return Err(ERR::ErrorFunctionNotFound( + self.gen_fn_call_signature( + #[cfg(not(feature = "no_module"))] + &crate::ast::Namespace::NONE, + name, + arg_values, + ), + pos, + ) + .into()) + } + } } else { - self.exec_fn_call( - None, global, caches, lib, name, *hashes, arg_values, false, false, pos, level, - ) - .map(|(v, ..)| v) + match crate::func::get_builtin_binary_op_fn(&name, arg_values[0], arg_values[1]) { + Some(f) => { + let entry = crate::eval::FnResolutionCacheEntry { + func: crate::func::CallableFunction::from_method( + Box::new(f) as Box + ), + source: None, + }; + c.insert(hash, Some(entry)); + c.get(&hash).unwrap().as_ref().unwrap() + } + None => { + println!("Exec {name} with {:?}", arg_values); + return self + .exec_fn_call( + None, global, caches, lib, name, *hashes, arg_values, false, false, + pos, level, + ) + .map(|(v, ..)| v); + } + } }; + + let func = entry.func.get_native_fn().unwrap(); + let context = (self, name, None, &*global, lib, pos, level).into(); + let result = (func)(context, arg_values); + return self.check_return_value(result, pos); } #[cfg(not(feature = "no_module"))] diff --git a/src/func/call.rs b/src/func/call.rs index 4b1696d8..2662cc80 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -131,7 +131,7 @@ impl Engine { /// Generate the signature for a function call. #[inline] #[must_use] - fn gen_call_signature( + pub(crate) fn gen_fn_call_signature( &self, #[cfg(not(feature = "no_module"))] namespace: &crate::ast::Namespace, fn_name: &str, @@ -174,26 +174,26 @@ impl Engine { fn resolve_fn<'s>( &self, _global: &GlobalRuntimeState, - state: &'s mut Caches, + caches: &'s mut Caches, lib: &[&Module], fn_name: &str, - hash_script: u64, + hash_base: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, is_op_assignment: bool, ) -> Option<&'s FnResolutionCacheEntry> { - if hash_script == 0 { + if hash_base == 0 { return None; } - let mut hash = args.as_ref().map_or(hash_script, |args| { + let mut hash = args.as_ref().map_or(hash_base, |args| { combine_hashes( - hash_script, + hash_base, calc_fn_params_hash(args.iter().map(|a| a.type_id())), ) }); - let result = state + let result = caches .fn_resolution_cache_mut() .entry(hash) .or_insert_with(|| { @@ -248,21 +248,21 @@ impl Engine { // Check `Dynamic` parameters for functions with parameters if allow_dynamic && max_bitmask == 0 && num_args > 0 { - let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_script)) + let is_dynamic = lib.iter().any(|&m| m.contains_dynamic_fn(hash_base)) || self .global_modules .iter() - .any(|m| m.contains_dynamic_fn(hash_script)); + .any(|m| m.contains_dynamic_fn(hash_base)); #[cfg(not(feature = "no_module"))] let is_dynamic = is_dynamic || _global .iter_imports_raw() - .any(|(_, m)| m.contains_dynamic_fn(hash_script)) + .any(|(_, m)| m.contains_dynamic_fn(hash_base)) || self .global_sub_modules .values() - .any(|m| m.contains_dynamic_fn(hash_script)); + .any(|m| m.contains_dynamic_fn(hash_base)); // Set maximum bitmask when there are dynamic versions of the function if is_dynamic { @@ -317,7 +317,7 @@ impl Engine { } }), ); - hash = combine_hashes(hash_script, hash_params); + hash = combine_hashes(hash_base, hash_params); bitmask += 1; } @@ -542,7 +542,7 @@ impl Engine { // Raise error _ => Err(ERR::ErrorFunctionNotFound( - self.gen_call_signature( + self.gen_fn_call_signature( #[cfg(not(feature = "no_module"))] &crate::ast::Namespace::NONE, name, @@ -1429,7 +1429,7 @@ impl Engine { Some(f) => unreachable!("unknown function type: {:?}", f), None => Err(ERR::ErrorFunctionNotFound( - self.gen_call_signature(namespace, fn_name, &args), + self.gen_fn_call_signature(namespace, fn_name, &args), pos, ) .into()), diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 0751a768..21abbde9 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -123,7 +123,7 @@ pub mod array_functions { /// let x = [1, 2, 3]; /// let y = [true, 'x']; /// - /// x.push(y); + /// x.append(y); /// /// print(x); // prints "[1, 2, 3, true, 'x']" /// ```