What are you using for signals? How listener threads are resumed depends on how the signal is fired. For example, BindableEvent:Fire()
is instant because Fire creates and resumes each listener thread itself. It’s the same for most signals that fire from property changes.
Signals like Stepped, RenderStepped, and Heartbeat are fired at particular moments in a frame. The microprofiler can be used to tell exactly when. Here’s another graph:
One: connections added later are resumed first, which is by design. Two: none of them have budgeting, also by design. Three: RenderStepped runs later in the frame than the other two, by design.
Interestingly, WaitForChild is budgeted. Here it is compared to wait() & friends:
WFC appearing lower on the graph indicates that it does not suffer from a minimum wait time as the others do. Taken as an average, the slopes are generally the same, with the minor deviations caused by sub-optimal or slightly different implementations:
Benchmark (LocalScript)
game.ReplicatedFirst:RemoveDefaultLoadingScreen()
local RunService = game:GetService("RunService")
local Step = RunService .RenderStepped
-- Used to avoid interference with budget.
local function Sleep(d)
local t = tick()
while tick()-t <= d do
Step:Wait()
end
end
Sleep(3)
local N = 10
local I = 1000
local a = table.create(I, 0)
local function mark(i, t)
a[i] = a[i] + (tick()-t)
for n = 1, 100 do -- Do some work.
local v = math.sqrt(i)
end
end
local RenderStepped = RunService.RenderStepped
local function doRenderStepped(i, t)
coroutine.resume(coroutine.create(function()
RenderStepped:Wait()
mark(i, t)
end))
end
local Stepped = RunService.Stepped
local function doStepped(i, t)
coroutine.resume(coroutine.create(function()
Stepped:Wait()
mark(i, t)
end))
end
local Heartbeat = RunService.Heartbeat
local function doHeartbeat(i, t)
coroutine.resume(coroutine.create(function()
Heartbeat:Wait()
mark(i, t)
end))
end
local function doSpawn(i, t)
spawn(function()
mark(i, t)
end)
end
local function doDelay(i, t)
delay(0, function()
mark(i, t)
end)
end
local function doWait(i, t)
coroutine.resume(coroutine.create(function()
wait()
mark(i, t)
end))
end
local wfca
local wfcb
local function doWFC(i, t)
coroutine.resume(coroutine.create(function()
wfca[i]:WaitForChild("Value")
mark(i, t)
end))
wfcb[i].Parent = wfca[i]
end
for n = 1, N do
wfca = table.create(I)
wfcb = table.create(I)
for i = 1, I do
wfca[i] = Instance.new("BoolValue")
wfcb[i] = Instance.new("BoolValue")
end
local t = tick()
for i = 1, I do
doWFC(i, t)
end
Sleep(1)
end
for i = 1, I do
print(i, a[i]/N)
end