define('DISALLOW_FILE_EDIT', true);{"id":4116,"date":"2011-12-05T20:18:15","date_gmt":"2011-12-06T01:18:15","guid":{"rendered":"http:\/\/www.unfocus.com\/?p=4116"},"modified":"2013-04-03T13:32:57","modified_gmt":"2013-04-03T17:32:57","slug":"fast-as3-signals-with-signalslite","status":"publish","type":"post","link":"http:\/\/www.unfocus.com\/2011\/12\/05\/fast-as3-signals-with-signalslite\/","title":{"rendered":"Fast AS3 Signals with SignalsLite"},"content":{"rendered":"

I was playing around and ended up writing a lite Signals class (ok, 3 classes). The set works like a basic AS3 Signal, minus most of the extra functionality of AS3 Signals (run-time dispatching argument type checking as one example). The goal was to create a very fast Signal dispatcher, with very little overhead, and to dispatch with absolutely no heap allocation (check, check and check) – targeted mostly for mobile (AIR). Regular AS3 Signals does well, but it seemed to have a lot of extra stuff that I don’t need – and this was a fun kind of exercise anyway.<\/p>\n

Some quick numbers from the performance-test<\/a> with 1,000,000 iterations on a Core 2 Duo 2.6GHz (in milliseconds):<\/p>\n

Func call time: 15
\nRunnable call time: 5
\nEvent (1 listener) time: 863
\nSignal (1 listener) time: 260
\nSignalLite (1 listener) time: 232<\/strong>
\nRunnableSignal (1 listener) time: 56<\/p>\n

Func call (10 listeners) time: 190
\nRunnable call (10 listeners) time: 399
\nEvent (10 listeners) time: 2757
\nSignal (10 listeners) time: 741
\nSignalLite (10 listeners) time: 725<\/strong>
\nRunnableSignal (10 listeners) time: 221<\/p>\n

The bold line is a vanilla SignalLite, and the line above Robert Penner’s AS3 Signals. They are pretty close, but SignalsLite takes a modest edge. But let’s look at the same test on iOS (iPhone 4S) with 100,000 iterations:<\/p>\n

Func call time: 171
\nRunnable call time: 26
\nEvent (1 listener) time: 3723
\nSignal (1 listener) time: 789
\nSignalLite (1 listener) time: 481<\/strong>
\nRunnableSignal (1 listener) time: 117<\/p>\n

Func call (10 listeners) time: 2004
\nRunnable call (10 listeners) time: 1892
\nEvent (10 listeners) time: 9217
\nSignal (10 listeners) time: 4030
\nSignalLite (10 listeners) time: 2074<\/strong>
\nRunnableSignal (10 listeners) time: 498<\/p>\n

On iPhone you can see that SignalLite is almost twice as fast as AS3 Signals – a more substantial difference than on desktop. I’m not sure why that is, maybe the AOT compiler can optimize something about SignalLite better – IDK, but it sure is fast!<\/p>\n

Then there’s that last line in each group – RunnableSignal. Now your talking speed. That one also solves a particular problem with function callback systems that they all seem to have – there is no compile time function signature checking. You have to wait until the thing runs, and then find out you are taking the wrong number of arguments, or the wrong type, etc. But, solving one problem (compile time type checking), solves the other (speed), and that brings us to SignalTyped which RunnableSignal in the test above extends (I’ll probably rename at some point).<\/p>\n

SignalTyped is beginnings of a fast executing type safe implementation of AS3 Signals. The idea is, you extend 2 classes – SignalTyped and SlotLite. SignalTyped is effectively an abstract class – you must extend it and implement the dispatch method, and the constructor (at least for now, I’m looking for better ways to handle this). It takes a bit of boilerplate to implement this in a class that would expose signals. This example is based on the performance test from Jackson Dunstan’s CallbackTest<\/a> which I borrowed (I hope that’s ok!):<\/p>\n

[sourcecode language=”actionscript3″]
\n\/\/ Interface for your class that might have listeners for the SignalTyped.
\n\/\/ Make one of these per listener type.
\ninterface IRunnable {
\n\tfunction run(): void;
\n} <\/p>\n

\/\/ Custom Slot has a specific property for the Runnable class.
\nclass RunnableSlot extends SlotLite
\n{
\n\tpublic function RunnableSlot( runnable:IRunnable ) {
\n\t\tthis.runnable = runnable;
\n\t}
\n\tpublic var runnable:IRunnable = new EmptyRunnable;
\n}<\/p>\n

\/\/ An empty IRunnable class for first node.
\nclass EmptyRunnable implements IRunnable {
\n\tpublic function run():void {};
\n}<\/p>\n

\/\/ You need one of these per dispatch type.
\nclass RunnableSignal extends SignalTyped
\n{
\n\t\/\/ last and first must be set to the typed Slot.
\n\tpublic function RunnableSignal() {
\n\t\tlast = first = new RunnableSlot;
\n\t} <\/p>\n

\t\/\/ implement the dispatch method to call the runnable prop directly
\n\t\/\/ It’s easy to have it take and dispatch any type you want – with compile time type checking!
\n\tpublic function dispatchRunnable():void
\n\t{
\n\t\tvar node:RunnableSlot = first as RunnableSlot;
\n\t\twhile ( node = (node.next as RunnableSlot) ) {
\n\t\t\tnode.runnable.run(); \/\/ FAST!
\n\t\t}
\n\t}
\n}
\n[\/sourcecode]<\/p>\n

That’s all necessary for the implementation requirements – a lot of boilerplate, I admit. Then you expose that in a class that might use it all:<\/p>\n

[sourcecode language=”actionscript3″]
\nclass MyDisplayObject
\n{
\n\t\/\/ could probably make this a getter..
\n\tpublic var signaled:RunnableSignal = new RunnableSignal;
\n}
\n[\/sourcecode]<\/p>\n

Now for the consumer to use this, it’s just a bit more boilerplate than a normal signal:<\/p>\n

[sourcecode language=”actionscript3″]
\nclass MyConsumerOfSignalLite implements IRunnable \/\/ boilerplate point 1
\n{
\n\tpublic function MyConsumerOfSignalLite()
\n\t{
\n\t\tvar dspObj:MyDisplayObject = new MyDisplayObject();
\n\t\t\/\/ add the signal (boilerplate point 2 – normal)
\n\t\tdspObj.signaled.addSlot( this );
\n\t} <\/p>\n

\t\/\/ boilerplate 3 – normal, but more strict – naming is specific – FAST!
\n\tpublic function run(): void {
\n\t\t\/\/ do whatever when signals
\n\t}
\n}
\n\/\/ boilerplates 2 and 3 are normal for any signal, except the strictness of #3
\n[\/sourcecode]<\/p>\n

What’s cool about this is you get compile time type checking for your method signature, and the performance improvement that comes with skipping those checks at runtime.<\/p>\n

I’m also thinking about a slightly different signal API that would be more like the Robot Legs’ contract system – think signals by contract – I’m working on it. Since we would be implementing a defined interface per signal type, we could boil the add methods and signal nodes down to one method to add all the listeners of a single object – one add method per dispatching class, instead of one per signal on the dispatching class. This could lead to a reduction in boilerplate. We’d filter by interface type instead of using multiple signal.add nodes and methods. So – improved runtime performance, reduction in (usage) boilerplate (if not implementation) and compile time type checking. I love it!<\/p>\n

Note<\/strong> – I tested none of the example in this post, and the code in github is all very early stage stuff. The performance-test class works though – give it a try!<\/p>\n

Oh, here’s the github repo:
\n
https:\/\/github.com\/CaptainN\/SignalsLite<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"

I was playing around and ended up writing a lite Signals class (ok, 3 classes). The set works like a basic AS3 Signal, minus most of the extra functionality of AS3 Signals (run-time dispatching argument type checking as one example). The goal was to create a very fast Signal dispatcher, with very little overhead, and … Continue reading “Fast AS3 Signals with SignalsLite”<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[211,174],"tags":[48,65,49,70,175],"_links":{"self":[{"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/posts\/4116"}],"collection":[{"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/comments?post=4116"}],"version-history":[{"count":24,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/posts\/4116\/revisions"}],"predecessor-version":[{"id":4138,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/posts\/4116\/revisions\/4138"}],"wp:attachment":[{"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/media?parent=4116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/categories?post=4116"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.unfocus.com\/wp-json\/wp\/v2\/tags?post=4116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}