Making dispatchEvent More Efficient

In most projects there are tons of events that are dispatched, but that have no listeners subscribed to them. This shouldn’t be an issue, but it turns out that the Flash Player deals with these events inefficiently. Luckily it’s pretty easy to patch this for specific situations.

The code below will run about 5X faster than a standard dispatchEvent call when there is no event listener for the event type.

override public function dispatchEvent(evt:Event):Boolean {
if (hasEventListener(evt.type) || evt.bubbles) {
return super.dispatchEvent(evt);
}
return true;
}

Note that the actual time difference is fairly minimal (80ms vs 450ms for 100,000 iterations in my tests), so I would only do this in classes that you are going to create a lot of instances of and which will generate a lot of events that may not have listeners.

For example, I used this in GTween, because you could have thousands of tweens active at the same time, each of them dispatch CHANGE events every frame while active, and it’s very common to create tweens without a listener for that event.

While I think this will work for all cases, I haven’t tested extensively with less common event scenarios. Bubbling events should work, but you won’t get a performance increase.

This change also reduces efficiency for events that do have listeners, but it is very minimal (<10%, 505ms vs 545ms for 100k iterations in my tests).

Grant Skinner

The "g" in gskinner. Also the "skinner".

@gskinner

17 Comments

  1. I think it’s even better if we avoid Event object creation in those cases that don’t have listeners. However, this is not needed when we reuse the same event instance everytime.

  2. I hope Adobe will implement this, next to the removeAllListeners(), removeAllListenersOfType(eventType), getAllListeners() and some others for some more flexibility.

    Something else i noticed that’s missing, but it’s more like a EMCA specs thing: when you pass the method/closure when subscribing to an event, there’s actually no way the compiler can check if it is a valid eventhandler (one that accepts a single valid typed argument).

    You’re stuck with runtime checking. This issue can be broadened into any closure you store in a Function-typed property or variable and call from that lateron (by using the call operator’s ‘()’ or apply() or call()).

    In these days of typing and Interfaces and all that it feels wrong. I know you can fight it with discipline, but im getting used to the compiler enforcing most of my lazy errors. I would prefer if there was a formular way of describing a required closure signature (like an Interface for Functions). Got any thoughts on that?

  3. Wouldn’t just calling something like this be more efficient in all cases?

    hasEventListener(Event.CHANGE) && dispatchEvent(new Event(Event.CHANGE));

  4. Ignacio & Matt – yes, both of those things would make it more efficient, however you start to reach diminishing returns in performance versus code clarity .

    Instantiating the Event object has a minimal impact on performance: it only speeds things up a further ~20% (65ms versus 80ms), and requires a lot more complexity.

    Using the approach Matt suggested is a lot faster (almost another 5X gain), and definitely might be worth doing in some situations. Note though that while the multiplier is similar, the performance gain is massively diminished (saving 60ms for 100k iterations versus saving 370ms). Again, you have to make a choice of performance versus clarity. My approach is transparent, whereas Matt’s suggestion requires a per case decision and implementation (it will break for bubbled events, for example). Mine also requires per class maintenance versus per call (ex. if EventDispatcher was updated to make these approaches unnecessary).

    Neither is wrong, you just need to evaluate all the advantages before choosing your tools.

  5. Yep, that’s true, it all depends on what you’re trying to do with it and how you want to approach it. I’m thinking that for my approach, I could check for bubbling as well if I thought the event had the potential to be bubbled.

    Oh and just in case any of your readers are confused at the ‘&&’ syntax I used, it’s functionally equivalent to writing it like this:

    if(hasEventListener(Event.CHANGE)) dispatchEvent(new Event(Event.CHANGE));

  6. Also worth noting, this syntax:

    if (hasEventListener(Event.CHANGE)) { dispatchEvent(new Event(Event.CHANGE)); }

    Runs at exactly the same speed as using &&, and is a much clearer syntax.

  7. I’m not sure, really, but in my opinion this override is only suitable for non-display objects, as it doesn’t consider listeners that link into some ancestor in the event queue. For them you’d check willTrigger() – but I may have missunderstood that.

  8. Severin – that’s what the check for bubbles is for. There’s a chance I may be missing something though.

  9. do you think doing this test is efficient before adding event Listener like

    if(!hasEventListener(Event.COMPLETE))addEventListener(Event.COMPLETE, becauseImFrench)

    or is it worthless?

  10. Event.bubbles only checks wether the Event can bubble. But you can also listen to an event in the capture phase (using useCapture) which is also triggered for events that don’t bubble. That’s at least how I understand it. But as I said, that’s only a potential problem for display object (unfortunately – i mean, I’d like to have this for my own tree type classes, but that’s yet another missing feature).

  11. Is willTrigger slower than checking hasEventListener & bubbles? If you don’t mind, can you explain why you didn’t use willTrigger? Thanks.

  12. Great pointer. I was actually wondering about this yesterday given the impact of firing events out in a loop where it could be cases that you don’t have listeners. I figured that the internally the EventDispatcher would just pass on the requst, but it sounds like it doesnt.

  13. Are you guys monkey patching EventDispatcher with this?

  14. Nice tip – you keep finding these things, amazing 🙂

  15. Hail Grant Skinner and Matt Rix!

    I know that this post is old, but I felt the need of posting this piece of info.

    If dispatchEvent will bubble, use willTrigger:

    willTrigger(Event.CHANGE) && dispatchEvent(new Event(Event.CHANGE, true));

    Else, use hasEventListener:

    hasEventListener(Event.CHANGE) && dispatchEvent(new Event(Event.CHANGE));

    Here is a benchmark:

    The cost if there is no need of dispatch is 21% of the original.
    The cost if there is need of dispatch is 116,3% (less effective, but Ok)

    The cost is 76% on average (half events bubbling, half not)

    I can’t see any BIG disadvantage of don’t use it always.
    Also, I have the .AS with the benchmark and a .XLSX with MUCH more info about the benchmark.

    Ask me if you get any interest on it.

    ps: I would like to Matt hear about this, but I don’t think that he will… =|

  16. Hah, I stumbled upon this post by accident. Thanks for the tip, NemoStein.

  17. In my humble opinion, it is always better to check if there are any listeners before we even instantiate the event object. The object instantiation always costs (memory allocation etc.)

    I’ve looked upon Flex framework internals extensively and saw many (say majority) of places where there’s an event instantiation without checking for possible listeners. That is a huge performance loss because there might be literally thousands of events created *in vain* during the display list instantiation, while nobody actually needs them (SHOW, HIDE, ADD…).

    So, I think you should always use hasEventListeners() to find out if there are any listeners subscribed to event dispatcher before instantiating an event.

    When dealing with display list, which supports event bubbling – use willTrigger(). That method which returns true if there are any listeners in the complete bubbling chain (capture+target+bubbling phases). It is actually the cumulative (OR operation) between hasEventListeners() run on each node in the component parent chain. And event bubbling is nothing more but firing the event on each node of that chain.

    Cheers!

Comments are closed.