Parallel Lua Beta

The biggest problem with writing to instances at the moment is that changing a property triggers the Changed event, possibly in another VM, that is already running a different thread. We plan to make more properties writable over time.

5 Likes

It’s not the main goal, but it could help with client FPS under some rare circumstances.

4 Likes

I think this update is really good and promises a lot of new possibilities. Well done!

1 Like

Just finished my ECS lib too, I’ll be using this in future projects. Thanks for all the good work!

I love it!
I’ve been waiting for it since the developer preview and im so excited to finally start using it in my projects.
.
.
.
Btw i think you forgot to lock the “task” table :slight_smile:

Is this behavior intentional?
When you require a module and try to connect something in parallel in a function you call from another script it will cause the error “Scripts that connect in parallel must be rooted under an Actor.”, but it works just fine when you do the same from within the module script.

Modulescript:

script.Parent = Instance.new("Actor")
local function thisRunsFine()
	Instance.new("BindableEvent").Event:ConnectParallel(function() end)
end
thisRunsFine() --this works
return thisRunsFine

Script:

local moduleScript = script.ModuleScript
thisCausesAnError = require(moduleScript) --this works
thisCausesAnError() --this causes an error
1 Like

That behavior isn’t intentional per sea. The main purpose of the error is a learning aid, that stops a user who’s new to the system from just changing Connect to ConnectParallel without setting up actors and thinking they have made things parallel. In a module script you might write a function that’s called by both scripts that are and are not under Actors, so for now we don’t throw this informative error. This may change in the future.

4 Likes

Overall, couldn’t understand this a lot, if someone can sum it up that’d be appreciated.

2 Likes

I have to say, I only understood half of what you were saying. I’m just going to wait for a youtube video to come out. Also, are there any resources for a more in-depth explanation of everything? I understand the basic premise of scripts running on different hardware threads, but that’s the limit of my understanding. Will there be an article tutorial about this?

2 Likes

This sounds incredible and all, but I’m just thinking about how on Earth I’m gonna switch my games to this new feature…

Switching them is not necessary nor is something you should be doing unless needed.

2 Likes

Wish there was a more clear and concise description and examples of how the functions exactly work and their intended usecase. The only example really given was with :ConnectParallel, which doesn’t really say much anyways.

After checking over clonetroopers example and eventually making sense of the post, I pretty much figured out some ways it works and its not terribly complicated, just convoluted.

--[[
    This with :ConnectParallel()
    ConnectParallel initiates task.desynchronize without needing to add it manually.
    According to the main post, its a much faster way of running a task in parallel,
    but may not always be convenient personally.
  ]]
local radius = 5

local function Paralleltest()
    local circumference = 2 * math.pi * radius

    local area = (circumference^2)/4*math.pi

    --[[
        I'm not entirely clear on how roblox handles the dividing of tasks between threads,
        given that they actively decided against the ability to manually create threads as
        you would be able to do in other languages such as C. So i presume it automatically
        distributes tasks across threads as it sees fit. So the more complex of a task you're
        running parallel, the better it scales? Otherwise its just a slightly better coroutine.
      ]]

    task.synchronize() --This should sync up the work done and allow for it to be properly read/used.

    --Do note that editing properties is not considered thread safe for good reason, you can only handle math and other non-interactive tasks via parallel

    print(circumference, area)
end

event:ConnectParallel(Paralleltest) 
--This is assuming its connected to an event of some sorts, a bindable or heartbeat are both valid examples


--This is with task.desynchronize()

local function paralleltask()
    task.desynchronize() --At least i think this is how its done? The documentation is extremely vague and its worked in testing so correct me if i am wrong

    --[[
        A roughly similar idea as with ConnectParallel, although its manually 
        invoked as opposed to automatically running via an event. This is 
        likely a useful way of handling heavy math orientated tasks, or the sorting
        of large tables. I imagine this could also do wonders for pathfinding API's,
        as many of them struggle with being bottlenecked by a single core.
      ]]

    local circumference = 2 * math.pi * radius

    local area = (circumference^2)/4*math.pi

    task.synchronize()

    print(circumference, area)
end

paralleltask()


--[[
    In practice, the implementation seems rather simple, albeit unnecessary for those who don't want to rewrite code to handle it.
    Its really only practical if you need it, otherwise its superfluous. But I'd suggest trying to implement it anyways if you
    have a lot of stuff handled on the server and its causing some performance bottlenecks. I'm sure theres more to it though, 
    so it might be worth waiting until its officially implemented rather than 
   trying to learn it now while theres room for it to radically change.
]]
11 Likes

Any chance that this will support GuiObjects as it’s childs?
As of how it is right now you can only add ScreenGuis as its child, not Frames, Labels, Buttons or TextBoxes.

1 Like

I have a bunch of code that I have running on the client that I would like to have multithreaded in the near future. Since LocalScripts can’t run under workspace unless they’re in the local player’s character model, how would we go about having multithreaded local code that retrieves information about a model and its descendants? I could be understanding Actors wrong, but don’t the scripts need to be inside of the actor they’re reading/writing to?

3 Likes

I found a really weird bug that causes some sort of memory leak that persists between playtesting sessions:

LocalScript inside of an actor in StarterPlayerScripts:

for i = 1,250 do
	RunService.RenderStepped:ConnectParallel(function()
		task.synchronize()
	end)
end

LuaHeap will continue to rise until it reaches 2000+ Mb, but the weirdest part is that if you stop the game and play again, LuaHeap will keep rising from wherever it left off. I tested this in the developer preview build and it doesn’t happen there, only on the new beta in the live version of studio.

ParallelLuaBugRepro.rbxl (22.3 KB)

So far I have managed it to completely crash Windows several times :sunglasses:
When ran on a more… competent machine, it completely accelerated my terrain generation system.

I have tripled the speed of the my terrain generation system while making it more stable and it no longer makes the server laggy while generating planets. 5.313 seconds to fully initialize and spawn the terrain of a 2048 x 512 x 2048 section of land.

What exactly can we expect from this proposed shared storage? Will it be instance based or will we be able to write tables to it for read only purposes?

Currently I have a custom grid and pathfinding algorithm and being able to multi thread this would be a great help. However, the pathfinding algorithm wouldn’t be able to access the grid data if run in parallel currently from my understanding. The only way I could transfer this information at the moment without the massive overhead of sending it through bindables would be by making the grid out of parts and trying to convey information through that and value objects. This would be incredibly unwieldy for obvious reasons.

Some kind of service with which we can write to from the main thread and read from in parallel threads would be greatly beneficial. Can we expect anything similar to that or give us any other ideas as to what to expect?

4 Likes

Do servers have access to multiple threads? I ask this because I want to know if it’s useful to implement parallel lua on the server.

I noticed that parallel tasks are always processed before gameStepped in the frame pipeline. This means when we call ConnectParallel on heartbeat, the actual processing is delayed until the next frame. Are there any plans to have an option to start a WaitingParallelScriptsJob at any point in the pipeline? It would be nice to have some background tasks running in heartbeat that won’t delay physics, or code that we have bound to gameStepped. Alternatively we might have stuff we want to process in parallel during PreRender, like calculations for visual effects to be applied before rendering.

Ultimately giving us more control over where to run tasks in parallel will allow us to make the most of this powerful tool.

Will we ever have access to the number of threads available? And will we ever have access to manually assigning jobs to specific threads?

In my situation, I want to evenly distribute a set amount of jobs that take a varied amount of time (which I have a rough estimate for). Back on the main thread, it will yield until all jobs are completed to then handle the processed data. I’m not exactly sure how Roblox determines what runs on what thread, but I don’t believe they are going to have better distribution than me (considering I know how long each job will take). The main problem with this is that I could have 20/28 jobs already completed on 11/12 threads, while that 1 final thread still needs to process 8 more jobs.

Are there any plans to address a situation like this?

EDITS: Just to clarify, each job isn’t taking 4-5 seconds and yielding inside, it’s performing calculations and I have a good estimate of how long it will take to process.

Technically each Actor is a thread, but if you have more actors than threads, Roblox will start doing some dynamic distribution? This means that if we have access to the number of threads on the user’s system, we also have the ability to control job distribution?

I just want to add that it would be super helpful to have a RunService:IsThread() function, or at least something similar to see what environment the code is currently running in.