The difference is negligible and should not matter (with multiple connections performing only slightly better as I expected), and connections should be very cheap on memory in the first place. Do note that running these tests by themselves without getting the average speed will kind of result in the first test (whether it is the single or multiple) almost always technically being slightly faster due to the creation of threads.
--!strict
local function single(accumulate: (seconds: number) -> ()): ()
local mainThread: thread = coroutine.running()
local singleBindableEvent: BindableEvent = Instance.new("BindableEvent")
local callbacks: {(...any) -> ...any} = {}
for i: number = 1000, 1, -1 do
table.insert(callbacks, function(timeFired: number): ()
local timeSince: number = os.clock() - timeFired
accumulate(timeSince)
print(`Callback #{i} fired. Took {string.format(`%.6f`, timeSince)} seconds.`)
if i == 1 then
coroutine.resume(mainThread)
end
end)
end
singleBindableEvent.Event:Connect(function(timeFired: number): ()
for _: number, callback: (...any) -> ...any in callbacks do
task.spawn(callback, timeFired)
end
end)
singleBindableEvent:Fire(os.clock())
coroutine.yield()
end
local function multiple(accumulate: (seconds: number) -> ()): ()
local mainThread: thread = coroutine.running()
local multipleBindableEvent: BindableEvent = Instance.new("BindableEvent")
for i: number = 1, 1000 do
local index: number = i
multipleBindableEvent.Event:Connect(function(timeFired: number): ()
local timeSince: number = os.clock() - timeFired
accumulate(timeSince)
print(`Connection #{i} fired. Took {string.format(`%.6f`, timeSince)} seconds.`)
if i == 1 then
coroutine.resume(mainThread)
end
end)
end
multipleBindableEvent:Fire(os.clock())
coroutine.yield()
end
local function compare<A..., R...>(func1: (accumulate: (seconds: number) -> (), A...) -> R..., func2: (accumulate: (seconds: number) -> (), A...) -> R..., ...: A...): ()
local func1AccumulatedTime: number = 0
local func2AccumulatedTime: number = 0
local function accumulate1(seconds: number): ()
func1AccumulatedTime += seconds
end
local function accumulate2(seconds: number): ()
func2AccumulatedTime += seconds
end
func1(accumulate1, ...)
func2(accumulate2, ...)
print(`Function #1 ran {string.format(`%.6f`, func1AccumulatedTime / func2AccumulatedTime)}x the speed of function #2`)
end
compare(single, multiple)
Running 1,000 callbacks with the array based (single) function ran on average ~0.96x-1.001x the speed of connection based (multiple) function.
Running 1,000 callbacks with the connection based (multiple) function ran on average ~0.99x-1.06x the speed of array based (single) function.
In conclusion: whichever one works better for you. The difference is so small that even with the extreme 1,000 simultaneous threads (a total of 2,000 threads for both tests), it doesn’t make that much of a speed difference.
I’m not too familiar on the specifics on how this works on the C++ side, but I assume Connect and Fire is the same thing as doing a manual callback table, except slightly faster due to being low-level.