Best Practices for PerformancePerformance issues fall into three main categories: 1. Perceived Performance These guidelines address all three and aim to help developers produce Flex and AIR applications that feel responsive and make efficient use of system resources. Performance tuning is all about making trade-offs, so these are not black-and-white instructions, but rather explanations to be considered when optimizing your own applications. Note that "micro-level" optimizations, such as using bitwise operators to accelerate algorithms are not covered here. Also, profiling tools are not explained in detail, but it is assumed that readers are familiar with the Flex Builder Profiler and standard operating system tools, like Activity Monitor and Task Manager. For additional information about optimization on the Flash Platform, please read http://www.adobe.com/go/optimize Contents1. Perceived PerformancePerceived performance is about the way an application feels to users. If the application start-up seems to take a long time, or performing an action is sluggish, the perceived performance is poor. If the app feels fast and responsive, perceived performance is good. Sometimes the perceived performance can be improved easily, for example by changing the duration of an effect, and other times larger changes are required, like deferring the loading of data. Defer Creation PoliciesDefer the creation of view components that are not immediately visible. Creating and rendering large view components can be expensive, harming perceived performance. Instead, create views as they are needed, taking advantage of the default creation policies of containers such as the ViewStack and Accordion. Note that it is occasionally justified to create hidden views up-front, using an =all= creation policy, but this is the exception to the rule. If a view that is initially hidden participates in a transition, then the perceived performance of the transition may be improved by creating the view up-front. Create Lightweight Item RenderersList-based components like DataGrid, Tree and List create many item renderers up front and redraw them whenever changes occur in the data provider. For this reason, it is important that item renderers are lightweight in terms of creation time and redrawing. For purely textual item renderers, use or extend the default item renderers for the list-based component. i.e. DataGridItemRenderer, AdvancedDataGridItemRenderer, etc. For more complex item renderers, containing multiple text fields, graphics and other assets, prefer to extend UIComponent in ActionScript. Make use of lightweight children, such as TextField and Sprite, instead of Label and Image. Avoid using Containers for item renderers unless the simplicity outweighs the loss of performance. Make Effects SnappyEffects that play too slowly make an application feel slow, particularly when they occur regularly. Fine tune the effect play speen to make sure the app feels snappy. Parallelize Service RequestsApplications often perform many different service requests, particularly during start-up, when the user profile and initial data sets are usually loaded. These service requests are often independent of one another, so can be performed concurrently. If there is no need to wait for the result of request A before starting request B, then don't wait and start request B immediately. The ParallelTask feature of the Cairngorm Task library may help you to achieve this. Load Data on DemandLoad data on demand when it is needed by the user, instead of upfront. For example, load only the summary information for a collection of data items, then load the detailed data for individual items as the user navigates into them. For large collections, use paging to fetch only enough data to render the currently visible rows in a list-based component. Adobe products such as LiveCycle Data Services provide paging and lazy-loading features. Be Careful with StyleManagerOperations involving the style manager can be expensive, sometimes causing applications to become unresponsive while style updates are processed. In particular, using the setStyleDeclaration or loadStyleDeclarations methods with the update parameter set to true will cause an immediate update of all styles in the whole application, which can be time consuming. The best practice is the set the update parameter to false whenever you can. For example, if you are loading a mode and corresponding compiled stylesheet SWF at runtime, don't add the module to the display list until after the style loading is complete. Then there will be no need to refresh the styles and the update parameter can be set to false. 2. CPU UtilizationA well-implemented Flex application should have low and stable CPU when the application is idle, with short peaks in CPU during periods of high activity. For example, when viewing a static publication the CPU should remain low and stable, but when navigating to a new area of the application for the first time, the CPU should be expected to spike while the new views are created and rendered. The following are guidelines for achieving the expected CPU profile for a large Flex application. Minimize Redraw RegionsUse the "Show Redraw Regions" feature of the Flash Debug Player to see how much redrawing is occurring. Optimize your application so that only the parts of the screen that you need to change are redrawn. Take particular care with:
Minimize Background ProcessingThe amount of ActionScript processing that happens during each frame affects CPU. Most logic in a Flex application should be short-lived and happen in response to a user gesture. However, sometimes regular processing is performed in the background, perhaps in response to frame events or timers. Take care to minimize background processing, since it can accumulate and push up the CPU. Patch UIMovieClip for Stateful SkinsA good practice for skinning components is to produce stateful vector skins in Flash and export them with the Flex Component Kit. All skins produced in this way use UIMovieClip as their base class, as described in the Design Spec. However, pre-Spark versions of UIMovieClip perform sizing calculations on every frame which causes an accumulation of CPU. For Flex 3.x projects, this can be avoided by extending UIMovieClip and removing the event handler, as described by Guillaume Malartre. A consequence of his approach is that some skinned components may need to be explicitly sized. Don't Play Effects ExcessivelyPlaying effects consumes CPU because of the processing and redrawing that takes place during the effect. In applications, it is best to use effects occasionally and not frequently or continuously. For example, using effects to animate prices in a price grid that changes every second is not advisable. 3. Memory ConsumptionWhen a Flex application is running, Flash Player grabs the memory it needs from the operating system. It uses memory for storing the class definitions and object instances for your application and also for rendering to screen. A well-implemented Flex application will maintain an acceptable memory footprint by managing its class definitions and object instances, so garbage collection can take place effectively. The steps below can help to achieve this. Unload Modules EffectivelyFor large, modular applications, the lowest memory footprint can be achieved by unloading modules when they are no longer needed. However, there are some known issues that can prevent modules from unloading effectively. Conduct memory profiling and follow the advice from the following blog posts to ensure that modules are unloading effectively:
Note that in some cases, the better approach is the load modules once and then leave them in memory. The trade off is a higher memory footprint but more responsive navigation between modules. Loading once and leaving in memory also prevents memory leaks to do with loading duplicate modules into memory. Remove Singleton Event ListenersA common cause of memory leaks is to attach event listeners to singletons that reference short-lived objects. For example:
MySingleton.instance.addEventListener("something", somethingHandler);
The code above creates a reference from the singleton to the object with the somethingHandler function. That prevents the object from being garbage collected until the singleton itself becomes eligible. But since the singleton instance is stored in a static class variable, it won't be garbage collected until the class definition is garbage collected. And that won't happen until the application domain containing the class definition is garbage collected! So, use weak event listeners when listening to singletons or else explicitly remove event listeners afterwards. Consider Reusing PopupsThe Flex PopupManager class is usually used for opening and closing popup windows. It is common to create a new instance of a popup each time it is opened. This can cause a memory leak when the popup is later closed. Unless all references to the popup and its children have been removed, the popup will not be eligible for garbage collection. The next time the popup is shown, a new instance may be created and again never garbage collected. Avoid popup-related memory leaks by either: 1. Conducting memory profiling to ensure that the popup instances and all their associated objects are effectively garbage collected. There is a trade-off here, since option 1, when implemented correctly should produce an application with the lowest memory footprint. However, option 2 will prevent memory leaks and result in faster perceived performance when the popup is re-opened. The Cairngorm Popup library includes declarative tags for managing popup opening and closing that support reuse. Find Memory Leaks by Comparing Instance CountsThe Loitering Objects view provided by Flash Builder Debugger can sometimes contain false positives, because an application may legitimately have different instances, but the same instance-count, between two memory snapshots. In these situations, a better methodology for finding memory leaks is to compare the instance counts between two snapshots. Alex Harui describes this process on his blog post: More On Finding Memory Leaks Profile The Export Build with Release PlayerBeware that the Flash Debug Player consumes more memory when running an application than the release Flash Player, due to the instrumentation carried out for debugging and profiling. For this reasons, you should export a release build and profile your application in the release Flash Player using system tools such as Task Manager, Activity Monitor, Process Explorer, etc. These tools will give an accurate representation of the memory footprint experienced by users. Revert back to the Flash Builder Profiler to identify memory leaks and performance bottlenecks. ConclusionFor performance tuning, there are some best-practices that should just be followed as a matter of course, like using deferred creation policies and developing with a modular architecture. There are others that involve additional effort and are not always justified, like replacing easy-to-read with cryptic bitwise operations. When a performance optimization is likely to complicate the code, it's worth considering whether there might be a more elegant solution. For example, one might be tempted to introduce a pseudo-threading library to simulate multiple-threads when translating large quantities of data, but the better solution is not to serve the client with a large quantity of data in the first place. Instead, paging and lazy-loading can be used to serve smaller quantities of data on demand. |
|