views:

1657

answers:

8

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.

A: 

I'm dealing with the exact same issue. If you have any insight I would appreciate an update, and I will bookmark this page and enter an answer if we figure it out first.

Are you by chance using Degrafa in your application?

Hey Jordan! I'm actually talking about *exactly* the same issue as you. It's Andy from Premiere. I posted this question yesterday when I came across the error (and before I emailed you about it). :)
Herms
+1  A: 

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.

Justin Bozonier
I tried playing around with the framerate, but it didn't have any real effect on the performance. Lowering the framerate too much would cause the UI changes to run too slowly and wouldn't fix the problems we're seeing.
Herms
The thing is, the profiler claims that event ITSELF is taking up 32% of the CPU time (it plus what it calls takes 84%). That's a very significant amount of time for an event. Something is going on, but I can't tell what.
Herms
A: 

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.

+3  A: 

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.

Just FYI: an empty Flex app on my machine runs consistently at 0% CPU. I'm going to test it on a crappy old box later to see if it's any different. So I don't think that's a particularly useful thing to look at, since the performance problems we have in the app are in Windows as well.
Herms
+5  A: 

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.

seanalltogether
But any methods passed to callLater or added as listeners should show up in the profiler, shouldn't it? Since those would be actual AS3 methods I'd expect the time to be shown in those methods, not within enterFrameEvent itself. That's what's strange.
Herms
Yes and no, enterframeEvent would be a cumulative of everything going on, if you had 10 or 20 callbacks being processed during that event they might all be minor and not register as much in the profiler seperately, but together they represent alot of time.
seanalltogether
The profiler shows 2 stats. Cumulative and Self. Cumulative is what you're talking about. I believe Self is how much time is spent in that method specifically, and not in the other profiled methods.
Herms
No, when I mean cumulative I mean everything happening within that dispatch. If you have functionA and functionB, and A calls B within its scope, then the time for A is going to included the time for B, they aren't separated. Likewise, events contain the time of all functions they dispatch to.
seanalltogether
Cumulative and Self in the profiler should refer to time over the duration of the app (cumulative) vs time spent during the snapshot (self)
seanalltogether
+2  A: 

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

A: 

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

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

Mark Ingram