Wednesday, June 16, 2010

10 Tips for Flex Application Performance

1) Manage your event listeners - this message is two fold. First, you should always remove event listeners that are no longer needed. They can lead to object references that prevent the garbage collector, which equates to memory leaks, which can be very difficult to track down and detrimental to application performance. You can use weakly referenced event listeners to minimize memory leakage, but you still should explicitly clean them up when you don't need them anymore. The second factor is that failure to remove event listeners can cause performance issues. Event handlers could be firing within your application, which you weren't even aware of. You dispatch an event in a child component, and there could be handlers up the DOM tree (parent objects) that are also firing on the same event. If you don't want this to happen, be explicit with your event handlers; make them handle specific event types, and get rid of them when your application doesn't need them anymore.

2) Unload loaders - any time that you are using an object based on a loader (Image, SWFLoader, etc...), it's a good practice to call unloadAndStop() to unload the content from the loader, and invoke the GC. This will free up valuable system resources and cpu cycles won't be wasted if they aren't needed. I typically even do this for static image files, to prevent memory usage from creeping up.

3) Dispose of things - I find it to be a very good practice to create "dispose()" functions in your custom components, data managers, or views that will clean up the object's resources. The dispose() method will need to be explicitly invoked when you are finished using an object, but that dispose method will handle everything needed to clean up an object and free it's resources. For example, stop timers, remove event listeners, unload loader objects, set variable references to null, etc... Basically, get rid of anything that could possibly cause a memory leak or eat cpu cycles behind the scenes. Yes, it takes cpu cycles to invoke a dispose method, but trust me. It is much easier and much less computationally expensive to explicitly dispose of objects, rather than burn time, computation resources, and budget tracking down leaks and performance issues.

4) Handle collections properly - There are a few things I see all the time, and they're always the first things to change if I see them. Collections (ArrayCollection, XMLListCollection, etc...) are helper classes that wrap primitive structures like array or xmllist. In order to help make working with those primitives easier, the collection classes do things that can be computationally expensive if you aren't aware of them. The reason that bindings to collection work is because every time you add, remove, or update an item, events get dispatched. They also get dispatched every time you refresh a collection.

The first tip is to be conscious of collection events. If you loop over a collection and update 100,000 items, 100,000 events will get dispatched. This can cause massive performance implications, and can completely lock your application UI. If you don't need those collection events to be dispatched, you can use the disableAutoUpdate() function to suspend collection events. Just be sure to turn them back on when you are done, or if you need them again, using the enableAutoUpdate() function.

The second, is to not use a collection if you don't have to. If all you need is to do loop over 100,000 items, and you arne't using data bindings, then use an array.

And the third tip on collections is only when collections are filtered using a filter function... If a filter function is applied, you don't need to call the refresh() function every time you add a new object to the collection. This can cause performance hits, in some of the least expected places. For example, if you have a datagrid bound to a collection, and have another process which updates that collection. If there is a filter on the collection, it will automatically get filtered when you call the collection's addItem method. Calling the refresh() method after adding an item will cause the list data of the datagrid to be invalidated, thus causing the entire datagrid to be re-validated and re-drawn. This is easily missed when optimizing, and can make drastic changes in application performance.

5) Use deferred instantiation - By default, all navigational containers within Flex (tab nav, accordion, viewstack, etc...) only create their children as they are needed. This prevents the application from creating thousands of components that aren't needed yet, which helps keep the application running smooth, and without hogging resources or locking up. Changing the creation policy can cause big problems if you are not careful.

You should also keep deferred instantiation in mind when creating your own custom components. Don't create child objects in the constructor. Instead, override the createChildren() method, and create them in there. This way, your components also follow deferred instantiation rules, and they don't introduce any performance issues that can be difficult to track down.

6) Object recycling vs new objects - I've written about this one before, but I'll say it again. It is often less expensive to reuse existing objects, rather than creating new ones. This goes hand-in-hand with data virtualization.

7) Don't invalidate/destroy/re-validate your objects if nothing changed.
If you are building custom components, and someone changes a property (through a getter/setter), don't invalidate the component properties if the incoming value didn't change. This will cause the component to go through the full invalidation/validation lifecycle, causing properties to be re-validated/comitted, and the object to be redrawn on the display list. Only invalidate properties if something actually changed. Here is a straightforward example to demonstrate the concept:

public function set myProperty( value : Number ) : void

{

if ( _myProperty != value )

{

_myProperty = value;

propertiesChanged = true;

invalidateProperties();

dispatchEvent( new Event( "change" ) );

}

}

8) Dynamic/Generic vs. Typed Objects - Dynamic and generic objects certainly have their place. They are generic, flexible, can be modified with any attribute, and can be used in a wide variety of situations. However, if you have a typed object and do not need the generic qualities, use strongly typed objects. The strongly typed nature of ActionScript 3 is one of the reasons it is fast. Accessing properties of strongly typed objects is simply faster than accessing properties of generic & dynamic objects.

9) Use constants when applicable - If you have a value that does not ever change, but you reference it all the time, use a constant. Constants are accessed faster and require less overhead.

10) Use static members - Static properties and functions do not require a variable instance to be invoked or accessed. Since they don't require an instance, it is faster to access them directly from the class, and does not require the memory necessary to instantiate the object. Utility functions, or functions that do not require attributes of a specific instance should be put into static functions.

Speaking of constants in #9... normally I suggest to make constants static. This will help you keep your memory footprint to a minimum b/c it will not need to be attached to a class instance.


No comments:

Post a Comment