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
|
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.
|
The minimum Rust version is now `1.61.0` in order to use some `const` generics.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
@ -23,7 +28,7 @@ New features
|
|||||||
|
|
||||||
### Fast operators
|
### 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
|
### 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.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]
|
#[bench]
|
||||||
fn bench_eval_scope_complex(bench: &mut Bencher) {
|
fn bench_eval_scope_complex(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
|
@ -35,17 +35,24 @@ bitflags! {
|
|||||||
impl LangOptions {
|
impl LangOptions {
|
||||||
/// Create a new [`LangOptions`] with default values.
|
/// Create a new [`LangOptions`] with default values.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::IF_EXPR | Self::SWITCH_EXPR | Self::STMT_EXPR | Self::LOOPING | Self::SHADOW | {
|
Self::IF_EXPR
|
||||||
#[cfg(not(feature = "no_function"))]
|
| Self::SWITCH_EXPR
|
||||||
{
|
| Self::STMT_EXPR
|
||||||
Self::ANON_FN
|
| 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>,
|
pub args: StaticVec<Expr>,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// Is this function call a simple symbol-based operator?
|
/// Is this function call a native operator?
|
||||||
pub is_standard_operator: bool,
|
pub is_native_operator: bool,
|
||||||
/// [Position] of the function name.
|
/// [Position] of the function name.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
@ -206,8 +206,8 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
}
|
}
|
||||||
if self.is_standard_operator {
|
if self.is_native_operator {
|
||||||
ff.field("is_standard_operator", &self.is_standard_operator);
|
ff.field("is_native_operator", &self.is_native_operator);
|
||||||
}
|
}
|
||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
@ -667,7 +667,7 @@ impl Expr {
|
|||||||
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
is_standard_operator: false,
|
is_native_operator: false,
|
||||||
pos,
|
pos,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -50,8 +50,6 @@ fn main() {
|
|||||||
// Initialize scripting engine
|
// Initialize scripting engine
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.set_fast_operators(true);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
engine.set_optimization_level(rhai::OptimizationLevel::Simple);
|
engine.set_optimization_level(rhai::OptimizationLevel::Simple);
|
||||||
|
|
||||||
|
@ -227,14 +227,14 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
capture_parent_scope: capture,
|
capture_parent_scope: capture,
|
||||||
is_standard_operator: std_ops,
|
is_native_operator: native_ops,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
..
|
..
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
// Short-circuit operator call if under Fast Operators mode
|
// Short-circuit native binary operator call if under Fast Operators mode
|
||||||
if *std_ops && self.fast_operators() {
|
if *native_ops && self.fast_operators() && args.len() == 2 {
|
||||||
let mut lhs = self
|
let mut lhs = self
|
||||||
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
|
||||||
.0
|
.0
|
||||||
@ -314,8 +314,19 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, global, caches, lib, this_ptr, name, first_arg, args, *hashes, *capture,
|
scope,
|
||||||
*std_ops, pos, level,
|
global,
|
||||||
|
caches,
|
||||||
|
lib,
|
||||||
|
this_ptr,
|
||||||
|
name,
|
||||||
|
first_arg,
|
||||||
|
args,
|
||||||
|
*hashes,
|
||||||
|
*capture,
|
||||||
|
*native_ops,
|
||||||
|
pos,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -989,7 +989,7 @@ impl Engine {
|
|||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
is_standard_operator: bool,
|
is_operator: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
@ -1002,7 +1002,7 @@ impl Engine {
|
|||||||
let redirected; // Handle call() - Redirect function call
|
let redirected; // Handle call() - Redirect function call
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
_ if is_standard_operator => (),
|
_ if is_operator => (),
|
||||||
|
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
|
@ -616,7 +616,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
is_standard_operator: false,
|
is_native_operator: false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -688,7 +688,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
is_standard_operator: false,
|
is_native_operator: false,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -1922,6 +1922,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1949,6 +1950,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1967,6 +1969,7 @@ impl Engine {
|
|||||||
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
|
||||||
args,
|
args,
|
||||||
pos,
|
pos,
|
||||||
|
is_native_operator: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -2341,7 +2344,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string(op.as_ref()),
|
name: state.get_interned_string(op.as_ref()),
|
||||||
hashes: FnCallHashes::from_native(hash),
|
hashes: FnCallHashes::from_native(hash),
|
||||||
pos,
|
pos,
|
||||||
is_standard_operator: op_token.is_standard_symbol(),
|
is_native_operator: !is_valid_function_name(&op),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,17 +75,6 @@ fn test_native_overload() -> Result<(), Box<EvalAltResult>> {
|
|||||||
format!("{s1} Foo!").into()
|
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!(
|
assert_eq!(
|
||||||
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
engine.eval::<String>(r#"let x = "hello"; let y = "world"; x + y"#)?,
|
||||||
"helloworld"
|
"helloworld"
|
||||||
@ -95,5 +84,16 @@ fn test_native_overload() -> Result<(), Box<EvalAltResult>> {
|
|||||||
"hello"
|
"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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,13 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
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!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
|
||||||
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]; 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]; hi(a, 2)")?, 6);
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; test(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!(
|
assert_eq!(
|
||||||
engine.eval::<String>("let a = [1, 2, 3]; greet(test(a, 2))")?,
|
engine.eval::<String>("let a = [1, 2, 3]; greet(test(a, 2))")?,
|
||||||
"6 kitties"
|
"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());
|
engine.register_static_module("test", exported_module!(test::special_array_package).into());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user