Question About Parallel Lua, Script & Actor Parenting (and parallel not helping optimization?)

Hopefully, this first part is a somewhat quick question to answer. Shockingly, I cannot find documentation anywhere that explains this. There’s a second, more lengthy explanation and issue below the main question of my topic.

So, it is stated that a ServerScript needs to have an Actor above it somewhere in order to get thrown into parallel when you run task.desynchronize(). All scripts together underneath the Actor will run in series with eachother. This is easily understood.

However, do all of the scripts need to be direct children of the Actor, or can they be deeper inside the rig somewhere?

“Deeper” example (this made zero difference on performance of the CalculateSpeeds script, read below):
image

Direct child example (wouldn’t be an ideal solution if this is required, but I am willing to try working with it if necessary):
image

If being a direct child is required, it will add extra steps within the script itself as you will have to put them in specific places, meaning you can’t just “script.Parent” a part, you’d have to add extra text to specifically find said part.

I should mention why I ask this question, and maybe it is a more lengthy answer this time.

I have a game that includes multiple automatic trains. These trains run on the server (they force the Network Owner to the server) and control their speed, stops, etc. all on their own. The trains work based on sensors along the track, so they only have to run code when a trigger on the train touches a sensor.

The most intensive part of the code is a loop that gradually slows down (or speeds up) the train, though the loop only activates and runs when the speed needs to be changed. This loop simply sets a NumberValue, and a separate script elsewhere adjusts the velocity of the train’s movement. (speed.Value is the current speed it’s moving, set.Value is the target speed, canLoop is a debounce protection and also an easy way to kill the loop if necessary)
image

A problem I have had is that, when some other things are happening in the game, this loop slows down and causes the trains to decelerate way too slowly. Having never touched parallel lua before, I started testing, because it seems like a good solution, even if it isn’t perfect.

Well, what I did was change the train’s main parent model from a Model to an Actor, and whenever the loop is about to run, the script calls task.desynchronize().

However, upon testing, I noticed zero difference in game performance, or more importantly, performance of the trains. With only 3 or 4 of these trains in the game at the time, and the rest of the game not using any parallel whatsoever, you’d think these trains would get their own cores and be able to run almost flawlessly.

Did I do something wrong, does this not work for my use case, or is the problem that my scripts are not the direct child of the Actor?

2 Likes

I’m so glad to see someone else use Actors for parallel Luau.

Yes, scripts must be parented under the Actor instance in order to run it in parallel. It’s an inconvenience for many of us, and I think that Roblox can definitely structure this better. Sorry if this was not the good news you wanted to know, but we’re limited to what Roblox does with the engine lol.

What I do know, is that you can design your own tool to make this easier for you; a library that can automate this process, but does the same thing. This has been done before, and hopefully Roblox will change this process in the future.

2 Likes

Well, it’s going to take a few extra lines of code to do stuff like “local part = script.Parent.PartA” however it is great to have this answer still. I thought this was going to have zero chance at working for me because I followed the documentation and saw literally no difference.

I’m going to rerun my test with the speed scripts directly underneath Actors and see if it finally helps my case.

Hey, anything other than “your use case doesn’t work” is good news to me, or at the very least better than my current situation. Most of the scripts in my games don’t have to be perfectly on time, so my plan is to use Parallel mostly for the specific scripts that absolutely require every chance they can get at being well timed.

Roblox should add this stipulation to their documentation about Parallel Lua to help people be less confused. “Scripts running in parallel must be the direct child of an Actor instance.”

Thank you for writing in!

1 Like

Glad I could give some info, I will definitely bring this up in the next RDC, I’m glad I’m not the only person who has the same opinion about how Actors and parallel luau is restricted in such a way.

1 Like

It’s honestly a strange restriction to have. If the whole purpose of the Actor instance is to replace the Model instance you’d normally use for a rig that you want to run in parallel, it makes almost no sense to force every single script to be in the exact same location within that rig.

1 Like

A possible solution you could try with the Actor, is that if you have multiple trains, you could technically desync the tasks by train class within that speed script through a for loop. Not ideal especially since that kind of defeats the purpose of desync, but the performance should at least be 1% faster since you’re using multithreading to handle each train’s speed. Maybe even throw in task.spawn in there.

Otherwise, all performance-based improvements will be based on your code’s runtime complexity.

Each train has its own calculations script, each under an actor within their own models, so I think they essentially already do as you’re describing.

I’m testing right now, and having the speed calc script as a direct child has now done what I was hoping for.

Now, the only thing I need to figure out, is how to reduce physics lag when there’s several server controlled trains moving around at once so they don’t end up only moving at 20sps when they think they’re going 100.

I just realized that everything I did, actually did absolutely nothing. I didn’t realize that wait() returns the script to serial, therefore it never actually runs in parallel. To make matters worse, you can’t write to values in parallel, making the entire concept of Parallel Lua completely and utterly pointless in every use case of mine. This was supposed to be some giant Roblox engine improvement with great promise. Nope, just another disappointment. I guess I can only hope they fix it someday.

I turned the solution box off to hopefully keep the topic from closing, as I now need some more advise on the subject.

What are you doing in the parallel scripts?

Putting things inside an actor isn’t a magical performance boost. Often going serial->parallel requires a lot of effort. I’d even say you can get worse performance when starting the swap!

The key is understanding what should and should not be done in parallel. Some scripts aren’t fit for parallel optimization.

The loop you showed is a simple calculation, with a value set. Setting external values can’t be done in parallel, so the only thing that’ll be parallel is the calculation.

This is a VERY fast calculation for a computer. Probably within microseconds. This won’t cause a noticeable performance increase.

There is a different loop that runs in the game, which gets the magnitude of the velocity of a part and pitches/plays a wheel rolling audio based on the speed it’s moving. The thing is, there can be several hundred of these running at once, because that’s how many railroad cars may be moving (regardless if it’s an automatic server train, or most of the time, a player train). These constant calculations could really do with Parallel operations, however it is physically impossible because it involves constantly changing the properties of the sounds.

The purpose of wanting to parallel the small loop shown is because of just how important it is that the loop runs on time, so operating it on an Actor would help pull it away from everything else (such as the sound script noted above).

Does this really need to be optimized?
Has this caused performance issues?
Are you sure it’s actually that script that’s causing performance issues?

These are important questions. With performance optimization it’s important that you find the issue first.

A simple speed check, calculation seems very fast too. The overhead from syncing (exiting parallel execution) is probably bigger than the time you save through computation.

2 Likes

Yes, the speed/volume calculations need to be.

Yes, they have, because there are many which can run at the same time.

Yes, because nothing else causes the slow downs.

The idea is to make the script operate 100% in parallel. There should never be a need to resync the script. However, Roblox doesn’t allow writing properties in parallel, so this script is essentially impossible to optimize.

Parallel writes aren’t allowed. This isn’t specific to roblox. Writing in parallel can cause non-deterministic behavior, when other scripts read those properties.

You could try having a parallel script that notifies a serial listener script which sets the values in serial execution. See here for more info: Parallel Luau | Documentation - Roblox Creator Hub

If all you’re doing is a single-line speed calculation, I highly doubt that’s actually the performance issue…
Without more info on the scripts we can’t help much.

1 Like