Make fast operators the default.
This commit is contained in:
parent
defdc2a5bc
commit
702bb9030a
@ -4,6 +4,11 @@ Rhai Release Notes
|
||||
Version 1.10.0
|
||||
==============
|
||||
|
||||
This version, by default, turns on _Fast Operators_ mode, which assumes that built-in operators for
|
||||
standard data types are never overloaded – in the vast majority of cases this should be so.
|
||||
Avoid checking for overloads may result in substantial speed improvements especially for
|
||||
operator-heavy scripts.
|
||||
|
||||
The minimum Rust version is now `1.61.0` in order to use some `const` generics.
|
||||
|
||||
Bug fixes
|
||||
@ -23,7 +28,7 @@ New features
|
||||
|
||||
### Fast operators
|
||||
|
||||
* A new option `Engine::fast_operators` is introduced that short-circuits all built-in operators of built-in types for higher speed. User overloads are ignored. For operator-heavy scripts, this may yield substantial speed-up's.
|
||||
* A new option `Engine::fast_operators` is introduced (default to `true`) that short-circuits all built-in operators of built-in types for higher speed. User overloads are ignored. For operator-heavy scripts, this may yield substantial speed-up's.
|
||||
|
||||
### Fallible type iterators
|
||||
|
||||
|
@ -53,23 +53,6 @@ fn bench_eval_scope_longer(bench: &mut Bencher) {
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_longer_fast_ops(bench: &mut Bencher) {
|
||||
let script = "(requests_made * requests_succeeded / 100) >= 90";
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::None);
|
||||
engine.set_fast_operators(true);
|
||||
|
||||
let mut scope = Scope::new();
|
||||
scope.push("requests_made", 99 as INT);
|
||||
scope.push("requests_succeeded", 90 as INT);
|
||||
|
||||
let ast = engine.compile_expression(script).unwrap();
|
||||
|
||||
bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap());
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_eval_scope_complex(bench: &mut Bencher) {
|
||||
let script = r#"
|
||||
|
@ -35,17 +35,24 @@ bitflags! {
|
||||
impl LangOptions {
|
||||
/// Create a new [`LangOptions`] with default values.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::IF_EXPR | Self::SWITCH_EXPR | Self::STMT_EXPR | Self::LOOPING | Self::SHADOW | {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
Self::ANON_FN
|
||||
Self::IF_EXPR
|
||||
| Self::SWITCH_EXPR
|
||||
| Self::STMT_EXPR
|
||||
| Self::LOOPING
|
||||
| Self::SHADOW
|
||||
| Self::FAST_OPS
|
||||
| {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
Self::ANON_FN
|
||||
}
|
||||
#[cfg(feature = "no_function")]
|
||||
{
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "no_function")]
|
||||
{
|
||||
Self::empty()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,8 +190,8 @@ pub struct FnCallExpr {
|
||||
pub args: StaticVec<Expr>,
|
||||
/// Does this function call capture the parent scope?
|
||||
pub capture_parent_scope: bool,
|
||||
/// Is this function call a simple symbol-based operator?
|
||||
pub is_standard_operator: bool,
|
||||
/// Is this function call a native operator?
|
||||
pub is_native_operator: bool,
|
||||
/// [Position] of the function name.
|
||||
pub pos: Position,
|
||||
}
|
||||
@ -206,8 +206,8 @@ impl fmt::Debug for FnCallExpr {
|
||||
if self.capture_parent_scope {
|
||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||
}
|
||||
if self.is_standard_operator {
|
||||
ff.field("is_standard_operator", &self.is_standard_operator);
|
||||
if self.is_native_operator {
|
||||
ff.field("is_native_operator", &self.is_native_operator);
|
||||
}
|
||||
ff.field("hash", &self.hashes)
|
||||
.field("name", &self.name)
|
||||
@ -667,7 +667,7 @@ impl Expr {
|
||||
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||
capture_parent_scope: false,
|
||||
is_standard_operator: false,
|
||||
is_native_operator: false,
|
||||
pos,
|
||||
}
|
||||
.into(),
|
||||
|
@ -50,8 +50,6 @@ fn main() {
|
||||
// Initialize scripting engine
|
||||
let mut engine = Engine::new();
|
||||
|
||||
engine.set_fast_operators(true);
|
||||
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
engine.set_optimization_level(rhai::OptimizationLevel::Simple);
|
||||
|
||||
|
@ -227,14 +227,14 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace,
|
||||
capture_parent_scope: capture,
|
||||
is_standard_operator: std_ops,
|
||||
is_native_operator: native_ops,
|
||||
hashes,
|
||||
args,
|
||||
..
|
||||
} = expr;
|
||||
|
||||
// Short-circuit operator call if under Fast Operators mode
|
||||
if *std_ops && self.fast_operators() {
|
||||
// Short-circuit native binary operator call if under Fast Operators mode
|
||||
if *native_ops && self.fast_operators() && args.len() == 2 {
|
||||
let mut lhs = self
|
||||
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
||||
.0
|
||||
@ -314,8 +314,19 @@ impl Engine {
|
||||
);
|
||||
|
||||
self.make_function_call(
|
||||
scope, global, caches, lib, this_ptr, name, first_arg, args, *hashes, *capture,
|
||||
*std_ops, pos, level,
|
||||
scope,
|
||||
global,
|
||||
caches,
|
||||
lib,
|
||||
this_ptr,
|
||||
name,
|
||||
first_arg,
|
||||
args,
|
||||
*hashes,
|
||||
*capture,
|
||||
*native_ops,
|
||||
pos,
|
||||
level,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -989,7 +989,7 @@ impl Engine {
|
||||
args_expr: &[Expr],
|
||||
hashes: FnCallHashes,
|
||||
capture_scope: bool,
|
||||
is_standard_operator: bool,
|
||||
is_operator: bool,
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
@ -1002,7 +1002,7 @@ impl Engine {
|
||||
let redirected; // Handle call() - Redirect function call
|
||||
|
||||
match name {
|
||||
_ if is_standard_operator => (),
|
||||
_ if is_operator => (),
|
||||
|
||||
// Handle call()
|
||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||
|
@ -616,7 +616,7 @@ impl Engine {
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_interned_string(id),
|
||||
capture_parent_scope,
|
||||
is_standard_operator: false,
|
||||
is_native_operator: false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace,
|
||||
hashes,
|
||||
@ -688,7 +688,7 @@ impl Engine {
|
||||
return Ok(FnCallExpr {
|
||||
name: state.get_interned_string(id),
|
||||
capture_parent_scope,
|
||||
is_standard_operator: false,
|
||||
is_native_operator: false,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace,
|
||||
hashes,
|
||||
@ -1922,6 +1922,7 @@ impl Engine {
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||
args,
|
||||
pos,
|
||||
is_native_operator: true,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -1949,6 +1950,7 @@ impl Engine {
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||
args,
|
||||
pos,
|
||||
is_native_operator: true,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -1967,6 +1969,7 @@ impl Engine {
|
||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||
args,
|
||||
pos,
|
||||
is_native_operator: true,
|
||||
..Default::default()
|
||||
}
|
||||
.into_fn_call_expr(pos))
|
||||
@ -2341,7 +2344,7 @@ impl Engine {
|
||||
name: state.get_interned_string(op.as_ref()),
|
||||
hashes: FnCallHashes::from_native(hash),
|
||||
pos,
|
||||
is_standard_operator: op_token.is_standard_symbol(),
|
||||
is_native_operator: !is_valid_function_name(&op),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
@ -75,17 +75,6 @@ fn test_native_overload() -> Result<(), Box<EvalAltResult>> {
|
||||
format!("{s1} Foo!").into()
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||
"hello***world"
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<String>(r#"let x = "hello"; let y = (); x + y"#)?,
|
||||
"hello Foo!"
|
||||
);
|
||||
|
||||
engine.set_fast_operators(true);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||
"helloworld"
|
||||
@ -95,5 +84,16 @@ fn test_native_overload() -> Result<(), Box<EvalAltResult>> {
|
||||
"hello"
|
||||
);
|
||||
|
||||
engine.set_fast_operators(false);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||
"hello***world"
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<String>(r#"let x = "hello"; let y = (); x + y"#)?,
|
||||
"hello Foo!"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -53,6 +53,13 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||
42
|
||||
);
|
||||
|
||||
engine.set_fast_operators(false);
|
||||
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||
123
|
||||
|
@ -128,11 +128,14 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; hi(a, 2)")?, 6);
|
||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(a, 2)")?, 6);
|
||||
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
||||
assert_eq!(
|
||||
engine.eval::<String>("let a = [1, 2, 3]; greet(test(a, 2))")?,
|
||||
"6 kitties"
|
||||
);
|
||||
assert_eq!(engine.eval::<INT>("2 + 2")?, 4);
|
||||
|
||||
engine.set_fast_operators(false);
|
||||
assert_eq!(engine.eval::<INT>("2 + 2")?, 5);
|
||||
|
||||
engine.register_static_module("test", exported_module!(test::special_array_package).into());
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user