#Signal+|Insanely optimized script signal

1 messages · Page 1 of 1 (latest)

stable heron
#

An exceptionally fast, lightweight, and elegant open-source signal
library for Luau — with generic types and detailed documentation.

DevForum post

cloud jasper
#

can you benchmark it against goodsignal, fastsignal and other popular signal modules

junior umbra
#

Where :Once() :(

swift nacelle
#

cool module but how does it stand compared to other signal modules

stable heron
tender hemlock
#

What would this be usefull for?

stable heron
# tender hemlock What would this be usefull for?

It's just BindableEvents but way faster. Generally, it's good to have the best performance, and if you're creating and firing signals every frame it's a huge performance boost to use this module.

#

It's super simple too. Just call a function instead of having to create a new instance and access the Event property.

barren pumice
#

a

stable heron
swift nacelle
#

oh okay

stable heron
#

Signal+|Super fast & featureful script signal

junior umbra
#

u should have thread pool instead of single thread

junior umbra
#

have more threads

stable heron
#

That wouldn't be good

junior umbra
#

How so

stable heron
#

Would use a lot more memory (depending on how many you want to save at once) and isn't really necessary for signals, as you usually don't have that many connections. Working with a table rather than a single variable would also be slightly more CPU expensive — which matters when we're trying to get the absolute best performance.

junior umbra
#

But what if better performance is having a collection of threads

stable heron
junior umbra
#

Is 10 too many?

stable heron
#

I mean I prefer just one

junior umbra
#

Idk my signal does better with pool yea

stable heron
junior umbra
#

None

#

Just whatever is added

stable heron
#

So you're keeping up to an infinite amount?

#

I would recommend putting a limit on that.

junior umbra
#

erm

#

there's only ever like one

stable heron
junior umbra
#

Nope

#

If there is threads in the table then they get used

#

And if not then add to table so

#

Would be flow of threads

#

Suphi uses same thing in his

stable heron
#

Makes sense

stable heron
#

I've updated the module with numerous improvements

#

It's definitely one of the fastest open-source signal modules.

#

I might do a benchmark

marble plume
junior umbra
#

Just checked the recent update and there is issue

stable heron
#

:O

#

What is it?

junior umbra
#

Because I actually was talking to Suphi about it the other day

#

u shouldn't use coroutine.resume

stable heron
#

Ahh oopsie

junior umbra
#

It works fine but there are edge cases

stable heron
#

🤨

#

Name one

junior umbra
#

Cuz I legit was messing with that the other day

#

Cuz I thought they were the same but ig not

stable heron
#

My beloved coroutine

#

But coroutine.resume is faster

junior umbra
#

Yeah like a few edge cases that make everyone use task instead

junior umbra
stable heron
#

Who uses a signal inside of game:BindToClose()?

junior umbra
#

Suphi

#

And also warn() just doesn't work at all with coroutines

#

oops

#

error() i means

stable heron
#

Ask him for it

junior umbra
#

He said it right around where I forwarded

#

Could use resume in your own version but idk if you want to release one to the public where someone will end up running into one of those edge cases and be very confused

stable heron
swift nacelle
#

🗣️ 🔥

stable heron
junior umbra
#

Yield and defer are not the same

stable heron
#

Oh sorry

#

There's no equivelant in the task library, right?

junior umbra
#

Don’t think so

#

Yield is unique awesome thing

stable heron
#

Yea task library only provides those that the coroutine can improve on.

#

So I guess yield in coroutine is all good.

stable heron
#

Btw guys it's way faster than GoodSignal!

stable heron
#

Signal+|Ridiculously fast, memory efficient script signal

#

Signal+|Insanely fast, memory efficient script signal

stable heron
#

Btw guys there's amazing custom types (v2.4.0+).

#

Signal+|Insanely optimized script signal

heavy sable
#

Alex

#

I swear if your update makes my idea work I'm going to do something sus

stable heron
#

And which update?

heavy sable
#

using typeof, hopefully casting makes this work

stable heron
heavy sable
#

Though you probably could've done that with the type notation as well, using typeof() might make casting this actually work, I'll let you know whether it works or not

stable heron
#

Unless you use my top secret method with typeof.

heavy sable
#
    type Event<E...> = {
        -- ? I think it works just fine
        Emit : ( --[[ Even in here!]] E...) -> ()
    }
#

Works with no type errors with --!strict

stable heron
heavy sable
#

😢

#

There are no type errors in --!nonstrict either

stable heron
#

With --!strict SignalPlus' side-bar looks like this:

heavy sable
#

Yep

stable heron
heavy sable
#

I just know that block comments inside () works

stable heron
#

Guys some guy send me his signal module (SimpleSignal) which is faster than GoodSignal.
Now watch me benchmark it against Signal+ 🔥

Create 100 signals

Signal+ measured at 0.0028682000023763973 milliseconds
SimpleSignal measured at 0.006316119998336944 milliseconds
🏆 Signal+ was 0.0034479199959605465 milliseconds (~2.2x) faster than SimpleSignal

Connect 100 functions:

Signal+ measured at 0.011354389998450643 milliseconds
SimpleSignal measured at 0.039814160002242714 milliseconds
🏆 Signal+ was 0.028459770003792073 milliseconds (~3.51x) faster than SimpleSignal

Fire with 100 functions:

Signal+ measured at 0.03842830000940012 milliseconds
SimpleSignal measured at 1.4735136999843235 milliseconds
🏆 Signal+ was 1.4350853999749233 milliseconds (~38.34x) faster than SimpleSignal

Once 100 functions:

Signal+ measured at 0.03111915999525081 milliseconds
SimpleSignal measured at 0.05852859000151511 milliseconds
🏆 Signal+ was 0.027409430006264304 milliseconds (~1.88x) faster than SimpleSignal

Disconnect 100 functions:

Signal+ measured at 0.007469740002306935 milliseconds
SimpleSignal measured at 0.023178689999440394 milliseconds
🏆 Signal+ was 0.01570894999713346 milliseconds (~3.1x) faster than SimpleSignal

Disconnect all with 100 functions:

Signal+ measured at 0.003145279998352635 milliseconds
SimpleSignal measured at 0.013685699996858602 milliseconds
🏆 Signal+ was 0.010540419998505968 milliseconds (~4.35x) faster than SimpleSignal

#

Nothing beats Signal+ 🗿

#

FastSignal's attempt:

Create 100 signals:

Signal+ measured at 0.002846440000575967 milliseconds
FastSignal measured at 0.005018470000504749 milliseconds
🏆 Signal+ was 0.002172029999928782 milliseconds (~1.76x) faster than FastSignal

Connect 100 functions:

Signal+ measured at 0.012557659999765746 milliseconds
FastSignal measured at 0.03306501000042772 milliseconds
🏆 Signal+ was 0.02050735000066197 milliseconds (~2.63x) faster than FastSignal

Fire with 100 functions:

Signal+ measured at 0.041073800004596706 milliseconds
FastSignal measured at 0.0390601999934006 milliseconds
🏆 FastSignal was 0.002013600011196104 milliseconds (~1.05x) faster than Signal+

Once 100 functions:

Signal+ measured at 0.024617289999696368 milliseconds
FastSignal measured at 0.03905997000711068 milliseconds
🏆 Signal+ was 0.014442680007414312 milliseconds (~1.59x) faster than FastSignal

Disconnect 100 functions:

Signal+ measured at 0.007536460004666878 milliseconds
FastSignal measured at 0.005873989997417084 milliseconds
🏆 FastSignal was 0.0016624700072497937 milliseconds (~1.28x) faster than Signal+

Disconnect all with 100 functions:

Signal+ measured at 0.0032694300036382627 milliseconds
FastSignal measured at 0.0041916699979992695 milliseconds
🏆 Signal+ was 0.0009222399943610068 milliseconds (~1.28x) faster than FastSignal

Final results

Total gained speed:
Signal+: 3.26x
FastSignal: 0.33x

stable heron
#

See benchmark against LemonSignal on DevForum too

swift nacelle
#

imagine being faster than fastsignal

#

fastsignal more like slowsignal 🗣️ 🔥

marble plume
wispy scarab
#

heya @stable heron a quick question.. if I'm communicating between my modules using bindable events should I use custom signal such like one made by you?.. my game contains single script architecture so all of my scripts are basically modules

white blade
#

Since it's ssa you could use signals and the functions from the other modules

#

No reason for bindableevents

swift nacelle
#

custom signals are just plain better than bindables

#

there is no reason why you shouldn't use them instead of bindables

white blade
#

+1

wispy scarab
#

i thought that they are better a bit as they are the official roblox thing you know?

#

also this signal module has a really really really good feature which setting the type of the signal

#

which can then give me a nice auto complete for types in the :Connect function

white blade
#

Single Script Architecture

swift nacelle
wispy scarab
#

but it's not the case

swift nacelle
#

🤷‍♂️

sterile lava
cobalt lynx
stable heron
stable heron
#

Signal+ is significantly faster than GoodSignal.
See this benchmark against SimpleSignal (which is faster than GoodSignal).

stable heron
junior umbra
#

That comparison was when you were using coroutine.resume instead of task.spawn

sterile lava
#

really Idk because the wait method seems good 🤔

stable heron
junior umbra
#

Didn't know there were so many

#

Of course FastSignal is only so fast because it doesn't spawn new threads

#

Can remove outliers of SimpleSingnal, RobloxSignal and FastSignal

#

This is because GoodSignal only uses a single thread to reuse

#

So if I add a yield then fails

#

With yield can see

#

I got 100 Connections

#

All good 🔥

#

Even just having 2 yielding functions makes GoodSignal the slowest wow

#

But 1 and its equal and 0 its faster

stable heron
#

Are you benchmarking fire?

#

Yea that's where Signal+ has the least of an advantage, but it e.g. is ~2.63x faster than FastSignal at connections in my benchmarks.

junior umbra
#

Yeah just fire

stable heron
#

But Signal+, MySignal (your signal??) and LemonSignal are pretty much equal in fire.

junior umbra
#

Yeah MySignal is my own

#

Yep

#

If I do Connect I get this

#

GoodSignal's disconnect is terrible

#

Since it searches through all the links

#

Idk I'll see what FastSignal gets for connect

stable heron
# junior umbra

That's interesting, can you send me the benchmark place file?

junior umbra
#

Sure I can

#

MySignal is not something I've released so 😱

#

Its possible that there are new versions of RobloxSignal | SimpleSignal & FastSignal

#

Because I grabbed thenm from Stravant's GoodSignal Dev Forum post

half drift
junior umbra
half drift
#

Ah

stable heron
#

@junior umbra Signal+ is better at handling many connections due to its multi-thread-recycling. Try benchmarking with 1000 connections/fires (so just do a for loop that goes up to 1000 for each test), then Signal+ will do better.

worthy merlin
#

@stable heron i have 2 bindable events in my game so should i make the switch to your Signal+? Will i notice a diffrence

half drift
worthy merlin
swift nacelle
worthy merlin
#

but Signal+ is faster than goodsignal?

half drift
#

pretty sure yea

#

also goodsignal is like the slowest one of all

worthy merlin
#

fr?

half drift
#

pretty sure yea

#

I haven't really looked into signals that well yet. But LemonSignal and FastSignal are like almost the same

worthy merlin
#

some guy on yt was glazing the guy who created goodsignal so i thought it was the best or something

half drift
#

and then GoodSignal is above all of em

half drift
worthy merlin
#

aight thanks for the help

half drift
#

nws

stable heron
junior umbra
#

Because when mine is made native it does the same thing

#

Becomes slower at normal amount of connections but faster when lots

stable heron
junior umbra
#

No

#

When thread yields it will make a new one

#

And then when thread is done yielding it gets added back to the threads

stable heron
# junior umbra No

I see. In my tests (using your benchmark though) our modules are equal (at least to the eyes, because there are no numbers :( ).

#

👍

#

But it's weird because I see a lot of optimization opportunities in your module. The code ain't coding.

junior umbra
#

As in? I can explain if I know the reasoning

stable heron
junior umbra
#

Optimization Opportunites

stable heron
# junior umbra Optimization Opportunites
  • You're using functions inside of functions.
  • You're assigning and indexing internal values as strings instead of numbers.
  • You're yielding a reusable thread right after creation—and then resuming it after—instead of using it immediately.
  • Your fire logic uses one more variable than Signal+, which can be avoided.
  • You're checking the status of the thread before resuming it in Wait, which I don't see why is needed.
junior umbra
#
  • Less memory and doesn't make much slower
  • Mixed Tables are slower
  • I don't know what this means
  • Less memory and doesn't make much slower
  • Its possible for the thread to not be in the state of "suspended" and will error
#

Those my responses lol

junior umbra
#

Yes

stable heron
#

Amazing

junior umbra
#

While an array is faster than dictionary, since you have metamethod of __index it now becomes mixed table and is slower than just using strings alone

stable heron
#

Okay but if I change my numbers to strings will it be faster?

junior umbra
#

Should help tiny bit

stable heron
stable heron
junior umbra
#

ah okay

stable heron
#

This is how Signal+ fixes it:

local function reusableThread(callback, ...)
    -- Use the thread first.
    callback(...)
    table.insert(threads, coroutine.running())
    -- THEN start yielding.
    while true do
        reusableThreadCall(coroutine.yield())
    end
end
stable heron
junior umbra
#

No

#

I mean if u putting numbers into table with metatable

#

But if their all strings then is just dictionary

#

Idk u can try changing urs to strings and benchmark to numbers

stable heron
#

Uhh it's significantly faster without numbers

#

There's something wrong

#

581x faster firing

junior umbra
#

lol

stable heron
#

Connections are like 1.2x faster

#

I'll see what's up with the 581x faster firing

#

Okay bro thanks so fucking much for that hack

#

Signal+ outperforms everything else now in your benchmark thing.

#

I will be updating it.

#

Ain't no way bro I will never forget about mixed tables being slow.

#

🙏

junior umbra
#

Hope what u changed is correct lol

stable heron
#

Okay maybe I should verify it works real quick

junior umbra
#

cuz they not that much slower but are

junior umbra
#

Yeah ur thread recyling still has issues I think

stable heron
#

What?

#

Like the current version?

#

Or do you mean the 581x faster firing?

junior umbra
#

I am not really sure

stable heron
#

Because yea I figured out there was something wrong.

#

The actual result with firing is ~1.05x faster.

#

Connection is ~1.44x faster.

#

Disconnect is ~1.05x faster.

#

DisconnectAll is ~1.37x faster.

#

Still good results for such a simple optimization.

#

Thanks

junior umbra
#

Im not really sure how the thread issue works

#

Like if its just lag or sum

stable heron
stable heron
#

This?

#

It just avoids having to resume another time.

junior umbra
#

But idk why it does that

stable heron
junior umbra
#

Will see if mine does that idk

stable heron
#

I mean it looks good to me

#

You're firing 10 new threads every frame.

#

Also why is FastSignal so unbelieveably fast at firing? It's missing some crucial feature, right?

junior umbra
stable heron
#

But yea firing wasn't made much faster with the mixed tables fix, so our modules are equal there.

#

Lemme check connection

#

Dude they're all in one spot, is there a way I can zoom in? I'm talking about the Global Benchmarker thing.

junior umbra
#

mmm no

stable heron
#

Bro

junior umbra
#

Hope u have the UiDrag beta enabled tho

stable heron
#

What?

junior umbra
#

His benchmarker uses these

#

So u can move the display

stable heron
#

Yea they're enabled.

junior umbra
#

But yeah zooming in no

stable heron
#

There must be a way we can like zoom in beforehand in the code.

junior umbra
#

I mean when using the array version I get this yea

stable heron
#

Yo how do you zoom in like that?

junior umbra
#

Just drag up

#

and it zooms in kinda

stable heron
#

Doesn't work

junior umbra
#

Grab actual part with the lines

#

And then mouse upwards

stable heron
#

I am

#

I can even see the drag mouse icon\

junior umbra
#

Then ur UiDragDetector might not be enabled?

#

The beta

stable heron
#

But it is

junior umbra
#

U should be able to move the whole gui if u grab the side too

stable heron
#

It doesn't even detect drag

#

I tried printing

junior umbra
#

What OS u using

stable heron
stable heron
junior umbra
#

mm idk im on win 10

stable heron
#

I mean it's a beta so 🤷‍♂️

#

But still

junior umbra
#

guess can use different benchmark yea

stable heron
#

I just called the function to zoom and put in a fake cursor movement. I got it working.

junior umbra
#

oh dang

stable heron
#

Yours is like 1.0 (whatever that means) faster than mine for connections.

#

I need to know how yours is faster

#

It makes no sense

#

Even though it's super minimal

junior umbra
#

Yea

stable heron
#

MySignal (your signal):

local function Fire<A...>(self: Signal<A...>, ...: A...): ()
    local link = self.Next
    while link do
        local length, thread = #threads, nil
        if length ~= 0 then
            thread = threads[length]
            threads[length] = nil
        else
            thread = coroutine.create(Yield)
            coroutine.resume(thread)
        end
        task.spawn(thread, link.Function, thread, ...)
        link = link.Next
    end
end

Signal+ (my signal):

Fire = function(signal, ...)
    -- Fire all callbacks.
    local node = signal.Head
    while node do
        -- Find or create a thread, and run the callback in it.
        local length = #threads
        if length == 0 then
            task.spawn(reusableThread, node.Callback, ...)
        else
            local thread = threads[length]
            threads[length] = nil -- Remove from free threads list.
            task.spawn(thread, node.Callback, ...)
        end
        
        -- Go to the next connection.
        node = node.Next
    end
end,
junior umbra
#

Nice

stable heron
#

Can we just agree that Signal+ fire should run faster?

#

I can not see it.

#

Please enlighten me

junior umbra
#

Would be related* to how threads are being handled I guess

stable heron
#

Hm yea probably

#

MySignal:

local threads: {thread} = {}

local function Call<A...>(func: (A...) -> (), thread: thread, ...: A...): ()
    func(...)
    table.insert(threads, thread)
end

local function Yield(): ()
    while true do Call(coroutine.yield()) end
end

Signal+:

local threads = {}
local function reusableThreadCall(callback, ...)
    callback(...)
    table.insert(threads, coroutine.running())
end
local function reusableThread(callback, ...)
    callback(...)
    table.insert(threads, coroutine.running())
    while true do
        reusableThreadCall(coroutine.yield())
    end
end
#

It might be coroutine.running()

junior umbra
#

LemonSignal uses same thread recyling

#

Cuz that's where I grabbed mine lol

stable heron
#

bruh

#

Okay but bro

#

Explain this

junior umbra
#

🤷‍♂️

stable heron
#

This:

Connect = function(signal, callback)
    -- Create connection.
    local connection = setmetatable({
        Signal = signal,
        Previous = nil,
        Next = signal.Head,
        Callback = callback,
        
        --Connected = true
    }, connectionClass)
    
    -- Add connection.
    signal.Head = connection
    
    -- Return connection.
    return connection
end,

Is slower than this:

local function Connect<A...>(self: Signal<A...>, func: (A...) -> ()): Connection<A...>
    local next = self.Next
    local link = {Previous = self, Function = func, Next = next} :: any
    if next ~= nil then next.Previous = link end
    self.Next = link
    return setmetatable(link, Connection) :: Connection<A...>
end
junior umbra
#

I think its because of Native

#

Native not good on that function

stable heron
#

Yep

#

It's now faster than yours

#

wtf

#

Ever-so-slightly

#

Well if I bring back the .Connected property they are literally equal.

junior umbra
#

Yeah mine doesn't have any cool features

#

Just basic

stable heron
#

lol

#

Well I don't need .Connected either but it's a community resource 🤷‍♂️

junior umbra
#

I did not find it beneficial to use native with mine

stable heron
#

Alright so now for firing yours is 4 upside-down-n-s's faster.

junior umbra
#

The best (for mine) was @native on Fire() and then u also would want @native on Disconnect() because will help Fire() be faster

#

But then Disconnect is bit slower

#

And only faster firing when lots of connections

stable heron
#

Oh damn lol

#

Very much time

#

Actually does your DisconnectAll really unlink the connections?

#

Doesn't it cause a memory leak?

junior umbra
#

There is no reference to the table

#

Therefore GC will clean it

#

Next = {}
so once Next = nil there is no way to get that table back

#

It is floating in memory space 🔥

#

Buh yeah for native check this, 10 Connections

#

1000 Connections

stable heron
#

So not worth it

junior umbra
#

Only gains because the loop is very slightly faster, so with big loop can be faster

#

Right so that's just why I don't have in mine

stable heron
junior umbra
#

If I make whole script native doesn't make difference

#

Will still be same as Disconnect and Fire only

#

Only then it makes Connect slower and stuff

#

Seems all native is worse actually

stable heron
#

lol

#

poor native

junior umbra
#

That's because its not good to make the thread recycling functions native yea

#

O yeah here is reason for the check in the Wait() method https://youtu.be/yevAvHU3ewo

#

Its niche and went unnoticed for years

stable heron
#

Weird bug

junior umbra
#

Yep

#

So I just have check for that

stable heron
#

👍

silent swallow
#

Hi, Alexander, can you check DM and help me with my questions on Signal+ ?

stable heron
#

Yo @junior umbra I just realized you don't allow for garbage collection after :DisconnectAll() in your own signal module.

#

You have to get rid of the next and previous pointers in each connection.

#

I believe a loop is unavoidable.

junior umbra
#

I think you are wrong

#

Buh I will see

#

looks fine to me

#

I will show how I concluded that tho

#

BEFORE CONNECTING

#

AFTER CONNECTING

#

AFTER DisconnectAll()

#

Script:

#

90 is about the max you can do without the Luauheap yelling at you

#

Since linked list kinda complex I guess

stable heron
#

How does it work?

junior umbra
#

I am losing reference to all the connections?

#

It changes the signal from {Next = {Previous = self, Function = func, Next = self.Next}} to {}

stable heron
#

Oh it makes perfect sense, actually.

#

Thanks

wispy scarab
#

How did you go for this insane optimization like being faster than fastSignal is a bit crazy @stable heron

#

like resources you learn from

stable heron
# wispy scarab like resources you learn from

I don't really look at much else. I did a lot of benchmarking to see what was fastest. You can not forget Z3SC though, he pointed out some things too.

Btw I will be releasing an update in a bit that has many more optimizations.

wispy scarab
#

but the question is really more like then why the other modules didn't optimized it

wispy scarab
#

like there isn't any pro that went and made the modules more performant

#

by other means contributed to it

stable heron
#

I don't know really. They likely thought it was already optimized to the maximum because it was pretty much described as that.

wispy scarab
#

Also a thing you mightn't care of is that some people could make an insane optimized versions of a lot of module but they use it for themself and don't just publish it

#

Like blox fruit and some big games definitely has big teams of developers who makes great modules... But they keep it for the production as the scripters cannot publish it to anyone to use

stable heron
wispy scarab
#

Also it's fps is fairly high as the game is pretty smooth with all of the abilities and effects in it

#

Tbh I don't play it but it's fps and performance is good

junior umbra
#

I think memory efficient is not the best way to describe this anymore

stable heron
junior umbra
#

Probably less efficient

stable heron
#

Can you prove it?

junior umbra
#

I just mean doing index like this is weird

#

Cuz now you got 2 tables here

stable heron
#

There's two tables anyways though?

junior umbra
#
local Connection = {}
Connection.__index = Connection```
#

No

stable heron
#

But if you set it to itself it shouldn't have any effect. How does that work?

junior umbra
#

And the fact that code is copied instead of reusing functions would also use more memory

stable heron
#

Sure

junior umbra
#

Like in Once and Wait

stable heron
#

Hm

#

I guess but it's faster

junior umbra
#

Instead of reuisng Connect you copied the link in it yes

stable heron
#

I'll change the description

junior umbra
#

So I mean technically not really "Memory Efficeint" lol

#

More so built entirely for speed than anything

stable heron
stable heron
#

Idk what to put though lol

stable heron
atomic maple
#

@stable heron do you have a ver that has .new

#

since i wanna switch up to this one

#

but i cant unless it returns a table

#

since im using a sharedModules table

atomic maple
stable heron
south pebble
#

i have a question but have to go for a bit… will ask in a few mins

stable heron
#

Ok

south pebble
#

Ok, sorry about that, had a question and then something came up and I got occupied for hours.

So, if I want this to trigger 10 ish times per frame (just assuming only light functions are connected), and then possibly have spikes per frame where it’s something like 50 to 100 connections per frame, would this still handle that well?

stable heron
#

It's built for that

#

But let me know if you do experience performance issues.

candid plank
#

This has probably been asked before but how does this compare against packet?

#

or is this even the same purpose as that

swift nacelle
#

packet is a networking library

#

signal+ is well, a signal

candid plank
#

an event?

#

rbxscriptsignal or wtv its called

swift nacelle
#

bindables but better

#

in short

candid plank
#

ah

#

mb

swift nacelle
#

with this

proven sandal
#

@stable heron hello, I believe im doing this correctly, but i'm unable to recieves signals with SignalPlus

#

I have two local scripts one for firing and the other is the listener

-- Script 1
local CustomSignal = SignalPlus()
local Bindable = ReplicatedStorage.Events.Test

local Input = KeyHandler.Create({
    Name = "One",
    Key = {Enum.KeyCode.E},
    Type = "Single",

    Began = function()
        
        Bindable:Fire()
        
        CustomSignal:Fire()
    end
})

-- Script 2
local SignalPlus = require(ReplicatedStorage.Modules.Networking.SignalPlus)

local CustomSignal = SignalPlus()
local Bindable = ReplicatedStorage.Events.Test

CustomSignal:Connect(function()
    print("Signal")
end)

local Connection = Bindable.Event:Connect(function()
    print("Bindable")
end)

#

the bindable event connection works, but I don't receive anything from the customsignal

junior umbra
#

Those are different Signal objects

proven sandal
#

They are?

junior umbra
#

When you call SignalPlus() it makes a new signal

#

You'll need ModuleScript to connect to the same Signal

proven sandal
#

I see

#

So I'd store the created signals in some type of cache inside said module, and then I could refer to them with an index and return that found signal?

junior umbra
#

Most commonly used when you make your own objects

#

So for example if I had a method on my object like Object:ChangeColor() I might have an event in the object called Object.OnColorChanged

#

And then I can connect to that event

#

And whenever ChangeColor() is called it will be fired

proven sandal
#

Oh I see

junior umbra
#

Yep :D

#

But just have to access that same event from both scripts

#

Same thing you're doing with the Bindable event, just not as straight forward

proven sandal
#

My use case wouldve been as a tag listener for a module to recieve a signal once a tag has been added/changed

#

So during runtime if I were in the middle of some type of attack and a tag was added, id use that signal to cancel the action

junior umbra
#

Not possible with Roblox's signals?

proven sandal
#

after a bit more testing, I came up with this listener

local CS = game:GetService("CollectionService")

local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()

CS:GetInstanceAddedSignal("Test"):Connect(function()
    local Model = CS:GetTagged("Test")
    
    for _, Object in ipairs(Model) do
        if Object == Character then
            print("Found Character")
        end
    end
end)
#

But I wouldnt I need to setup this connection each time a tag is added?

junior umbra
#

I am not sure wym

proven sandal
# junior umbra I am not sure wym

The point of this method is to listen for a tag being added to a specific character during runtime, and connect a function to that listener

#

So let's say i have a loop, and I listen for a tag being added during that loop which cancels it and starts a new function

#

that's what im going for

strange salmon
#

@stable heron is your signal module checking the thread status before task.spawn?

#

and also it would be good if SignalPlus api is same as the others

#

like SignalPlus.new()

#

SignalPlus.wrap

#

at also it would be cool if also has reconnect like lemonsignal

junior umbra
#

Just use LemonSignal then

tall bluff
#

Is thee faster than lemon signal

tall bluff
stable heron
#

If I remember correctly

#

You can always try benchmarking yourself

strange salmon
feral hinge
#

metatables man

#

cool one but i prefer non metatable signals

junior umbra
#

So Signal.Fire(Event) ?

half drift
#

i use signal.fire(name, params)

feral hinge
#

i mean no it behaves the same all that changes is there are no metatables at all

#

pretty much just keeping everything in a simple table managing it within here

half drift
#

how would u do that without metatables tho

junior umbra
#

Yeah but how do you connect and fire without metatable

#

Is what I mean

feral hinge
#

black magic basically

half drift
#

signal for a signal?

feral hinge
#

what so hard in fire and connect

#

like you can just store a func within a table and call it on fire called whats so hard bout it

#

if we go over it like very barebones

junior umbra
#

The SimpleSignal I found is just doing OOP but bad

feral hinge
#

ye there is 2

junior umbra
#

Both of the ones I found are doing the same thing

feral hinge
#

personally i dislike the setmetatable signals although the result is pretty much the same

junior umbra
#

Is it by Devollin or Athar

feral hinge
junior umbra
feral hinge
#

oh github

junior umbra
#

Their both called SimpleSignal and both use no metatable

#

But they both do the same exact thing that is even worse than metatables

#

athar does this as well

feral hinge
#

allthar is bindable events

#

lo

#

im trying to find the other simplesignal on devforum

#
#

but this one is kinda what it is about

#

tho this one is slow

junior umbra
#

Yeah same thing as the others

#

Just putting the functions inside of the table

feral hinge
junior umbra
#

Worse for memory

feral hinge
#

how so

junior umbra
#

Because you have to write the keys "Fire" "Connect" ect.. in the object

#

And the __index index always exist

#

So putting a __index on your table actually doesn't use any extra memory over an empty table

#

But then you only have a pointer to the metatable

feral hinge
#

huh

junior umbra
#
local myObject = {
    __index = MyClass,
}

local myObject = {
    __index = BasePointer, -- This always exist idk
    Connect = func,
    Fire = func,
    -- ect.
}```
#

More memory

#

Because each object has to have all keys

#

Instead of the metatable just having the keys

feral hinge
#

so you mean metatable funcs are stored more like a pointer ye

junior umbra
#

everything is a pointer yes

feral hinge
#

and in nonmetatable version you write to it

junior umbra
#

Just gets the memory address of the table

feral hinge
#

so what if it uses localfuncs instead

#

stored in a module

junior umbra
#

Same thing

feral hinge
#

wont that be a pointer

#

alr

junior umbra
#

So this would be worse on Memory but might be a tiny bit faster

#

aka 1 key access faster

#

Cuz would just do Object:Fire() vs Object.__index:Fire()

#

Under the hood

feral hinge
#

yea fair

#

well if there will be a way to use pointers in future

#

like pointers pointers same as c++

#

will metatables still be better in that case

junior umbra
junior umbra
feral hinge
#

and it in reality point to the given func

#

so you basically store a billion lines long func as a 00xblahblahblah

junior umbra
#

You can see the performance loss is from accessing __index if you do like this

junior umbra
feral hinge
#

i mean yea thats why i did not like metatables

#

but memory

#

ugh

junior umbra
#

But trade off for that small speed is memory yea

feral hinge
#

im just curious if that still gonna be the case if for example we use 100 different signals

junior umbra
#

But yeah a while ago a friend and I were talking about it and he posted this

feral hinge
#

ye pointer

junior umbra
#

Always metatable pointer

feral hinge
#

epic

junior umbra
#

Even if one is not set

#

So setting a metatable

#

Uses no extra memory

feral hinge
#

i thought metatables are dogshit before but nowadays i see why they are useful

junior umbra
#

Just helpful with memory yea

feral hinge
#

if only people told me it has a pointer yo

junior umbra
#

I mean everything is a pointer

feral hinge
#

thought its basically a walmart object management with own bs

feral hinge
junior umbra
#

Yee I found that out from testing

#

Here's what my test was

#

So this is the memory usage from 100_000 blank tables

#

And here is memory usage for metatable

#

And for example

#

This is what you're doing

#

With the signals

#

That extra key adds some memory

#

It's not too much to worry about

#

But its something to note

feral hinge
#

if some signals dont get cleaned up by some miracle error setmetatable is superior in that case

#

pretty cool intel

junior umbra
#

Yeah I think Suphi even has said not to worry about it really

#

When seeing someone do OOP without metatables

#

Just because you do have a bit of memory to work with and it doesn't use an insane amount

#

But if memory becomes an issue then might be something you could change

ancient cosmos
#

how do you even cleanup the signals

#
    local TestConnection = ToolSignals.Equipped:Connect(function()
        warn(`gurt`)
    end)
    
    ClientJanitor:Add(TestConnection,"Disconnect")
#

im using janitor to clean up stuff and it doesnt seem to work with signal connections

#

any reason why

stable heron
#

or Signal:Destroy() to completely remove the signal.

lethal arrow
#

This was my introduction to LinkedLists while attempting to decipher how the module worked, literally 100x speed compared to i,v loops in some cases in the benchmarker I used lmao.
Thx king, this stuff is goated 🤝