Home  >  

Flex RIA Performance Considerations Part 4: The Display Hierarchy

Author photo
AddThis Social Bookmark Button

Overview

When building a Flex application, you need to consider the objects you display and how they interact with the end user, the Flash Player, and even the Flex Framework itself. Performance is all about doing things in the least amount of CPU cycles, and if you're not careful, you could be taxed on CPU cycles that you may not necessarily need.

By keeping in mind instantiation, the various properties an object has, and also the way in which it interacts with the framework and player, you will be able to build your apps with good performance and as a result good CPU utilization and in some cases, a better memory footprint.

Display Hierarchy: Instance Considerations

First up, we need to consider that every container, control, shape, media asset, and textual item you see in your app needs to be added to the display hierarchy in some form or fashion. This hierarchy is known as the display list, and you can think of it as a tree of display objects that might need to be drawn. This display list tree starts at the Stage, and through the main Application class it trickles down through all the children actively participating in the display list.

If you want to learn a ton about the display list, feel free to check out the Adobe Livedocs, but in regard to my point you just need to know that the more objects you put on the display list, the more CPU cycles you're going to chew up on each redraw. This is because each time the Layout Manager is invoked to figure out how the pieces of the application need to be sized and positioned, the Layout Manager needs to make three passes through the display list objects: a commitment pass, a measurement pass, and a layout pass. Unfortunately or fortunately, depending on viewpoint, each of these passes will interact with every display list participant to determine the final sizing and positioning of the display objects at render time.

So, to help with optimizing performance, and lessening the load on the Layout Manager, try and express yourself with the minimum number of objects that you can. For starters, one of the best ways to reduce the number of objects participating on the display list is to analyze your application for redundant containers. Another way is to defer the instantiation of objects until they are needed. A third would be to create your complex objects offstage and then add them to the display list when their children are ready to be displayed.

Container Nesting:

When you come from an HTML world and are used to building table-based HTML pages you think of nesting tags to build your layout. For instance, you would have a <table/> nested inside another <table/> and so forth. When used to that mentality you might find yourself using nested <mx:VBox/> and <mx:HBox/> or even <mx:Grid/> to accomplish your multi-columnar layout like you've been used to. As we said before, when an invalidating change happens within the application, for instance, when a user changes the width of their browser window, the Layout Manager will need to make it's three passes to figure out the new sizing and positioning information. The more containers you have during this time, the more stops the Layout Manager will need to make during the process causing the passes to take longer.

Here are links to two versions of an application. The first makes use of containers in a very liberal fashion, while the second is much more frugal. Both applications have view source enabled.

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_ContainerNesting_BAD/InsideRIA_Part4_ContainerNesting_BAD.html

and

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_ContainerNesting_GOOD/InsideRIA_Part4_ContainerNesting_GOOD.html

If you pull down the source and do a diff compare between the non-optimized and optimized version, you will see that there were a number of containers removed. For those that don't have the time to analyze the code, here is a list of the changes:

  • Removed an unnecessary <mx:VBox> that was a direct child of the <mx:Application> with default layout set to vertical.

  • Removed two <mx:VBox> from the main app file that we surrounding some buttons. The buttons were set to 100% width and when I removed the <mx:VBox> I hard coded the width of the buttons to match what the width of the VBox had been.

  • The custom button component com.junheider.view.MyLameCustomButton.mxml had an unnecessary <mx:Canvas> as the root node which I removed.

Although you probably won't notice much of a difference between the two examples when you run them, I have a screen cap of Flex Builder Profiler results to show where you gain performance when you remove unneeded containers. Check out the Average Cumulative Times for LayoutManager.validateDisplayList, LayoutManager.validateClient, and LayoutManager.doPhasedInstantiation. You will see that the gains are anywhere from 0.74 to 3.67 milliseconds on each call!

part4_container_nesting_profiler_results.png

I could go on and on about container nesting and this post could get very long. Luckily for me, Adobe's Brandon Purcell and Deepa Subramaniam provided a very wonderful article about performance back in the day - the article is almost as old as my four year old daughter - and there's tons of examples of bad container nesting. Here's the link: http://www.adobe.com/devnet/flex/articles/client_perf_05.html

Deferred Instantiation:

Like a broken record, I feel like every post about performance mentions this. However, deferred instantiation is a very big deal with helping to improve not only perceived performance to the end users of your application but also potentially the memory footprint. For those of you that are new to the concept, deferred instantiation is a way of adding objects to the display list in a just in time manner.

As an example, if you are building a catalog application, why instantiate 500 display objects to represent each piece of data in your collection when you're only ever going to actively show 25 at a time? Draw the initial 25 display objects and then either recycle them or instantiate new objects only as they're needed when they're needed.

I already discussed deferred instantiation in a previous post, so why re-invent the wheel, right? Anyway, here's the link to the info, complete with code samples: (Look for the section on "Deferred Instantiation"): http://www.insideria.com/2008/04/flex-ria-performance-considera.html

Adding Children:

Although you can add a display object to the display list very early in its creation and initialization it's probably a good idea to step back and think about the right time to add it to the list. For instance, if you have a very complex custom component with many children, it might make sense to build it off the list then add it to the list after it and it's children have been fully initialized.

Display Hierarchy: Property Considerations

Every object has properties, even display objects. The reason they have these properties is to define the essence of why they exist. The reason I bring up properties, is because improper use of some of these properties can be a negative impact on display list performance.

First there's DisplayObject. It is a base class that was written to represent objects than can be added to the display list. Coincidentally, DisplayObject has a couple of properties that represent whether or not you can see a DisplayObject: visible and alpha. The visible property can be set to true or false with true being visible and false being invisible. The alpha property can be set to numeric values between 1 and 0 with 1 being completely opaque and 0 being completely invisible.

Someone new to development or simply in a rush may decide to use visible=false and alpha=0 interchangeably, but that could be bad. The difference between the two is that DisplayObject instances that have visible=false are disabled from mouse interaction whereas DisplayObject instances with alpha=0 are still active. Using alpha=0 in place of visible=false in a standard use case will result in unnecessary CPU cycles being used to dispatch mouse events on an effectively invisible object.

I've uploaded a little tester app. You can find it here:

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_IsItInvisibleOrAlpha0/InsideRIA_Part4_IsItInvisibleOrAlpha0.html

If you click start test, the top image will be made invisible by visible=0 and the bottom will be made invisible by alpha=0, same goes for clicking on the "hide the images" button. Also, by default these images don't spit any output out to the TextArea consoles unless you start the test or click on the "make noisy" button. Play around with it, the images are configured to output messages on mouseOver, measure(), commitProperties(), and updateDisplayList().

You can also uncomment a couple lines of code to get them to be noisy when render or updateComplete fires. I suggest playing around with "InsideRIA_Part4_IsItInvisibleOrAlpha0.html" by mousing over the images when they're hidden, or turning "Show Redraw Regions" on in your Flash debug player. Key take-away is really the only difference between the two is that visible=false turns off mouse interaction.

Another DisplayObject property to keep in mind is called cacheAsBitmap. When you set this property to true on a DisplayObject, the Flash Player will end up caching a bitmap of this object rather than wasting cycles on vector rendering. This is definitely a property to keep in mind when running transitions and effects or if you have a display object that isn't interactive and/or remains relatively static. Just keep in mind that on first interaction with the DisplayObject the Flash Player will throw out the cached bitmaps and start all over again. In other words, don't set cacheAsBitmap to true on the dynamic and interactive parts of your display list since it will just result in wasted CPU cycles.

Moving on, we find ourselves looking at InteractiveObject. Being that InteractiveObject represents a base class that the user can mouse around with, it has a mouseEnabled property. This property deals with interactivity rather than the display list and rendering, so it doesn't necessarily fit in with this topic. However, most every object you will put on your display list will be interactive, and it's a property that's defaulted to true. I just found this really cool post by Nate Chatellier recently regarding how mouseEnabled can affect performance and CPU utilization. Here's the link: http://natejc.com/blog/?p=83

Display Hierarchy: Component Considerations

Custom Components:

As I said earlier, the Layout Manager makes several passes throughout the display list whenever an invalidation occurs necessitating an update of the sizing and positioning of the display objects in your app. When the Layout Manager makes these passes through the display list of your Flex application it can involve some pretty important methods of each of the participating display object. Components added to the Flex display list through addChild() calls must implement the IUIComponent interface and are normally subclasses of UIComponent in some form or fashion. The reason behind this is so that during the passes Layout Manager can call the required interface methods: commit(), measure(), and updateDisplayList(). That being said, we need to make sure not to bloat these methods, otherwise we run the risk of performance situations.

Here is the link to two sample applications with view source enabled:

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_UpdateDisplayList_BLOATED/InsideRIA_Part4_UpdateDisplayList_BLOATED.html

and

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_UpdateDisplayList_NOT_AS_BLOATED/InsideRIA_Part4_UpdateDisplayList_NOT_AS_BLOATED.html

If you open the code for the first example InsideRIA_Part4_UpdateDisplayList_BLOATED.html and look at the updateDisplayList() method in the MyBloatedButton.as class you will see:

 
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
 	var numberToAvg:int; 
 	var startTime:int = getTimer();
 	
 	super.updateDisplayList( unscaledWidth, unscaledHeight );
 
 	graphics.clear();
 		
 	for( var i:int=0; i < 300; i++ )
 	{
  		var newPoint:Point = new Point( ( i * 2 ) , ( 50 )  );
  
  			if( i == 0 )
  			{
   				graphics.moveTo( newPoint.x, newPoint.y );
   			}
  			else	
  			{
   				graphics.beginFill( uint( Math.random() * 16777215 ) );	
   				graphics.drawCircle( newPoint.x, newPoint.y , 2 );
   				graphics.endFill();
   			}
  	}
 	
 	trace( "updateDisplayList took:" + ( getTimer() - startTime ) + " ms"  );
 	output += "updateDisplayList took:" + ( getTimer() - startTime ) + " ms\n";
 	
 	millisecondsArray.push( getTimer() - startTime );
 	
 	for( i=0; i < millisecondsArray.length; i++ )
 	{
  		numberToAvg += millisecondsArray[ i ];
  		avgMilliseconds = numberToAvg / millisecondsArray.length;	
  	}
}

As you can see the code is very inefficient in such a crucial method. In the second example "InsideRIA_Part4_UpdateDisplayList_NOT_AS_BLOATED.html" several changes were made which resulted in a performance gain of about 2 milliseconds:

  • The Point class is reused and instantiated outside the for loop

  • The conditional block to decide whether to moveTo() or actually draw was unnecessary and removed

  • The trace statement was stripped out

  • The loop through the results Array and calculation of the avg milliseconds was moved into a different function

Granted, 2 milliseconds is not too much of a gain, but in this example we just optimized one component. Imagine the performance gain you could realize by removing the bloat from the updateDisplayList(), measure(), and commitProperties() of all of the custom components being displayed in your application?

As a Flex developer, it's good to know the various events that are dispatched during the lifecycle of a component. These events can range from preinitialize to creationComplete and happen at pre-determined times. For instance, the creationComplete event is a framework event dispatched by UIComponent and is fired by a component after its constructed, the properties are set, any drawing is complete, and all of its sizing and layout has been determined.

The reason I bring up creationComplete is that it is an event that is pretty regularly used by Flex developers to do their custom initialization routines. For instance, in a creationComplete handler you may see stuff like setStyle() calls to implement runtime styles. Although certain types of non-invalidating initialization routines are fine to implement in a creationComplete handler, you're going to want to shy away from calling setStyle() at creationComplete or better yet, not even using setStyle() at all for setting initial styles. There are other ways to style your components such as using external CSS style sheets or blocks.

If you absolutely have to use the setStyle() call to set style properties in your application just keep in mind that it's a very expensive process. When setStyle() is invoked it will not only affect the component you're styling, but will also cause the component's children to run a style lookup afterwards. Requiring a component's children to do an extra style lookup is a waste of CPU time, and if you have to do it, it's why it's highly recommended to make your setStyle() calls early in the lifecycle of a component, such as when the preinitialize event is fired.

Here are the links to two sample applications with view source enabled:

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_SetStyle_BAD/InsideRIA_Part4_SetStyle_BAD.html

and

http://labs.realeyes.com/labs/examples/InsideRIA/Part4/InsideRIA_Part4_NO_SetStyle_GOOD/InsideRIA_Part4_NO_SetStyle_GOOD.html

If you run each of the examples and pay attention to the display at startup you will notice that in the first one "InsideRIA_Part4_SetStyle_BAD/InsideRIA_Part4_SetStyle_BAD.html" you actually see the styles being applied twice. In other words, the call to setStyle() in the creationComplete handler causes a perceived performance issue with the user since they first see the small black font and then a second later they see the intended large red font. The code that caused this was:

private function _onCreationComplete( p_evt:FlexEvent ):void
{
 	main_hbox.setStyle( "color", "0xFF0000" );
 	main_hbox.setStyle( "fontSize", "32" );			
}

What you see in the above code block is something you should avoid at all costs. By using external CSS, Style blocks, or inline styling as in the case of the example "InsideRIA_Part4_NO_SetStyle_GOOD.html" you will not run into the perceived performance issue and see the red font right off the bat.

Summary

So, now you should have a feel for some of the main things to watch out for in regard to optimizing the display list in your Flex applications:

  • Display objects are nice, without them we'd have an empty stage to look at. However, keep the Layout Manager and it's sizing and positioning passes in mind when you do add your object to the display list. In other words, only use what you need and recycle objects when possible. For instance, keep the number of containers in your application down to a minimum.

  • Use the API docs to your advantage and get to know the properties of the various classes available to you, if you do, you can avoid problems caused by misunderstandings such as interchangeably using alpha=0 when you really mean visible=false.

  • Remember how the Layout Manager interacts with objects on the display list and how UIComponent was built to work. With this knowledge you can remember not to bloat important methods such as updateDisplayList(). Also, remember that by keeping in mind the lifecycle events of a component and when they occur you can prevent very expensive code routines such as calling setStyle() during a creationComplete handler.

Before I conclude this month's post, here are links to various online articles and livedoc pages that I used as reference during my analysis. If you're looking for more information and any more examples, these are the places to look:

In the next part of this series we'll be talking about how to deal with data and the implications that its use can have on your application.

Read more from Jun Heider.

Comments

4 Comments

Alan said:

Thanks for the info.

Jun Heider said:

You're welcome!

Eric Belair said:

Hey Jun.

We met a while back (last year you gave a CF class I attended at FreeLife International in Milford, CT). Thanks for this great article. I have a question for you that is related to this. Please email me (ejbelair at hotmail dot com.)

Thanks.

Eric.

Jun Heider said:

Howdy Eric,

Good to hear from you and thanks for the kind words. Check your hotmail inbox. :)

Leave a comment


Type the characters you see in the picture above.

Tag Cloud

Poll: Adobe MAX

Will you be attending Adobe MAX next week?

Vote | View Poll Results | Read Related Blog Entry

Latest Features

Recommended for You

Development Series

Get an overview of the tools and technologies that work together to allow developers to build Rich Internet Applications (RIAs) quickly and easily.

Anatomy of an Enterprise Flex RIA

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.