Optimize hot path of operators calling.

This commit is contained in:
Stephen Chung
2020-05-24 16:40:00 +08:00
parent 65ee262f1b
commit 0374311cf6
6 changed files with 294 additions and 249 deletions

View File

@@ -1,10 +1,9 @@
use crate::any::Dynamic;
use crate::calc_fn_hash;
use crate::engine::{
Engine, FunctionsLib, State as EngineState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
};
use crate::fn_native::FnCallArgs;
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
@@ -53,23 +52,19 @@ struct State<'a> {
/// An `Engine` instance for eager function evaluation.
engine: &'a Engine,
/// Library of script-defined functions.
fn_lib: &'a [(&'a str, usize)],
lib: &'a FunctionsLib,
/// Optimization level.
optimization_level: OptimizationLevel,
}
impl<'a> State<'a> {
/// Create a new State.
pub fn new(
engine: &'a Engine,
fn_lib: &'a [(&'a str, usize)],
level: OptimizationLevel,
) -> Self {
pub fn new(engine: &'a Engine, lib: &'a FunctionsLib, level: OptimizationLevel) -> Self {
Self {
changed: false,
constants: vec![],
engine,
fn_lib,
lib,
optimization_level: level,
}
}
@@ -128,7 +123,8 @@ fn call_fn_with_constant_arguments(
.engine
.call_fn_raw(
None,
&mut EngineState::new(&Default::default()),
&mut Default::default(),
state.lib,
fn_name,
(hash_fn, 0),
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
@@ -557,7 +553,10 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
// First search in script-defined functions (can override built-in)
// Cater for both normal function call style and method call style (one additional arguments)
if !*native_only && state.fn_lib.iter().find(|(id, len)| *id == name && (*len == args.len() || *len == args.len() + 1)).is_some() {
if !*native_only && state.lib.values().find(|f|
&f.name == name
&& (args.len()..=args.len() + 1).contains(&f.params.len())
).is_some() {
// A script-defined function overrides the built-in function - do not make the call
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
return Expr::FnCall(x);
@@ -615,11 +614,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
}
}
fn optimize<'a>(
fn optimize(
statements: Vec<Stmt>,
engine: &Engine,
scope: &Scope,
fn_lib: &'a [(&'a str, usize)],
lib: &FunctionsLib,
level: OptimizationLevel,
) -> Vec<Stmt> {
// If optimization level is None then skip optimizing
@@ -628,7 +627,7 @@ fn optimize<'a>(
}
// Set up the state
let mut state = State::new(engine, fn_lib, level);
let mut state = State::new(engine, lib, level);
// Add constants from the scope into the state
scope
@@ -713,41 +712,35 @@ pub fn optimize_into_ast(
const level: OptimizationLevel = OptimizationLevel::None;
#[cfg(not(feature = "no_function"))]
let fn_lib_values: StaticVec<_> = functions
.iter()
.map(|fn_def| (fn_def.name.as_str(), fn_def.params.len()))
.collect();
#[cfg(not(feature = "no_function"))]
let fn_lib = fn_lib_values.as_ref();
#[cfg(feature = "no_function")]
const fn_lib: &[(&str, usize)] = &[];
#[cfg(not(feature = "no_function"))]
let lib = FunctionsLib::from_iter(functions.iter().cloned().map(|mut fn_def| {
let lib = {
if !level.is_none() {
let pos = fn_def.body.position();
let lib = FunctionsLib::from_iter(functions.iter().cloned());
// Optimize the function body
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), fn_lib, level);
FunctionsLib::from_iter(functions.into_iter().map(|mut fn_def| {
let pos = fn_def.body.position();
// {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(x.1.unwrap()))
}
// { return; } -> ()
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
}
// All others
stmt => stmt,
};
// Optimize the function body
let mut body = optimize(vec![fn_def.body], engine, &Scope::new(), &lib, level);
// {} -> Noop
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
// { return val; } -> val
Stmt::ReturnWithVal(x) if x.1.is_some() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(x.1.unwrap()))
}
// { return; } -> ()
Stmt::ReturnWithVal(x) if x.1.is_none() && (x.0).0 == ReturnType::Return => {
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
}
// All others
stmt => stmt,
};
fn_def
}))
} else {
FunctionsLib::from_iter(functions.into_iter())
}
fn_def
}));
};
#[cfg(feature = "no_function")]
let lib: FunctionsLib = Default::default();
@@ -756,7 +749,7 @@ pub fn optimize_into_ast(
match level {
OptimizationLevel::None => statements,
OptimizationLevel::Simple | OptimizationLevel::Full => {
optimize(statements, engine, &scope, fn_lib, level)
optimize(statements, engine, &scope, &lib, level)
}
},
lib,