Undo secion changes to README on optimization levels.
This commit is contained in:
parent
083b3147be
commit
bd6eb27154
58
README.md
58
README.md
@ -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.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user