Thanks for pinging me! I’ll now provide constructive criticism on your event.
First off, I started off by testing the code you have on your GitHub. I was unable to get it to work the first time due to an oversight on line 77
table.insert(self, #self + 1, connection)
where it should be (at least I’m assuming);
table.insert(self._connections, #self._connections + 1, connection)
After adding this fix I got your module to work for me. It looks like it’s repro’d in your Roblox model as well. Additionally, I attempted to use the Destroy
method and was met with a nil value error on line 28:
connection:Disconnect()
Line 29 makes the index’s value nil and I think that should be enough to satisfy what you wanna achieve out of a dispose method. Also the self = nil
line doesn’t do anything because self is just a local reference to the table itself within the method’s scope, but the diligence is appreciated
At this point I’ve gotten your module to work, lets see what my benchmarks are here (benchmark script at the bottom). I noticed you guys in this thread are running benchmarks on how fast ::Fire
takes to execute; I’ll be benchmarking the time inbetween Fire
and the callback’s entry point.
21:10:28.628 bindable event - 9.22 microseconds - Edit
21:10:45.311 signal module - 8.82 microseconds - Edit
21:11:01.993 bindable event - 10.79 microseconds - Edit
21:11:18.675 signal module - 9.32 microseconds - Edit
21:11:35.358 bindable event - 9.25 microseconds - Edit
21:11:52.040 signal module - 13.11 microseconds - Edit
21:12:08.723 bindable event - 9.32 microseconds - Edit
21:12:25.406 signal module - 8.60 microseconds - Edit
21:12:42.089 bindable event - 9.26 microseconds - Edit
21:12:58.771 signal module - 8.21 microseconds - Edit
It seems like at best I save a little more than a microsecond, and at worst it costs a little under 3 more microseconds. I wanted to see if I could make this any faster, so I started poking at the Fire
method and changed a few things
- I replaced the
pairs
iterator with a numerical loop. This is known to be faster than both pairs
and ipairs
, and even next
- I removed the
if
statement, since (correct me if I’m wrong) it seems like the statement will always be true
function Signal:Fire(...)
for index = 1, #self._connections do
local thread = coroutine.create(self._connections[index].Function)
coroutine.resume(thread, ...)
end
end
So here we have the new fire method, lets check the benchmarks
21:25:32.109 bindable event - 9.02 microseconds - Edit
21:25:48.789 signal module - 7.60 microseconds - Edit
21:26:05.490 bindable event - 9.41 microseconds - Edit
21:26:22.173 signal module - 8.00 microseconds - Edit
21:26:38.857 bindable event - 9.34 microseconds - Edit
21:26:55.537 signal module - 8.19 microseconds - Edit
21:27:12.221 bindable event - 9.69 microseconds - Edit
21:27:28.904 signal module - 8.04 microseconds - Edit
21:27:45.587 bindable event - 9.90 microseconds - Edit
21:28:02.269 signal module - 8.01 microseconds - Edit
Cool, it seems we’re 5 for 5 here and shaving off more than a microsecond each time. I wanted to see if I could improve it further so I poked at the ::Connect
method as well, and instead of creating a table for each connection and storing it in _connections
I just have it insert the function itself directly into self
, a-la:
table.insert(self, #self + 1, givenFunction)
This required me to change the for loop & coroutine creation line ins ::Fire
to
for index = 1, #self do
local thread = coroutine.create(self[index])
This cuts down on the amount of indexing we do. Lets see where that gets us
21:42:41.350 bindable event - 9.52 microseconds - Edit
21:42:58.033 signal module - 7.88 microseconds - Edit
21:43:14.716 bindable event - 9.59 microseconds - Edit
21:43:31.400 signal module - 7.86 microseconds - Edit
21:43:48.081 bindable event - 9.22 microseconds - Edit
21:44:04.763 signal module - 7.60 microseconds - Edit
21:44:21.446 bindable event - 8.95 microseconds - Edit
21:44:38.128 signal module - 7.92 microseconds - Edit
21:44:54.812 bindable event - 9.55 microseconds - Edit
21:45:11.494 signal module - 8.10 microseconds - Edit
yay! we’re hitting sub-8 microseconds more often than not now.
My only other criticism is to use runService.Heartbeat
instead of .Stepped
in your ::Wait
method, as Heartbeat will have fired 3 times by the time Stepped fires once. And sorry if I come off as pedantic here, I really do want to help so I tried explaining as much as I could here. I think this is a neat idea and I hope you keep up the good work!
Benchmark.lua
local Signal = require(workspace.Signal);
local tests = 1000;
local runserv = game:GetService('RunService');
local beat = runserv.Heartbeat;
local function TestA()
local total = 0;
local completed = 0;
local signal = Instance.new('BindableEvent', workspace);
signal.Event:Connect(function(t)
total += (os.clock() - t);
completed += 1;
end);
for i = 1, tests do
signal:Fire(os.clock());
beat:Wait();
end;
repeat
beat:Wait();
until completed == tests;
signal:Destroy();
return total / tests;
end;
local function TestB()
local total = 0;
local completed = 0;
local signal = Signal.new();
signal:Connect(function(t)
total += (os.clock() - t);
completed += 1;
end);
for i = 1, tests do
signal:Fire(os.clock());
beat:Wait();
end;
repeat
beat:Wait();
until completed == tests;
signal:Destroy();
return total / tests;
end;
local i;
for i = 1, 5 do
print( string.format('bindable event - %.2f microseconds', TestA() * 1000000) );
print( string.format('signal module - %.2f microseconds', TestB() * 1000000) );
end;