Add Rhai book.
This commit is contained in:
24
doc/src/engine/optimize/disable.md
Normal file
24
doc/src/engine/optimize/disable.md
Normal file
@@ -0,0 +1,24 @@
|
||||
Turn Off Script Optimizations
|
||||
============================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
When scripts:
|
||||
|
||||
* are known to be run only _once_,
|
||||
|
||||
* are known to contain no dead code,
|
||||
|
||||
* do not use constants in calculations
|
||||
|
||||
the optimization pass may be a waste of time and resources. In that case, turn optimization off
|
||||
by setting the optimization level to [`OptimizationLevel::None`].
|
||||
|
||||
Alternatively, turn off optimizations via the [`no_optimize`] feature.
|
||||
|
||||
```rust
|
||||
let engine = rhai::Engine::new();
|
||||
|
||||
// Turn off the optimizer
|
||||
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||
```
|
36
doc/src/engine/optimize/eager.md
Normal file
36
doc/src/engine/optimize/eager.md
Normal file
@@ -0,0 +1,36 @@
|
||||
Eager Function Evaluation When Using Full Optimization Level
|
||||
==========================================================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
When the optimization level is [`OptimizationLevel::Full`], the [`Engine`] assumes all functions to be _pure_ and will _eagerly_
|
||||
evaluated all function calls with constant arguments, using the result to replace the call.
|
||||
|
||||
This also applies to all operators (which are implemented as functions).
|
||||
|
||||
For instance, the same example above:
|
||||
|
||||
```rust
|
||||
// When compiling the following with OptimizationLevel::Full...
|
||||
|
||||
const DECISION = 1;
|
||||
// this condition is now eliminated because 'DECISION == 1'
|
||||
if DECISION == 1 { // is a function call to the '==' function, and it returns 'true'
|
||||
print("hello!"); // this block is promoted to the parent level
|
||||
} else {
|
||||
print("boo!"); // this block is eliminated because it is never reached
|
||||
}
|
||||
|
||||
print("hello!"); // <- the above is equivalent to this
|
||||
// ('print' and 'debug' are handled specially)
|
||||
```
|
||||
|
||||
Because of the eager evaluation of functions, many constant expressions will be evaluated and replaced by the result.
|
||||
This does not happen with [`OptimizationLevel::Simple`] which doesn't assume all functions to be _pure_.
|
||||
|
||||
```rust
|
||||
// When compiling the following with OptimizationLevel::Full...
|
||||
|
||||
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'
|
||||
```
|
24
doc/src/engine/optimize/optimize-levels.md
Normal file
24
doc/src/engine/optimize/optimize-levels.md
Normal file
@@ -0,0 +1,24 @@
|
||||
Optimization Levels
|
||||
==================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
Set Optimization Level
|
||||
---------------------
|
||||
|
||||
There are actually three levels of optimizations: `None`, `Simple` and `Full`.
|
||||
|
||||
* `None` is obvious - no optimization on the AST is performed.
|
||||
|
||||
* `Simple` (default) performs only relatively _safe_ optimizations without causing side-effects
|
||||
(i.e. it only relies on static analysis and will not actually perform any function calls).
|
||||
|
||||
* `Full` is _much_ more aggressive, _including_ running functions on constant arguments to determine their result.
|
||||
One benefit to this is that many more optimization opportunities arise, especially with regards to comparison operators.
|
||||
|
||||
An [`Engine`]'s optimization level is set via a call to `Engine::set_optimization_level`:
|
||||
|
||||
```rust
|
||||
// Turn on aggressive optimizations
|
||||
engine.set_optimization_level(rhai::OptimizationLevel::Full);
|
||||
```
|
17
doc/src/engine/optimize/reoptimize.md
Normal file
17
doc/src/engine/optimize/reoptimize.md
Normal file
@@ -0,0 +1,17 @@
|
||||
Re-Optimize an AST
|
||||
==================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
If it is ever needed to _re_-optimize an `AST`, use the `optimize_ast` method:
|
||||
|
||||
```rust
|
||||
// Compile script to AST
|
||||
let ast = engine.compile("40 + 2")?;
|
||||
|
||||
// Create a new 'Scope' - put constants in it to aid optimization if using 'OptimizationLevel::Full'
|
||||
let scope = Scope::new();
|
||||
|
||||
// Re-optimize the AST
|
||||
let ast = engine.optimize_ast(&scope, &ast, OptimizationLevel::Full);
|
||||
```
|
43
doc/src/engine/optimize/semantics.md
Normal file
43
doc/src/engine/optimize/semantics.md
Normal file
@@ -0,0 +1,43 @@
|
||||
Subtle Semantic Changes After Optimization
|
||||
=========================================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
Some optimizations can alter subtle semantics of the script.
|
||||
|
||||
For example:
|
||||
|
||||
```rust
|
||||
if true { // condition always true
|
||||
123.456; // eliminated
|
||||
hello; // eliminated, EVEN THOUGH the variable doesn't exist!
|
||||
foo(42) // promoted up-level
|
||||
}
|
||||
|
||||
foo(42) // <- the above optimizes to this
|
||||
```
|
||||
|
||||
If the original script were evaluated instead, it would have been an error - the variable `hello` does not exist,
|
||||
so the script would have been terminated at that point with an error return.
|
||||
|
||||
In fact, any errors inside a statement that has been eliminated will silently _disappear_:
|
||||
|
||||
```rust
|
||||
print("start!");
|
||||
if my_decision { /* do nothing... */ } // eliminated due to no effect
|
||||
print("end!");
|
||||
|
||||
// The above optimizes to:
|
||||
|
||||
print("start!");
|
||||
print("end!");
|
||||
```
|
||||
|
||||
In the script above, if `my_decision` holds anything other than a boolean value,
|
||||
the script should have been terminated due to a type error.
|
||||
|
||||
However, after optimization, the entire `if` statement is removed (because an access to `my_decision` produces
|
||||
no side-effects), thus the script silently runs to completion without errors.
|
||||
|
||||
It is usually a _Very Bad Idea™_ to depend on a script failing or such kind of subtleties, but if it turns out to be necessary
|
||||
(why? I would never guess), turn script optimization off by setting the optimization level to [`OptimizationLevel::None`].
|
19
doc/src/engine/optimize/side-effects.md
Normal file
19
doc/src/engine/optimize/side-effects.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Side-Effect Considerations for Full Optimization Level
|
||||
====================================================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
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 any side-effects, with the exception of `print` and `debug` which are handled specially) so using
|
||||
[`OptimizationLevel::Full`] is usually quite safe _unless_ custom types and functions are registered.
|
||||
|
||||
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 overload built-in operators, they will also be called when the operators are used
|
||||
(in an `if` statement, for example) causing side-effects.
|
||||
|
||||
Therefore, the rule-of-thumb is:
|
||||
|
||||
* _Always_ register custom types and functions _after_ compiling scripts if [`OptimizationLevel::Full`] is used.
|
||||
|
||||
* _DO NOT_ depend on knowledge that the functions have no side-effects, because those functions can change later on and, when that happens, existing scripts may break in subtle ways.
|
16
doc/src/engine/optimize/volatility.md
Normal file
16
doc/src/engine/optimize/volatility.md
Normal file
@@ -0,0 +1,16 @@
|
||||
Volatility Considerations for Full Optimization Level
|
||||
===================================================
|
||||
|
||||
{{#include ../../links.md}}
|
||||
|
||||
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 environment and is not _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`], will _merrily assume_ that all functions are _pure_,
|
||||
so when it finds constant arguments (or none) it eagerly executes the function call and replaces it with the result.
|
||||
|
||||
This causes the script to behave differently from the intended semantics.
|
||||
|
||||
Therefore, **avoid using [`OptimizationLevel::Full`]** if non-_pure_ custom types and/or functions are involved.
|
Reference in New Issue
Block a user