AlecMcE.com

Coding on the Flash Platform

Replacing ENTER_FRAME

View Comments

Today an interesting discussion took place on Twitter, in which I was involved. It caught my attention for a variety of reasons, and I wanted to document the conversation, the problem and the solution here. It started when it was identified that Flash’s event dispatcher is sub-optimal in performance terms, and revolved around trying to find a solution.

Why replace the ENTER_FRAME?

Every event that is dispatched creates an object. An ENTER_FRAME creates as many objects per second as there are frames. If you have mutliple objects dispatching ENTER_FRAME events, that’s many objects creating event objects many times per second. Computationally, chains of “many” are one of those things you want to avoid. So, can we do better?

Doing Better

Robert Penner proposed creating an as3signal and using a MovieClip with two frames to dispatch that signal on each frame. He posted his idea to this GitHub gist.

One drawback to this approach is that it requires a MovieClip symbol with two empty frames, to which the two event dispatcher methods could be attached. I remembered reading in this excellent article by Bit 101 how using a [Frame] metadata tag would force your application to have two frames. This seemed to be what Penner needed for his functionality to work correctly, so I came up with my own GitHub gist which extends the concept. The code is also added ‘below the fold‘, at the bottom of this article.

[Update, after Jackson's comment, below] The idea behind the approach is that a frame-loop is an internal Flash player structure. It sits underneath the AS3 functionality, and so it doesn’t produce ENTER_FRAME events. By hooking into them we can produce our own iterative ‘event’ (Penner proposes using an as3signal), which doesn’t construct an object each time it is dispatched. It is a lovely idea, and quite distinct from invoking one ENTER_FRAME dispatcher on the root or stage of the application.

[Update, after Joa Ebert tweeted test results] Whether this method is superior to the ‘one-event ticker’ is open to question. Joa Ebert applied the frameloop concept to AudioBox and found no memory nor performance improvements.

Background

It all started today with a statement on Twitter from Robert Penner:

Listening to a single #AS3 enterFrame will create over 1000 event instances per minute. @robpenner

In fact, at a decent frame rate, you’re looking at 1800 event instances per minute. Across the blogosphere and on Twitter, there has been a lot of talk about the cost of object construction in AS3. People are pushing the limitations of the player, and are looking for performance optimisation wherever they can find it. Strategies such as object pooling are becoming mainstream in order to avoid the creation and destruction of objects. In the meanwhile, the internal Flash event model is creating and destroying huge numbers of objects to furnish us with an event model.

At the same time however, there has been a murmur of discomfiture about the state of the event model. (Not that I have much of a voice, but I chimed in to this debate as well!). The most notable result of this murmur was Penner’s decision to create as3signals. It is an on-going project to which I have contributed in a small way, and which I whole-heartedly support.

After Penner’s tweet, the #as3 twitterati (please tweet if there are glaring omissions from this list) became very excited indeed.

My initial thoughts led to the idea that maybe the setInterval/setTimeout functions, which are hangovers from AS2 might offer a solution – perhaps they shortcut the event system? Not only ain’t it so, they’re even worse as this Action Script Viewer deconstruction of the setInterval methods demonstrate.

So what else? Mike Chambers (aka @Mesh) in conversation with @TheFlashBum pointed towards a solution using a ‘ticker’; a single ENTER_FRAME dispatcher, and @bengarney confirmed that he also uses this technique.

It seems that @scottjanousek and @robpenner had the idea of using the player frameloop (which the solution offered here exploits) at pretty much the same time. (@scottjanousek’s tweets are protected, but it was reported by this tweet.)

Whether and how this vein of thought will develop is really interesting. It is fascinating how a tweet from one of the best-known ActionScript developers can provoke such a lot of attention from the general community. It is interesting too to see the envelope of what is possible being pushed.

FrameLoop Example Source Code

To use this code, you will need the as3signals library. You will also need to compile through the MXML compiler. I have tested it with the Flex 4 SDK – if you try and discover that it does not work for other target platforms, please share the information by leaving a comment!.

Main.as

package
{
	import org.osflash.signals.Signal;

	import flash.display.Shape;
	import flash.display.Sprite;

	/**
	 * A Main application class that uses the [Frame] MetaData Tag to use the
	 * EventFrameDispatcher class to configure itself. This class contains a
	 * frame signal which is constructed and passed into it by the
	 * EventFrameDispatcher class before init() is called. Application
	 * functionality should be placed in the init() method
	 *
	 * @author Alec McEachran
	 */
	[Frame(factoryClass="EnterFrameDispatcher")]
	public class Main extends Sprite
	{
		/** an as3signal that is dispatched on every ENTER_FRAME */
		public var frame:Signal;

		/**
		 * do interesting things in here...
		 */
		public function init():void
		{
			// functionality here...
		}
	}
}

EnterFrameDispatcher.as

package
{
	import org.osflash.signals.Signal;

	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.utils.getDefinitionByName;

	/**
	 * The EnterFrameDispatcher is injected into the Main class through
	 * the [Frame] MetaData tag.
	 *
	 * The class creates a frame signal. By being injected, two frames are
	 * generated, onto which the frameMethod are attached so that the
	 * frame signal is dispatched on every frame cycle. It then injects this
	 * object into the Main class and calls the Main class's init() method.
	 *
	 * @author Alec McEachran
	 *
	 * based on an idea by Robert Penner @see http://gist.github.com/243630
	 */
	public class EnterFrameDispatcher extends MovieClip
	{
		public var frame:Signal;

		public function EnterFrameDispatcher()
		{
			frame = new Signal();
			addFrameScript(0, frameMethod, 1, frameMethod);

			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			nextFrame();
		}

		private function onEnterFrame(event:Event):void
		{
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			init();
			play();
		}

		private function frameMethod():void
		{
			frame.dispatch();
		}

		private function init():void
		{
			var mainClass:Class = Class(getDefinitionByName("Main"));
			if (mainClass)
			{
				var app:* = new mainClass();
				app.frame = frame;
				addChild(app);
				app.init();
			}
		}
	}
}

Written by alec

November 26th, 2009 at 11:37 pm

View Comments to 'Replacing ENTER_FRAME'

Subscribe to comments with RSS or TrackBack to 'Replacing ENTER_FRAME'.

  1. I too have (briefly) struggled with this issue. It seems to me that you have essentially two options: ENTER_FRAME and Timers. Both of these call your callback via the AS3 events system and therefore result in an Event allocation. What you've done above is to still use ENTER_FRAME, but then not use the AS3 event system to notify everyone of the new frame (as3signals uses its own lists of Functions) and therefore limit the Event object creation to just one. I use a similar approach in my own proprietary framework (owned by the company, sorry), but the core issue remains: thousands of Event objects per minute (60*stage.frameRate) are created and freed. This seems to be an intractable problem in AS3 Flash programming due to the fact that only ENTER_FRAME and Timer events can be used for rapid updates. I'd be super excited if this discussion led to a way around that limitation (the original problem tweeted by Robert Penner)…

    jacksondunstan

    27 Nov 09 at 1:54 am

  2. Great recap and lots of great info. Thanks for pulling it all together in one place.

    Derrick Grigg

    27 Nov 09 at 3:19 am

  3. I don't think so Jackson. The frame loop is internal-to-player functionality; the player has no reason to construct an Event object when passing from frame to frame. That's why this solution is different and interesting! I have to admit, I haven't tested this assertion, mostly because it was late in the UK and I needed to go to sleep! It would seem that Penner has though: http://bit.ly/8IYtuv. I will try to today if
    I get the chance.

    I need to make this clearer in the post, I think, to avoid this confusion. Thanks for the feedback!

    alecmce

    27 Nov 09 at 8:14 am

  4. I don't think so Jackson. The frame loop is internal-to-player functionality; the player has no reason to construct an Event object when passing from frame to frame. That's why this solution is different and interesting! I have to admit, I haven't tested this assertion, mostly because it was late in the UK and I needed to go to sleep! It would seem that Penner has though: http://bit.ly/8IYtuv. I will try to today if I get the chance.

    I need to make this clearer in the post, I think, to avoid this confusion. Thanks for the feedback!

    alecmce

    27 Nov 09 at 10:55 am

  5. @joa reports that he implemented the frameloop in his AudioBox tool, but found no memory nor performance improvements: http://bit.ly/7FGv2c, http://bit.ly/8KW0XM.

    alecmce

    27 Nov 09 at 10:58 am

  6. But his original tweet that you quoted says “Listening to a single #AS3 enterFrame will create over 1000 event instances per minute.”. Then your whole “Why replace the ENTER_FRAME?” section says this again in even more depth. In your code example you are literally listening to a single AS3 enterFrame. The only savings I see is by making sure that you only listen to that *single* enterFrame and not have lots of enterFrame listeners. But if, as you claim now, Event objects are not created when passing from frame to frame, then why would it matter in the first place? Maybe I have missed something fundamental about this discussion… :)

    jacksondunstan

    27 Nov 09 at 6:52 pm

  7. Hi Jackson,

    I think you've missed the point – that or I'm being foolish!

    The hypothesis is that there's a difference between adding an event listener using addEventListener, and triggering a method to be called in a frame script. The first necessitates the creation of an Event object, but there's no reason why the method called by the addFrameScript should create an event object. In which case, there ought to be a marginal saving between a loop created by addFrameScript over a loop created by addEventListener. As I say, it's a hypothesis. Joa Ebert's results cast doubt about it, but until a rigorous, isolated test can be done, and both the test and results agreed to be accurate, we can't really be sure.

    Perhaps the confusion stems from the fact that I do in fact create an onEnterFrame listener in the EnterFrameDispatcher class? I do, because the [Frame] metadata tag works in such a way that the original class (Main) is inaccessible until the second frame. I have to wait for a frame to elapse before I can inject the frame Signal into the Main class. I add an ENTER_FRAME listener, then trigger nextFrame(). As soon as the onEnterFrame is triggered I remove that listener – it is not responsible for the loop.

    The loop is in fact generated by the addFrameScript(0, frameMethod, 1, frameMethod) line, which attaches the frameMethod function to both frames in the 2-frame timeline (created by the [Frame] metadata tag), which I then play.

    alecmce

    27 Nov 09 at 7:55 pm

  8. I see what you're getting at now. I saw the ENTER_FRAME listener but didn't see that you remove it and replace it with an addFrameScript. I hadn't heard of addFrameScript, probably because it's undocumented. It does seem to fix the problem I brought up in my initial comment of all rapid update callbacks requiring an AS3 event since this approach just uses a plain Function callback. I like the idea and really look forward to someone doing as you suggest: a rigorous, isolated test. Joa's results are, sadly, not encouraging. :( Thanks for the fascinating article and patient explanation!

    jacksondunstan

    28 Nov 09 at 12:56 am

  9. [...] Replacing ENTER_FRAME – Testing Tickers [...]

  10. [...] This implementation came out of a discussion on Twitter that I was involved in on the fringes; I wrote about it in a previous blog post. [...]

  11. How about of triggering stage.invalidate() from within Event.ENTER_FRAME handler, and then subscribing to the Event.RENDER to defer updating objects on the screen?

    og2t

    19 Jan 10 at 11:18 am

  12. I'm not sure that would give us any performance improvements would it? It is certainly need in that it cuts down the number of event objects being constructed per cycle, but it is still double the events that the EnterFrame ticker uses: one event for the ENTER_FRAME and one for the RENDER.

    Still, a nice addition to the panoply of alternatives, tanks og2t

    alecmce

    20 Jan 10 at 12:37 am

  13. Hi Alec!

    Thank you for sharing.

    As your code example doesn't “do” anything, maybe the following change/addition to your could clarify it, for Signal newbees like me…:

    public function init():void {
    // functionality here…
    trace('init');
    frame.add(onFrame);
    }

    private function onFrame():void {
    trace('onFrame');
    }

    Regards! / Jonas

    cambiata

    30 Jan 10 at 2:23 pm

  14. I'm a bit confused. My understanding is that, prior to the Flash 10.1 Player, there was nothing in the basic Flash code that checks to see if anything is listening before dispatching an event. So it seems to me that at the time this was written eliminating listeners to enter frame events wouldn't logically affect performance, because those events would have been dispatched whether anything was listening or not.

    Can someone point me to some documentation that speaks to how different versions of the Flash Player determine whether to dispatch these events?

    Amy

    1 Apr 10 at 2:50 pm

  15. [...] Replacing ENTER_FRAME [...]

  16. Hi, I've set up an experiment about these EnterFrame approaches and posted the report on my blog (http://bit.ly/cgVjdT). To sum up, it is possible to say that frame-loop doesn't generate ENTER_FRAME events. However, with a single frame-loop or enterFrameHandler instance, there's virtually no difference between the 2 approaches in terms of performance and memory. The ENTER_FRAME events only affect if you use them in large scale.
    BTW, I've tried your [Frame] tag technique for the TickManager and found that, it included many redundant Flex's classes, while my demo only compiled with pure AS3. So I still prefer the Flash CS symbol approach of Penner. I know if you use the parameter “-frame” in FlexSDK compiler, you can also have a 2 frame movie clip but without any Flex's classes. You may consider this if you need improvement.

    Thanh

    1 Jul 10 at 7:13 am

Leave a Reply

blog comments powered by Disqus