Tips for Optimizing Your Flex Applications – O’Reilly Answers

Developing Rich Internet Applications for the Enterprise with Flex can be rewarding if you know what to look out for. This excerpt from Fain, Rasputnis, & Tartakovsky’s Enterprise Development with Flex will help you keep your application lean and mean.

This section discusses three areas that may seriously affect performance of your application: memory leaks, Flash Builder’s Profiler, and the just-in-time compiler. At the end of this section, you’ll find a checklist of items that can help you in planning performance-tuning tasks.

Dealing with Memory Leaks

Wikipedia defines a memory leak as “a particular type of unintentional memory consumption by a computer program where the program fails to release memory when no longer needed. This condition is normally the result of a bug in a program that prevents it from freeing up memory that it no longer needs” (http://en.wikipedia….iki/Memory_leak).

Flash Player offers help in dealing with memory leaks. A special process called Garbage Collector (GC) periodically runs and removes objects from memory that the Flex application no longer uses. It counts all references to each object in memory, and when it gets down to zero, the object is removed from memory.

In some cases, two objects have references to each other, but neither of them is referred to anywhere else. In this case, the reference count never becomes zero, but Flash Player tries to identify such situations by running a slower method called mark and sweep.

Sure enough, you need to write code that nullifies reference variables that point to objects that are not needed (myGreatObj=null;): if you call addChild(), be sure not to forget about removeChild(); if you call addEventListener(), keep in mind removeEventListener().

The function addEventListener() has three more optional arguments, and if the last one is set to true, it’ll use so-called weak references with this listener, meaning that if this object has only weak references pointing to it, GC can remove it from memory.

Of course, if you ignore these recommendations, that’ll lead to littering RAM with unneeded objects, but your main target in optimization of memory consumption should be the unloading of unneeded data.

Closures

In some cases, there is not much you can do about memory leaks, and some instances of the objects get stuck in memory, gradually degrading the performance of your application.

A closure—or rather, an object representing an anonymous function—will never be garbage-collected. Here’s an example:

myButton.addEventListener("click",

         function (evt:MouseEvent){//do something});

With such syntax, the object that represents the handler function gets attached to the stage as a global object. You can’t use syntax like removeEventListener("click", myHandlerFunction) here, because the closure used as an event handler didn’t have a name. Things get even worse because all objects created inside this closure won’t be garbage-collected, either.

Note

Be careful with closures. Don’t use them just because it’s faster to create an anonymous in-place function than declaring a named one. Unless you need to have an independent function that remembers some variables’ values from its surrounding context, don’t use closures, as they may result in memory leaks.

You can’t use weak references with the listeners that use closures, as they won’t have references to the function object and will be garbage-collected.

Note

If you add a listener to the Timer object, use a weak reference; otherwise, Flash Player will keep the reference to it as long as the timer is running.

Opportunistic garbage collector

The GC will work differently depending on the web browser your Flex application runs in. The mechanism of allocating and deallocating the memory by Flash Player can be browser-specific.

How do you determine that you have memory leaks? If you can measure available heap memory before and after GC runs, you can make a conclusion about the memory leaks. But this brings the next question: “How can you force GC?”

There is a trick with the LocalConnection object that can be used to request immediate garbage collection. If your program creates two instances of the LocalConnection object using the same name in the connect() call, Flash Player will initiate the process of GC.

var conn1:LocalConnection = new localConnection();

var conn2:LocalConnection = new localConnection();

conn1.connect("MyConnection");

conn2.connect("MyConnection");

Note

It’s not typical, but you can use the LocalConnection object to send and receive data in a single .swf, for example to communicate between modules of the same Flex application.

Some web browsers force GC on their own. For example, in Internet Explorer minimizing the browser’s window causes garbage collection.

If you can force all your users to use Flash Player version 9.0.115 or later, you may use the following API to cause GC: flash.system.System.gc().

Just-in-Time Benefits and Implications

Flex compiler is actually a set of subcompilers converting your ActionScript and MXML code into different formats. For example, besides mxmlc and compc, there is a precompiler that extracts the information from the precompiled ActionScript Byte Code (ABC). You can read more about compilers at http://opensource.ad…Compiler Design. The ABC is the format that Flash Player runs. But the story doesn’t end here.

Most of the performance advances in the current version of AS3 as compared to AS2 are based on its just-in-time (JIT) compiler, which is built into Flash Player. During the .swf load process, a special byte code verifier performs a lot of code analysis to ensure that code is valid for execution: validation of code branches, type verification/linkage, early binding, constants validation.

The results of the analysis are used to produce machine-independent representation (MIR) of the code that can be used by the JIT compiler to efficiently produce machine-dependent code optimized for performance. Unlike Flash VM code, which is a classic stack machine, MIR is more like a parsed execution path prepared for easy register optimization. The MIR compiler does not process the entire class, though; it rather takes an opportunistic approach and optimizes one function at a time, which is a much simpler and faster task. For example, the following is how the source code of an ActionScript function is transformed into the assembly code of the x86 Intel processor.

In ActionScript 3:

function (x:int):int {

return x 10

}

In ABC:

getlocal 1

pushint 10

add

returnvalue

In MIR:

@1 arg  8// argv

@2 load [@1 4]

@3 imm 10

@4 add (@2,@3)

@5 ret @4 // @4:eax

In x86:

mov eax,(eap 8)

mov eax,(eax 4)

add eax,10

ret

The difference in time for execution of the ABC code and x86 can be on the order of 10 to 100 times and easily justifies having an extra step such as the JIT process. In addition, the JIT process does dead code elimination, common exp ressions optimization, and constants folding. On the machine-code generation side, it adds optimized use of registers for local variables and instruction selection.

You need to help realize these benefits by carefully coding critical (as opposed to overoptimized) loops. For example, consider the following loop:

for (var i:int =0; I

It can be optimized to produce very efficient machine code by removing calculations and references to other classes, thus keeping all references local and optimized:

var someConstant:String = SomeClass.SOMECONSTANT;

var len:int = array.length;



for (var i :int = 0; I

JIT is great at providing machine code performance for heavy calculations, but it has to work with data types that the CPU is handling natively. At the very least, in order to make JIT effective, you should typecast to strong data types whenever possible. The cost of typecasting and fixed property access is lower than the cost of lookup, even for a single property.

JIT works only on class methods. As a result, all other class constructs—variable initialization on the class level and constructors—are processed in interpreter mode. You have to make a conscious effort to defer initialization from constructors to a later time so that JIT has a chance to perform.

Using the Flash Builder Profiler

The Flash Builder Profiler monitors memory consumption and the execution time. However, it monitors very specific execution aspects based on information available inside the Virtual Machine and currently is incomplete. For example, memory reported by the Profiler and memory reported by the OS will differ greatly, because the Profiler fails to account for the following:

  • Flash Player’s memory for code and system areas: hidden areas of properties associated with display objects

  • Memory used by JIT

  • The unfilled area of the 4 KB memory pages as a result of deallocated objects

More importantly, when showing memory used by object instances the Profiler will report the size used by object itself and not by subobjects. For example, if you are looking at 1,000 employee records, the Profiler will report the records to be of the same size, regardless of the sizes of last and first names. Only the size of the property pointing to the string values is going to be reported within the object. Actual memory used by strings will be reported separately, and it’s impossible to quantify it as belonging to employee records.

The second problem is that with deferred garbage collection there are a lot of issues with comparing memory snapshots of any sizable application. Finding holding references as opposed to circular ones is a tedious task and hopefully will be simplified in the next version of the tool.

As a result, it is usually impractical to check for memory leaks on the large application level. Most applications incorporate memory usage statistics like System.totalMemory into their logging facility to give developers an idea of possible memory issues during the development process. A much more interesting approach is to use the Profiler as a monitoring tool while developing individual modules. You also need to invoke System.gc() prior to taking memory snapshots so that irrelevant objects won’t sneak into your performance analysis.

As far as using the Profiler for performance analysis, it offers a lot more information. It will reveal the execution times of every function and cumulative times. Most importantly, it will provide insights into the true cost of excessive binding, initialization and rendering costs, and computational times. You would not be able to see the time spent in handling communications, loading code, and doing JIT and data parsing, but at least you can measure direct costs not related to the design issues but to the coding techniques.

Note

Read about new Flash Builder 4 profiler features in the article by Jun Heider at http://www.adobe.com….html?devcon=f7.

Performance Checklist

While planning for performance improvement of your RIA, consider the following five categories.

Startup time

To reduce startup time:

  • Use preloaders to quickly display either functional elements (logon, etc.) or some business-related news.

  • Design with modularization and optimization of .swf files (remove debug and metadata information).

  • Use RSLs, signed framework libraries.

  • Minimize initially displayed UI.

  • Externalize (don’t embed) large images and unnecessary resources.

  • Process large images to make them smaller for the Web.

UI performance

To improve user interface performance at startup:

  • Minimize usage of containers within containers (especially inside data grids). Most of the UI performance issues are derived from container measurement and layout code.

  • Defer object creation and initialization (don’t do it in constructors). If you postpone creation of UI controls up to the moment they become visible, you’ll have better performance. If you do not update the UI every time one of the properties changes but instead process them together (commitProperties()), you are most likely to execute common code sections responsible for rendering once instead of multiple times.

  • For some containers, use creationPolicy in queues for perceived initialization performance.

  • Provide adaptive user-controlled duration of effects. Although nice cinematographic effects are fine during application introduction, their timing and enablement should be controlled by users.

  • Minimize update of CSS during runtime. If you need to set a style based on data, do it early, preferably in the initialization stage of the control and not in the creationComplete event, as this minimizes the number of lookups.

  • Validate performance of data-bound controls (such as List-based controls) for scrolling and manipulation (sorting, filtering, etc.) early in development and with maximum data sets. Do not use the Flex Repeater component with sizable data sets.

  • Use the cacheAsBitmap property for fixed-size objects, but not on resizable and changeable objects.

I/O performance

To speed up I/O operations:

  • Use AMF rather than web services and XML-based protocols, especially for large (over 1 KB) result sets.

  • Use strong data types with AMF on both sides for the best performance and memory usage.

  • Use streaming for real-time information. If you have a choice, select the protocols in the following order: RTMP, AMF streaming, long polling.

  • Use lazy loading of data, especially with hierarchical data sets.

  • Try to optimize a legacy data feed; compress it on a proxy server at least, and provide an AMF wrapper at best.

Memory utilization

To use memory more efficiently:

  • Use strongly typed variables whenever possible, especially when you have a large number of instances.

  • Avoid using the XML format.

  • Provide usage-based classes for nonembedded resources. For example, when you build a photo album application, you do want to cache more than a screenful of images, so that scrolling becomes faster without reloading already scrolled images. The amount of utilized memory and common sense, however, should prevent you from keeping all images loaded.

  • Avoid unnecessary bindings (like binding used for initialization), as they produce tons of generated code and live objects. Provide initialization through your code when it is needed and has minimal performance impact.

  • Identify and minimize memory leaks using the Flash Builder Profiler.

Code execution performance

For better performance, you can make your code JIT-compliant by:

  • Minimizing references to other classes

  • Using strong data types

  • Using local variables to optimize data access

  • Keeping code out of initialization routines and constructors

Additional code performance tips are:

  • For applications working with a large amount of data, consider using the Vector data type (Flash Player 10 and later) over Array.

  • Bindings slow startup, as they require initialization of supporting classes; keep it minimal.

 

Advertisements
This entry was posted in Adobe Technologies, General and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s