Merge pull request #112 from schungx/master

Undo secion changes to README on optimization levels.
This commit is contained in:
Stephen Chung 2020-03-21 00:41:37 +08:00 committed by GitHub
commit dfb8f31256
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1317,7 +1317,7 @@ For example, in the following:
123; // eliminated - no effect 123; // eliminated - no effect
"hello"; // eliminated - no effect "hello"; // eliminated - no effect
[1, 2, x, x*2, 5]; // eliminated - no effect [1, 2, x, x*2, 5]; // eliminated - no effect
foo(42); // NOT eliminated - functions calls are not touched foo(42); // NOT eliminated - the function 'foo' may have side effects
666 // NOT eliminated - this is the return value of the block, 666 // NOT eliminated - this is the return value of the block,
// and the block is the last one // and the block is the last one
// so this is the return value of the whole script // so this is the return value of the whole script
@ -1351,12 +1351,14 @@ are spliced into the script text in order to turn on/off certain sections.
For fixed script texts, the constant values can be provided in a user-defined [`Scope`] object For fixed script texts, the constant values can be provided in a user-defined [`Scope`] object
to the [`Engine`] for use in compilation and evaluation. to the [`Engine`] for use in compilation and evaluation.
Beware, however, that most operators are actually function calls, and those are not optimized away: Beware, however, that most operators are actually function calls, and those functions can be overridden,
so they are not optimized away:
```rust ```rust
const DECISION = 1; const DECISION = 1;
if DECISION == 1 { // NOT optimized away because it requires a call to the '==' function if DECISION == 1 { // NOT optimized away because you can define
: // your own '==' function to override the built-in default!
: :
} else if DECISION == 2 { // same here, NOT optimized away } else if DECISION == 2 { // same here, NOT optimized away
: :
@ -1367,8 +1369,8 @@ if DECISION == 1 { // NOT optimized away because it requires a call to
} }
``` ```
because no operator functions will be run during the optimization process (unless the optimization level is because no operator functions will be run (in order not to trigger side effects) during the optimization process
set to [`OptimizationLevel::Full`]). So, instead, do this: (unless the optimization level is set to [`OptimizationLevel::Full`]). So, instead, do this:
```rust ```rust
const DECISION_1 = true; const DECISION_1 = true;
@ -1405,14 +1407,11 @@ There are actually three levels of optimizations: `None`, `Simple` and `Full`.
* `None` is obvious - no optimization on the AST is performed. * `None` is obvious - no optimization on the AST is performed.
* `Simple` (default) relies exclusively on static analysis, performing relatively _safe_ optimizations only. * `Simple` (default) performs relatively _safe_ optimizations without causing side effects
In particular, no function calls will be made to determine the output value. This also means that most comparison (i.e. it only relies on static analysis and will not actually perform any function calls).
operators and constant arithmetic expressions are untouched.
* `Full` is _much_ more aggressive. Functions _will_ be run, when passed constant arguments, to determine their results. * `Full` is _much_ more aggressive, _including_ running functions on constant arguments to determine their result.
One benefit is that many more optimization opportunities arise, especially with regards to comparison operators. One benefit to this is that many more optimization opportunities arise, especially with regards to comparison operators.
Nevertheless, the majority of scripts do not excessively rely on constants, and that is why this optimization level
is opt-in; only scripts that are machine-generated tend to have constants spliced in at generation time.
An [`Engine`]'s optimization level is set via a call to `set_optimization_level`: An [`Engine`]'s optimization level is set via a call to `set_optimization_level`:
@ -1422,15 +1421,15 @@ engine.set_optimization_level(rhai::OptimizationLevel::Full);
``` ```
When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_ and will _eagerly_ When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_ and will _eagerly_
call all functions when passed constant arguments, using the results to replace the actual calls. This also affects all operators evaluated all function calls with constant arguments, using the result to replace the call. This also applies to all operators
because most of them are implemented as functions. For instance, the same example above: (which are implemented as functions). For instance, the same example above:
```rust ```rust
// When compiling the following with OptimizationLevel::Full... // When compiling the following with OptimizationLevel::Full...
const DECISION = 1; const DECISION = 1;
// this condition is now eliminated because 'DECISION == 1' is a // this condition is now eliminated because 'DECISION == 1'
if DECISION == 1 { // function call to the '==' function with constant arguments, and it returns 'true' if DECISION == 1 { // is a function call to the '==' function, and it returns 'true'
print("hello!"); // this block is promoted to the parent level print("hello!"); // this block is promoted to the parent level
} else { } else {
print("boo!"); // this block is eliminated because it is never reached print("boo!"); // this block is eliminated because it is never reached
@ -1449,20 +1448,25 @@ let x = (1 + 2) * 3 - 4 / 5 % 6; // <- will be replaced by 'let x = 9'
let y = (1 > 2) || (3 <= 4); // <- will be replaced by 'let y = true' let y = (1 > 2) || (3 <= 4); // <- will be replaced by 'let y = true'
``` ```
Function side effect considerations
----------------------------------
All of Rhai's built-in functions (and operators which are implemented as functions) are _pure_ (i.e. they do not mutate state
nor cause side any effects, with the exception of `print` and `debug` which are handled specially) so using [`OptimizationLevel::Full`]
is usually quite safe _unless_ you register your own types and functions.
If custom functions are registered, they _may_ be called (or maybe not, if the calls happen to lie within a pruned code block).
If custom functions are registered to replace built-in operators, they will also be called when the operators are used (in an `if`
statement, for example) and cause side-effects.
Function volatility considerations Function volatility considerations
--------------------------------- ---------------------------------
Rhai functions never mutate state nor cause side any effects (except `print` and `debug` which are handled specially). Even if a custom function does not mutate state nor cause side effects, it may still be _volatile_, i.e. it _depends_ on the external
The only functions allowed to mutate state are custom type getters, setters and methods, and functions calls involving custom types environment and not _pure_. A perfect example is a function that gets the current time - obviously each run will return a different value!
are never optimized. So using [`OptimizationLevel::Full`] is usually quite safe. The optimizer, when using [`OptimizationLevel::Full`], _assumes_ that all functions are _pure_, so when it finds constant arguments.
This may cause the script to behave differently from the intended semantics because essentially the result of each function call will
However, even if a function cannot mutate state nor cause side effects, it may still be _volatile_, i.e. it may always be the same value.
_depend_ on the external environment and not be _pure_. A perfect example is a function that gets the current time -
obviously each run will return a different value!
The optimizer, when using [`OptimizationLevel::Full`], _assumes_ that all functions are _pure_ when it finds constant arguments.
This may cause the script to behave differently from the intended semantics because essentially the result of each function call
will always be the same value.
Therefore, **avoid using [`OptimizationLevel::Full`]** if you intend to register non-_pure_ custom types and/or functions. Therefore, **avoid using [`OptimizationLevel::Full`]** if you intend to register non-_pure_ custom types and/or functions.