14
votes

I've been tasked with finding (and potentially fixing) some serious performance problems with a Flex application that was delivered to us. The application will consistently take up 50 to 100% of the CPU at times when it is simply idling and shouldn't be doing anything.

My first step was to run the profiler that comes with FlexBuilder. I expected to find some method that was taking up most of the time, showing me where the bottleneck was. However, I got something unexpected.

The top 4 methods were:

  • [enterFrameEvent] - 84% cumulative, 32% self time
  • [reap] - 20% cumulative and self time
  • [tincan] - 8% cumulative and self time
  • global.isNaN - 4% cumulative and self time

All other methods had less than 1% for both cumulative and self time.

From what I've found online, the [bracketed methods] are what the profiler lists when it doesn't have an actual Flex method to show. I saw someone claim that [tincan] is the processing of RTMP requests, and I assume [reap] is the garbage collector.

Does anyone know what [enterFrameEvent] is actually doing? I assume it's essentially the "main" function for the event loop, so the high cumulative time is expected. But why is the self time so high? What's actually going on? I didn't expect the player internals to be taking up so much time, especially since nothing is actually happening in the app (and there are no UI updates going on).

Is there any good way to find dig into what's happening? I know something is going on that shouldn't be (it looks like there must be some kind of busy wait or other runaway loop), but the profiler isn't giving me any results that I was expecting. My next step is going to be to start adding debug trace statements in various places to try and track down what's actually happening, but I feel like there has to be a better way.

7

7 Answers

5
votes

Theres a couple things that typically happen on an enterframe Handler within a flex project. Some things to watch for

  1. Manual < mycomponent enterFrame="" > event responses or ones added manually via component.addEventListener(Event.ENTER_FRAME, myfunc)

  2. callLater() calls, these happen ALOT in the framework and can be byproduct of jumping down any number of rabbit holes, developers tend to use these alot to solve timing related problems and sometimes bad code can cause these to continue calling every frame. For example, there are ~120 occurrences of a calllater() in the latest flex sdk build.

  3. lastly, I can't guarantee that the [enterframeEvent] handles just enterframe specific event callbacks, and not timers, mouse, etc.. events, since enterframes occur during the main event loop, you may be seeing the cumulative result of all events firing from the main event pool. I'm not saying this IS whats happening, but I can't say it ISN'T whats happening either, I don't know enough about the internals there to be sure.

/edit also as stated earlier, the [enterFrameEvent] should technically fire internally at the start of each frame, but it shouldn't be doing anything unless events have been explicitly attached to it to execute user code.

3
votes

Some updates: We aren't doing anything in the app other than listening for events and using bindings...meaning, no ChangeWatchers, no manual polling...just waiting for events. We are connected to FMS the entire time, so there is some overhead for that, but it's minimal. Bindings are not super efficient in Flex, and we've found that it's not good to add the [Bindable] metadata keyword directly to classes (in high volume, with a lot of classes). We aren't doing much of this, but it's one way to squeeze a bit more performance out of your app. If you use the [Bindable(event="usersUpdated")] then you have control over the binding, and it will only fire when you dispatchEvent(new Event("usersUpdated")) from within a function in the class, ie, your setter for 'users'.

Anyone who's used System.gc() in Flex or AIR will tell you that Flex garbage collection is a joke. It is a partially implemented feature and no one trusts it. There are tricks for this too...call it twice, wait a frame, call it again. It might clean up your old objects, but don't cross your fingers...Flex does what it wants.

Also, use temporary objects to decrease the number of bindings fired. Instead of...

myUser.location = new Location(); myUser.location.state = "CO"; myUser.location.city = "Denver";

do...

var tempLoc : Location = new Location(); tempLoc.state = "CO"; tempLoc.city = "Denver"; myUser.location = tempLoc;

The former fires 3 bindings to anything bound to location.*, while the latter should only fire 1 binding (in reality it's usually extra due to the way Flex handles it.)

Bindings won't kill your app until you have a lot of them in a visually rich application....binding and rendering seem to be Flex's slowest jobs.

Another interesting thing: create a brand new Flex3 app in Flex Builder and run it in the browser. Our tests showed that the CPU stays between 8-10% on a MacBookPro (when the app is idle, and the browser window hidden). Our application now runs steadily at ~20% and while it spikes higher in order to handle view changes and the like, it always returns to a level close to 20%. Our initial concern was that there was a memory leak or something running away that would take the CPU very high and leave it hovering around 40-50% (again, on the MBP...all relative to this machine). We took out all references to Degrafa and while we noticed a good bit of performance increase, it didn't account for everything. The empty Flex app was enlightening though - Flex itself hogs 8-10% CPU at all times, even when idle.

Yet another find...if using Mate, be careful how you handle switching views. It's easy to have assets available and just hide&disable them when they're not being used, by using an injector and a binding in MXML, but Flex is not very smart when it comes to hiding/disabling things. It's best to create the views on the fly, and destroy them when they are done. Even though the initial creation may take more time and there will be a longer wait between views, use some display magic (a progress bar, spinning disk, etc) to indicate that the view is switching, wait for creationComplete on the view, and then fade into it.

Also, stay away from ViewStack for visually rich apps. Manage your own stack.

So far this thread has gone over basic performance issues common to any language, but Flex is a very special little boy in many ways ("special" not always considered a positive thing). There are innumerable pitfalls because it's built on a very visual platform, yet it's built for RIAs, so while Flash Player may optimize video, animations, etc, it won't optimize a Flex app. Do not expect Flex apps to perform the same as Flash apps. There is also a big difference between AVM (ActionScript Virtual Machine) for AS2 and AS3.

This is simply scratching the surface of performance issues and potential gains in Flex apps. This is a dark art and it's easy to see why.

Code on, ninjas.

3
votes

An update to this post should anyone come across it in the future...

A few coworkers at EffectiveUI had this exact problem with their application a couple of months later, so it was revisited. It was discovered that when using Flash to produce visual assets, USING STATEFUL SKINS, and exporting them to SWC with the Flash/Flex asset toolkit, you get runaway Flash movies (presumably something internal to the way this is implemented, say, forgetting to put stop() commands in frames).

There is apparently nothing you can do about it except to go in and remove all stateful skins. A good write-up can be found here:

http://patrickhansen.com/blog/index.php/2009/03/05/flex-stateful-skins-vs-stateless?blog=3

And a JSFL script that you can use to convert stateful skins to stateless skins:

http://patrickhansen.com/blog/index.php/2009/04/08/stateful-to-stateless-jsfl-flash-command?blog=3

Hope this helps someone! It's a very nasty, mysterious bug, but you can work around it!

Cheers

1
votes

I think your problem lies elsewhere. This happens because Flex is built on top of Flash and Flash fires that event as often as the framerate (so like 20-30 times a second).

http://www.adobe.com/support/flash/action_scripts/actionscript_dictionary/actionscript_dictionary546.html

EDIT: I'm not saying your solution would be to lower the framerate. That would only work if the event u noticed were the issue. I'm not convinced that that is actually what's causing your slowdowns. It may be the calling a function that is causing the issue... but that event itself isn't it. It's supposed to fire a lot.

1
votes

This link explains each of the [function]'s described in the performance profiler:

http://livedocs.adobe.com/flex/gumbo/html/WS6f97d7caa66ef6eb1e63e3d11b6c4d0d21-7edf.html

0
votes

Justin, thanks for the reply. The issue is not with enterFrame executing, but rather with it trying to do too much in each iteration.

FYI: Coincidentally, the original poster and I are dealing with the same application. We have decided to remove all Degrafa resources in favor of ProgrammaticSkins. I'll report the findings here when we have completed this.