Parallel Lua Beta

I hope this will scale with the amount of players or in some other way. A server with around 10 players might not even need multi-threading, but it is a must on servers with over 50 players. Also, some genres like RTSs would benefit much more than more casual ones.

I’ve made some upgrades to my parallel terrain generator since the previous revision.

It’s hard to really show it off in one clip though, best to check it out for yourself!
ParallelTerrain.rbxl (55.4 KB)

Hopefully I can test this in a live game soon.

44 Likes

good stuff. ive been wondering what this actor stuff that ive been seeing around was. finally the uncomfortable feeling of crunching a lot of numbers in a synchronous thread is gone, at least partially. the explanation was a little confusing, though; why is writing to instances restricted? id think that as soon as an instance is written to that write would be visible to other scripts like normal. if this isnt the case, why? if it is the case, whats the problem?

1 Like

I’m curious how you’re going to approach this given how Luau VM stacks currently work with ModuleScripts.

It would be nice if the synchronised phase could access the root global state of a module though, whether thats possible or not, is another question

1 Like

The reason scripts under an Actor have a different view of module scripts even during the synchronous phase is because a native lua object can’t move between VMs. The module root is a lua table, so it would have to be copied by value into the other VM, which is expensive and likely not what you want.

The shared storage will likely be an object that isn’t a native lua object and is thread safe.

5 Likes

I saw you typing btw, also with the allowance of multiple threads have an impact on improving FPS and increasing render distance?

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.

3 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?

2 Likes