Weave - A Job-based General-purpose Parallelism Library

:construction: IN BETA PHASE

This library is relatively new, lacking features, and the API is subject to change, though it has been unit tested and seems to be stable enough for my ASCII Shader

Docs | API | Github

Brief Overview

Weave is a job-oriented parallelism library for Roblox built on Actors and callback-based dispatch.

It is the successor to Parallelizer, which used a buffer-based execution model. After some profiling and experimentation, that approach proved to introduce unnecessary overhead and often reduced performance rather than improving it.

Weave uses a job-based model that batches synchronous work across worker Actors and rejoins results through callbacks, which has more predictable performance and lower scheduling overhead.

Notable Optimization Tricks

  • Uses BindableEvents instead of SharedTables for parallel-serial communication
  • Because parallel to serial communication is expensive; even with bindable events (the best SharedTable alternative I could find), instead of firing after each batch, each actor only fires the remote ONCE for any number of threads to reduce traffic
  • This library is callback-oriented instead of promise-oriented due to the bulky nature of promise libraries. It’s simpler and way faster
  • It’s recommended to set the workspace’s SignalBehavior to Immediate for the maximum performance (because we’re using remote events which behavior depends on the signal behavior)

What is this even used for?

  • Terrain Generation
  • Intensive Raycasting
  • Custom Renderers
  • Bullet Projectile Physics
  • … and other computationally heavy systems

Usage

-- master_script.server.luau
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local weave = require(ReplicatedStorage.weave)

local dispatcher = weave.dispatcher.new(8, script.Parent.worker)

local origin = Vector3.zero
local dir = Vector3.yAxis -- up
local threadCount = 200*200

dispatcher:DispatchDeferred("ray", threadCount, function(buf)
	print(buf)
end, nil, origin, dir)
--!native
--!optimize 2
-- worker.server.luau
local actor = script:GetActor()

if not actor then return end

local weave = require(game.ReplicatedStorage.weave)
local kernel = weave.kernel.new(actor)

kernel:On("ray", function(id, origin: Vector3, dir: Vector3)
	local res = workspace:Raycast(origin, normal_dir.Unit * -1000)
	return res
end)

kernel:Ready()

It’s recommended to put weave somewhere easily accessible and independent, like in ReplicatedStorage

Installation

> Pesde

pesde add artzified/weave

> Github Release (rbxmx)

Examples

Terrain Generation:
terrain.rbxl (80.2 KB)

NOTE: I WILL NOT ENGAGE IN A CYCLIC CONVERSATION WITH YARIK.

11 Likes

Release - 0.10.0-beta.1

Added (PUBLIC API)

  • Kernel:OnDetached
  • Dispatcher:DispatchDetached
  • Dispatcher:DispatchDetachedDeferred

Minor Changes

  • Made sure every kernel is processed in parallel (explicit task.desynchronize)
  • Profiling is disabled by default

(suggested by @athar_adv)

What is a detached dispatch?

It essentially is a fire and forget. The kernel (workers) never reports back to the master. No result buffer is allocated, no registry entry is created, and no callback is ever fired. The kernels (workers) run, the work is done, and the main thread forgets about it.

This is especially useful for side-effects parallel use cases such as terrain generation, where you don’t need to send data to the master, you just do the work by yourself. Eliminating any need for reconciliation and table allocations.

1 Like

I also made a terrain generation example, but I can’t publish an experience right now unless I’m verified. So here’s a rbxl instead
terrain.rbxl (80.2 KB)

1 Like

There’s documentation now

2 Likes

A successor of Paralleizer? Yipee! Will definitely be checking this out because I really like Parallelizer! Paralellizer… however you spell it. :3

Could you benchmark the new and old modules to visualize the improvements? Thanks!

Do you mean BindableEvents? Remotes don’t really make sense in parallelism. Actors sort of have BindableEvent behavior, why not just use the actor?

2 Likes

Oh yeah my bad

If you’re talking about SendMessage that only works for serial to parallel and not the other way around

1 Like

I’ll get to that later, the main thing is this doesn’t do buffer serialization as it didn’t perform as good. Small benchmarks may not reflect the actual performance so I might benchmark the terrain generation.

Though parallelizer might have the upperhand if you actually need buffers as the result

I see! I currently do need buffers since I used Parallelizer to render images. If you say not using buffers is more performant, maybe I’ll give Weave a try. When I render images, they’re usually interlaced so I still need to split the buffer pixels quite a bit, which takes around a millisecond.