Parallel Scripting

You can take full advantage of your users' devices by splitting your experience's code into different Actors which distribute the load across multiple cores. Using multiple cores allows unrelated code to run simultaneously (parallel) as opposed to sequentially (serial).

A diagram showing how parallel code runs simultaneously

Actors

An Actor is a container for code that can be safely split into its own core. The Actor should also include instances that the code depends on.

When a script is nested within multiple Actors, it is owned by its closest ancestor Actor.

A tree of actors and scripts that shows how a script is owned by its closest actor

For best performance, use as many Actors as possible. Even if the device has fewer cores than Actors, the granularity allows for more efficient load balancing between the cores.

A demonstration of how using more actors balances the load across cores

Desynchronizing

Scripts run serial by default, even when it's within an Actor. To begin running a script in parallel, call task.desynchronize(). To switch a script back to serial execution, call task.synchronize().

Thread Safety

Parallel code might interfere with other code and cause crashes. To prevent this, you can't use certain API members in parallel code. API members have a thread safety level that indicates whether you can use them in parallel code. If an API member does not mention thread safety level, the API member has thread safety level Unsafe.

Safety Level For Properties For Functions
Unsafe Cannot be read or written Cannot be called
Read Parallel Can be read but not written N/A
Local Safe Can be used within the same Actor, can be read but not written to by other Actors Can be called within the same Actor, cannot be called by other Actors
Safe Can be read and written Can be called

Requiring Modules

If you call require() in parallel, then the required ModuleScript executes in parallel as well.

If two scripts within the same Actor require the same ModuleScript, they receive the same object as a result and can share state. If two scripts from different Actors require the same ModuleScript, it isn't possible to predict whether or not they will receive the same object, so modules required by multiple Actors should be stateless and purely functional to prevent unintended behavior. The following example would be unreliable if multiple Actors require it.

To return a new value when calling the same ModuleScript, move the module's contents into a constructor and call the constructor when requiring the module. Calling the constructor guarantees that state isn't shared between Actors and that any side effects run only once per call. The following example has reliable, consistent behavior between Actors.

Execution Phases

Roblox processes logic inside frames. Each frame has two phases: parallel and serial. Even in parallel, long computations can block execution of other scripts and cause lag. Avoid using parallel scripting to handle a large volume of long, unyielding calculations.

A diagram demonstrating how overloading the parallel execution phase can still cause lag