110 lines
3.4 KiB
Markdown
110 lines
3.4 KiB
Markdown
Script Optimization
|
|
===================
|
|
|
|
{{#include ../../links.md}}
|
|
|
|
Rhai includes an _optimizer_ that tries to optimize a script after parsing.
|
|
This can reduce resource utilization and increase execution speed.
|
|
|
|
Script optimization can be turned off via the [`no_optimize`] feature.
|
|
|
|
|
|
Dead Code Removal
|
|
----------------
|
|
|
|
For example, in the following:
|
|
|
|
```rust
|
|
{
|
|
let x = 999; // NOT eliminated: variable may be used later on (perhaps even an 'eval')
|
|
123; // eliminated: no effect
|
|
"hello"; // eliminated: no effect
|
|
[1, 2, x, x*2, 5]; // eliminated: no effect
|
|
foo(42); // NOT eliminated: the function 'foo' may have side-effects
|
|
666 // NOT eliminated: this is the return value of the block,
|
|
// and the block is the last one so this is the return value of the whole script
|
|
}
|
|
```
|
|
|
|
Rhai attempts to eliminate _dead code_ (i.e. code that does nothing, for example an expression by itself as a statement,
|
|
which is allowed in Rhai).
|
|
|
|
The above script optimizes to:
|
|
|
|
```rust
|
|
{
|
|
let x = 999;
|
|
foo(42);
|
|
666
|
|
}
|
|
```
|
|
|
|
|
|
Constants Propagation
|
|
--------------------
|
|
|
|
Constants propagation is used to remove dead code:
|
|
|
|
```rust
|
|
const ABC = true;
|
|
if ABC || some_work() { print("done!"); } // 'ABC' is constant so it is replaced by 'true'...
|
|
if true || some_work() { print("done!"); } // since '||' short-circuits, 'some_work' is never called
|
|
if true { print("done!"); } // <- the line above is equivalent to this
|
|
print("done!"); // <- the line above is further simplified to this
|
|
// because the condition is always true
|
|
```
|
|
|
|
These are quite effective for template-based machine-generated scripts where certain constant values
|
|
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
|
|
to the [`Engine`] for use in compilation and evaluation.
|
|
|
|
|
|
Watch Out for Function Calls
|
|
---------------------------
|
|
|
|
Beware, however, that most operators are actually function calls, and those functions can be overridden,
|
|
so they are not optimized away:
|
|
|
|
```rust
|
|
const DECISION = 1;
|
|
|
|
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 == 3 { // same here, NOT optimized away
|
|
:
|
|
} else {
|
|
:
|
|
}
|
|
```
|
|
|
|
because no operator functions will be run (in order not to trigger side-effects) during the optimization process
|
|
(unless the optimization level is set to [`OptimizationLevel::Full`]).
|
|
|
|
So, instead, do this:
|
|
|
|
```rust
|
|
const DECISION_1 = true;
|
|
const DECISION_2 = false;
|
|
const DECISION_3 = false;
|
|
|
|
if DECISION_1 {
|
|
: // this branch is kept and promoted to the parent level
|
|
} else if DECISION_2 {
|
|
: // this branch is eliminated
|
|
} else if DECISION_3 {
|
|
: // this branch is eliminated
|
|
} else {
|
|
: // this branch is eliminated
|
|
}
|
|
```
|
|
|
|
In general, boolean constants are most effective for the optimizer to automatically prune
|
|
large `if`-`else` branches because they do not depend on operators.
|
|
|
|
Alternatively, turn the optimizer to [`OptimizationLevel::Full`].
|