Full Release of Parallel Luau V1

If you place your Components under their own Actors, this would work.

2 Likes

Why are Actors in the Instance hierarchy ? Mandating that scripts be parented in the Instance hierarchy to an actor is pretty irritatingā€¦ Why canā€™t we have an ActorsService or something in which we can set up our actors that way ? Or perhaps Actor Instances themselves have set functionsā€¦

3 Likes

Since communication between Serial Script <ā€”> Parallel ModuleScript isnā€™t possible, and with BindableFunctions being extremely strenuous (see below), having a way to read tables becomes very crucial. To ensure no mutability on those tables is allowed, the tables could be verified on whether theyā€™re frozen or not before theyā€™re passed along to actors.
The reason I find communication so important is because Iā€™m currently using BindableFunctions to share large tables to process, which have ~200 sub-tables that themselves are nested with tons of values. Just the process of sending and having the BindableFunction deep-copy those tables to each actor takesā€¦ this long, per frame:
image
Itā€™s currently just not possible to efficiently communicate information to actors, and so Iā€™m practically unable to use this whatsoever.

You donā€™t need actors to run parallel, but you will if you intend to modify properties when that feature becomes available. The purpose of actors, as far as I understand it, is to provide a sort of safe-space for parallel threads to operate and execute. Actors being an instance also allows developers to swap the model class with the actor class for easier implementation.

Iā€™m just hoping that the ability to modify properties within an actor will happen soon, because one of my projects is heavily dependent on that ability and canā€™t truly progress without it. (Specifically Position or CFrame, as well as creating or cloning instances, or really any part manipulation within a parallel actor.)

Iā€™m somewhat confused by this, can someone explain how does this works behind the scene in relation to the task scheduler?

The guide article that was posted mentioned that the execution is ran on 2 phases, but now you can de/resync multiple times in a frame which seems to contradict thisā€¦?

2 Likes

You could always recursively split those 200 subtables into single tables and send them to another actor. If the actor receiving them knows how to process incoming tables you can likely get better performance.

update: didnā€™t see you were using bindable functions. Iā€™m pretty sure only bindable events can be fired in parallel - Actors only really need to communicate in one direction (unless you tell an actor to echo back via a message). So, Iā€™m assuming youā€™ll also see performance gains by using just bindable events.

Scripting isnā€™t a big thing for me, but please correct me if Iā€™m wrong, does this mean you are capable of syncing Roblox audios by making them play at the same time by using this new feature?

Sound:Play is unsafe to call in parallel, but I believe you can still sync audio in a singular thread anyways.

@zeuxcg
How do I get any actors parented to Workspace to execute behaviors in parallel on the client?
This seems like a major design flaw, and Iā€™m kinda stumped.

Iā€™m working on a library that makes it easy to write client-side behavior code in a way thatā€™s compatible with StreamingEnabled. The approach uses generators with cancelable yielding functions (e.g. WaitForChild) in a way that makes the code super easy to reason about and prevents memory leaks for you.

However, when I have an Actor in workspace, and I want some client-side behavior to run under that actor, how can I accomplish this at all?

It would be super nice if there were some way to spawn a task thatā€™s assigned to an actor (as opposed to a Script itself).

for example:

task.spawnWithActor(actor: Actor, cb: (...any) -> (...any), ...: any): thread

Some solution like this would make it so much easier to make use of these optimizations on the client. Right now it seems as though if your game is heavy on effects/behaviors on instances in workspace, youā€™ll have to go single-threaded for this client-side code. Iā€™m not even sure what domains of client side code you can even feasibly run parallel code that uses Actors other than UI, and even then you have to place ScreenGuis underneath the Actors themselves and replace the whole UI every time the player respawns, which doesnā€™t really lend itself to massive parallelism.

note: I would definitely make this a part of the task API and NOT a part of the coroutine API because coroutines can hide error messages, and debugging is super important when writing threadsafe/parallel code.

Of course, right now the current implementation ties a whole script to an actor rather than a thread. Is there any technical reason why it canā€™t be the threads that are tied to an Actor, and if not, wouldnā€™t it make sense to have APIs that allow some way to spawn threads assigned to an Actor? If itā€™s an issue with closures, could you have make it a hard requirement that the spawned thread cannot be a closure?

6 Likes

Assuming that this was in a script which was in an Actor that did some work in parallel, would this result in any memory leaks or undefined behaviour?

script.Parent.BindableFunction.OnInvoke = function(...)
	task.desynchronize()
	-- do something
	return ...
end

Based on current tests, thereā€™s no undefined behaviour but, what happens if you desynchronise several times without synchronising?

You could always just ā€œspawnā€ an actor that acts as a middleman between your Workspace actors and Client actors. But I agree, having to (potentially) sub-divide systems like yours isnā€™t a very fun way to do things since it really makes things reliant on Actors being cohesive and fault tolerant. I think a potential benefit for the client would be to be able to read input from the client in parallel.

I was trying to make a worker thread module but, it appears that you canā€™t send functions from one VM into another, it makes sense why this is the case but, it would be pretty nice if exceptions were made for pure functions which donā€™t have any upvalues at all allowing developers to send them over to different actors which could then behave like worker threads; the current work around is somewhat hacky and, you need to use modules which a script within an actor needs to require and, the master script (which is dispatching jobs to workers) has to use a key to allow the actor to find out which function itā€™s meant to execute.

4 Likes

Please allow us to spawn ā€œactorsā€ inside of scripts themselves. I would imagine it would function similarly to how you create coroutines, except it would be parallel actors.

I cannot use parallel Luau until such a feature is added because my entire codebase is built on module scripts requiring other module scripts, and it is not suitable to have dedicated scripts inside actors for performing specific calculations only.

8 Likes

Currently Workspace doesnā€™t really execute local scripts outside of cases like characters; this is unrelated to Actors and weā€™re looking into this. Right now youā€™d need to use a different top-level container for this that does execute local scripts.

Weā€™re looking into this as well but it may result in semantics that are complex enough that itā€™s not worth it due to the fact that you wonā€™t be able to access any locals defined outside of the function, and you might have to use an anonymous function. This, in addition to restrictions like inability to use require in parallel, may make this impractical.

4 Likes

There are still two phases, but each phase can happen more than once per frame.

3 Likes

If using sleitnickā€™s component system, this isnā€™t possible. It is simply a module script that binds functions to objects that are added to the game. Thus, whichever script requires the component framework would be where the actor is. As such, you cannot use parallel.

2 Likes

This is a feature iā€™ve already planted into most of the software I develop, Itā€™s good to know that software will now take advantage of it.

Though, something that I ran into a few days before is, what if you need to call something in a Non-Parallel thread repeatedly? Would you go about this by calling task.sync, desync repeatedly?

My solution to this was to invent a parallel scheduler :smile:

Each sync ā†’ desync transition is somewhat costly so ideally youā€™d minimize those (the optimal number is 0 or 1 :wink: ).

For example, if youā€™re writing code that has to move 100 parts of a model to different locations and computing target locations for each part takes some time, this would be a bad way to go about this:

for i=1,#parts do
    local cf = computeCFrame(i)
    task.synchronize()
    parts[i].CFrame = cf
    task.desynchronize()
end

Instead, you should perform as much work as possible and then synchronize and apply the changes:

local newcf = table.create(#parts)

for i=1,#parts do
    newcf[i] = computeCFrame(i)
end

task.synchronize()

for i=1,#parts do
    -- note: here you can use BulkMoveTo but I'm illustrating a general concept
    parts[i].CFrame = newcf[i]
end
4 Likes

I want to ask, should I replace all script-containing models with actors? If so, should the same be done for module scripts?

2 Likes

How does one obtain the same module instance across different actors? I have a module in my game that is responsible for holding save data for players but from my understanding, if I call this module from different actors, each actor gets a different instance of the module.