3.4 KiB
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:
{
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:
{
let x = 999;
foo(42);
666
}
Constants Propagation
Constants propagation is used to remove dead code:
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:
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:
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
].