Performance Build ================= {{#include ../../links.md}} Some features are for performance. In order to squeeze out the maximum performance from Rhai, the following features should be considered: | Feature | Description | Rationale | | -------------------- | ------------------------------------------------------ | ------------------------ | | [`only_i32`] | support only a single `i32` integer type | reduce data size | | [`no_float`] | remove support for floating-point numbers | reduce code size | | [`f32_float`] | set floating-point numbers (if not disabled) to 32-bit | reduce data size | | [`no_closure`] | remove support for [variables] sharing | no need for data locking | | [`unchecked`] | disable all safety [checks][checked] | remove checking code | | [`no_module`] | disable loading external [modules] | reduce code size | | [`no_position`] | disable position tracking during parsing | reduce data size | | [`no_custom_syntax`] | disable [custom syntax] | reduce code size | When the above feature flags are used, performance may increase by around 15-20%. ~~~admonish info.small "See also: Benchmarks" See Rhai performance [benchmarks]. ~~~ Unchecked Build --------------- By default, Rhai provides a [_Don't Panic_](https://en.wikipedia.org/wiki/Phrases_from_The_Hitchhiker%27s_Guide_to_the_Galaxy#Don't_Panic) guarantee and prevents malicious scripts from bringing down the host. Any panic can be considered a bug. For maximum performance, however, these [safety] checks can be turned off via the [`unchecked`] feature. Fast Operators Mode ------------------- Make sure that [_Fast Operators_ Mode][options], which is enabled by default, is on. It ignores any user [overloading][operator overloading] of [built-in operators]. For operator-heavy scripts, this may provide a substantial speed-up. Use Only One Integer Type ------------------------- If only a single integer type is needed in scripts – most of the time this is the case – it is best to avoid registering lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster because fewer functions need to be loaded. The [`only_i32`] and [`only_i64`] features disable all integer types except `i32` or `i64` respectively. Use Only 32-Bit Numbers ----------------------- If only 32-bit integers are needed – again, most of the time this is the case – turn on [`only_i32`]. Under this feature, only `i32` is supported as a built-in integer type and no others. On 64-bit targets this may not gain much, but on certain 32-bit targets this improves performance due to 64-bit arithmetic requiring more CPU cycles to complete. Minimize Size of `Dynamic` -------------------------- Turning on [`f32_float`] (or [`no_float`]) and [`only_i32`] on 32-bit targets makes the critical [`Dynamic`] data type only 8 bytes long for 32-bit targets. Normally [`Dynamic`] needs to be up 12-16 bytes long in order to hold an `i64` or `f64`. A smaller [`Dynamic`] helps performance due to better cache efficiency. Use `ImmutableString` --------------------- Internally, Rhai uses _immutable_ [strings] instead of the Rust `String` type. This is mainly to avoid excessive cloning when passing function arguments. Rhai's internal string type is [`ImmutableString`] (basically `Rc` or `Arc` depending on the [`sync`] feature). It is cheap to clone, but expensive to modify (a new copy of the string must be made in order to change it). Therefore, functions taking `String` parameters should use [`ImmutableString`] or `&str` (maps to [`ImmutableString`]) for the best performance with Rhai. Disable Capturing in Closures ----------------------------- ```admonish info.side "Anonymous functions still work" [Anonymous functions] continue to work even under [`no_closure`]. Only capturing of external shared [variables] is disabled. ``` Support for [closures] that capture _shared_ [variables] adds material overhead to script evaluation. This is because every data access must be checked whether it is a shared value and, if so, take a read lock before reading it. As the vast majority of [variables] are _not_ shared, needless to say this is a non-trivial performance overhead. Use [`no_closure`] to disable support for [closures] to optimize the hot path because it no longer needs to take locks for shared data. Disable Position ---------------- For embedded scripts that are not expected to cause errors, the [`no_position`] feature can be used to disable position tracking during parsing. No line number/character position information is kept for error reporting purposes. This may result in a slightly fast build due to elimination of code related to position tracking. Avoid Cloning ------------- ### Use `&mut` functions Rhai values are typically _cloned_ when passed around, especially into [function] calls. Large data structures may incur material cloning overhead. Some functions accept the first parameter as a mutable reference (i.e. `&mut`), for example _methods_ for [custom types], and may avoid potentially-costly cloning. ### Compound assignment For example, the `+=` (append) compound assignment takes a mutable reference to the [variable] while the corresponding `+` (add) assignment usually doesn't. The difference in performance can be huge: ```rust let x = create_some_very_big_and_expensive_type(); x = x + 1; // ^ 'x' is cloned here // The above is equivalent to: let temp_value = x.clone() + 1; x = temp_value; x += 1; // <- 'x' is NOT cloned ``` ### Use `take` Another example: use the `take` function to extract a value out of a variable (replacing it with [`()`]) without cloning. ```rust let x = create_some_very_big_and_expensive_type(); let y = x; // <- 'x' is cloned here let y = x.take(); // <- 'x' is NOT cloned ``` ```admonish tip "Tip: Simple variable references are already optimized" Rhai's script [optimizer][script optimization] is usually smart enough to _rewrite_ function calls into [_method-call_]({{rootUrl}}/rust/methods.md) style or [_compound assignment_]({{rootUrl}}/language/assignment-op.md) style to take advantage of this. However, there are limits to its intelligence, and only **simple variable references** are optimized. ~~~rust x = x + 1; // <- this statement... x += 1; // ... is rewritten as this x[y] = x[y] + 1; // <- but this is not, so this is MUCH slower... x[y] += 1; // ... than this some_func(x, 1); // <- this statement... x.some_func(1); // ... is rewritten as this some_func(x[y], 1); // <- but this is not, so 'x[y]` is cloned ~~~ ``` Short Variable Names for 32-Bit Systems --------------------------------------- On 32-bit systems, [variable] and [constant] names longer than 11 ASCII characters incur additional allocation overhead. This is particularly true for local variables inside a hot loop, where they are created and destroyed in rapid succession. Therefore, avoid long [variable] and [constant] names that are over this limit. On 64-bit systems, this limit is raised to 23 ASCII characters, which is almost always adequate.