Native Code Generation
Supported platforms:
Available since: Gideros 2024.11
Description
With Luau support for native code generation, Gideros scripts can be compiled directly into the machine code instructions that CPUs execute, rather than regular bytecode that the Luau VM operates on. This feature can be used to improve execution speed for some scripts, in particular those that have a lot of numerical computation without using too many heavy Luau library or Gideros API calls.
Enabling Native
To enable native code generation for a Script, add the --!native comment at the top:
--!native
print("Hello from native code!")
This enables native code generation for all functions in the script, and the top-level scope, if deemed profitable. No additional changes are required; behavior of the natively executing scripts is exactly the same as before and only the performance is different. All features of the Luau language and all Gideros APIs remain supported.
Alternatively, you can enable native code generation for an individual function by adding the @native attribute:
@native
local function f(x)
return (x + 1)
end
Best Practices
The following tips will help you benefit most from native code generation:
- It's best to enable this feature inside scripts that perform a lot of computation directly inside Luau. If you have a lot of mathematical operations on tables and especially buffer types, the script may be a good candidate.
- Only the script's functions are compiled natively. The code in the top outer scope is often executed only once and doesn't benefit as much as functions that are called many times, especially those that are called every frame.
- It's recommended that you measure the time a script or a function takes with and without native compilation to judge when it's best to use it. The Script Profiler tool can measure the performance of functions in order to make informed decisions.
- It may be tempting to place the --!native comment in every script just in case some of them will execute faster, but native code generation has some drawbacks:
- Code compilation time is required which can increase the startup time of servers.
- Extra memory is occupied to store natively compiled code.
- There's a limit on the total allowed amount of natively compiled code in an experience.
These problems can be addressed by a judicious use of the @native attribute.
Code to Avoid
While all features will behave the same with or without native code generation enabled, some of them will not run natively and might cause de‑optimization or a fallback to interpreted execution. These include:
- Use of deprecated getfenv()/setfenv() calls.
- Use of various Luau built‑in functions like math.asin() with non‑numeric arguments.
- Passing improperly typed parameters to typed functions, for example calling foo(true) when foo is declared as function foo(arg: string). Remember to always use correct type annotations.
When using the Script Profiler, you can compare time taken by a regular version of the function versus the one compiled natively. If a function inside a --!native script or marked with @native doesn't appear to be natively executing, one or more factors from the list above may be triggering de‑optimization.
Using Type Annotations
Native code generation attempts to infer the most likely type for a given variable in order to optimize code paths. For example, it's assumed that a + b is performed on numbers, or that a table is accessed in t.X. Given operator overloading, however, a and b may be tables or Vector3 types, or t may be a Luau datatype.
While native code generation will support any type, mispredictions may trigger unnecessary checks, resulting in slower code execution.
To solve some common issues, Luau type annotations on function arguments are checked, but it's especially recommended to annotate Vector3 arguments:
--!native
-- "v" is assumed to be a table; function performs slower due to table checks
local function sumComponentsSlow(v)
return v.X + v.Y + v.Z
end
-- "v" is declared to be a Vector3; code specialized for vectors is generated
local function sumComponentsFast(v: Vector3)
return v.X + v.Y + v.Z
end
Example
local num = 1
local time1 = os.clock()
local time2
@native
function rep()
repeat
num += 1
until num == 10^9
time2 = os.clock()
end
rep()
print(num, time2 - time1)
-- results:
--- without @native: 1000000000 11.400012800000695
--- with @native: 1000000000 7.631150999997772
Disclaimer
Documentation is based on create.roblox.com documentation.
https://create.roblox.com/docs/luau/native-code-gen