Thursday, March 5, 2009

Flex Component Kit: CPU black hole

On a recent AIR project we tried to use state-based skins using the Flex Component Kit, as recommended by Adobe. This route was much cleaner and faster than the old, stateless method. And everything seemed great at first. However, as the app grew we noticed that our CPU usage while idle was getting ridiculous (70-80%) and made the disturbing discovery that removing our CSS allowed our CPU usage to plunge back to standard AIR levels (5-10%).

With a little more research we determined that the stateful skins seemed to be the culprit, so we created a simple test program to test our theory. In this program we have a single MXML component that is a standard WindowedApplication component that contains 500 checkboxes. We put together a simple checkbox skin with the stateful method and another that is stateless.

To establish a baseline we ran the application without loading either CSS file. The resulting CPU usage was within the typical range for AIR applications (5-10%). The 500 boxes themselves did not seem to add any CPU usage, as expected.

Then we loaded the stateless skin's CSS file and saw identical CPU usage. Again the behavior was what we expected -- skinning does not add any extra CPU usage. Since there is no activity or animation on our check boxes, having a skin loaded should not require ongoing computation. So far so good.

Finally, we launched the application loading the stateful CSS and this time our CPU usage was a staggering 50% and it stayed there. We ran the profiler to see what was going on. A few seconds after the application started we saw 377 EnterFrameEvents, which is quite a few but not drastically more than other calls in the application. 40 seconds later the count was 5190. Clearly something in the stateful skinning was causing our application to redraw continuously.

Now that we had established that the stateful skinning was the problem we wondered whether it was something about our SWC itself or if it was something about each individual skin. To test this we ran the application with different numbers of checkboxes. Running with 100 boxes we saw a CPU usage of 19% -- twice that of a normal application, but still relatively low. 250 boxes used 30% CPU, and, as noted above, 500 boxes used 50%. This seems to indicate that there is some error in each skin. We didn't see our checkboxes animating through states, so the state separation seems to be working correctly. There is something in each state's generated skin that doesn't stop playing once its drawn.

This discovery explains why a Google search doesn't turn up a myriad of complaints about Adobe's new recommended workflow -- the average application probably doesn't have hundreds of skinned components instantiated at once. Many applications probably have 100 or fewer, meaning that their increased CPU usage may not have set off any red flags. Furthermore, many applications include running animations or additional processing which would take the blame for increased CPU usage. No matter the reason, it appears that this bug has largely gone unnoticed but can have devastating effects on large applications. Hopefully Adobe will have a fix out quickly, otherwise it will be back to stateless skinning misery for us.

Read more about stateful skinning from Patrick Hansen, one of the designers at EffectiveUI who worked on this project with me.

6 comments:

Eric A. Gravel said...

Interesting post.

Unfortunately, something like this won't help me push our team, which is primarly Java focused, to consider implementing a very large application in Flex.

I've been pushing it since I think it could meet our no more than 1 second screen refresh/transition requirements and ease of development, and such.

I'm starting to wonder if their concerns are justified. How large is your application? Do you have any books, articles, and such to guide me in architecting such large applications to be performant and scalable?

One of the things I started doing to allow parallel development is to break the code in module (as I would do in Java/Servlet/JSP) but I'm running in the problem of having to create a TON of namespace. Anyone to avoid this when using packages such as:

com.company.project.events.module1
com.company.project.events.module2
com.company.project.maps.module1
com.company.project.maps.module2
com.company.project.managers.module1
com.company.project.managers.module2
com.company.project.views.module1
com.company.project.views.module2


If I need views from module1 and module2 then I need to create a namespace for both... quite annoying.

Greg said...

Eric,
Flex is a great tool for quickly and easily developing really rich and engaging User Interfaces. Efficiency isn't its strong point, but its getting consistently better. Our application is fairly large -- hundreds if not thousands of DisplayObjects. However, with the stateless skinning method this CPU issue goes away. So it isn't a built in Flex drawback, just a peculiarity in the skinning method.

That being said, there are certainly things that Flex is good for and things that it is not good for. As with any technology, choosing the right fit is imperative. An application that is user focused with a lot of interactions and not a huge amount of processing could be perfect for Flex/AIR. An application where speed is far more important than appearance -- like an application giving second by second information to doctors in life or death situations -- would probably be better suited to C++ or Java. An application that is largely processing and little visual may also be better suited to something other than Flex/AIR since the Flex framework is built with animation in mind (via the Flash player).

An exciting possibility is mixing technologies. Currently there aren't a lot of mature solutions for this. There is Merapi, which is very promising although still in beta. If/when we have the ability to mix technologies you can have a rich, engaging Flex frontend and let C++ or Java do the heavy lifting behind the scenes.

Ryan said...

I posted this on Juan's blog, but figured it'd be helpful here as well :)

You should take a look at the UIMovieClip code. In it you’ll see there’s an enterFrameHandler. Basically in the Flex world, we have this nice contract of invalidation/validation and notification of when my size changes (you call invalidateSize()). However, in the Flash world, this doesn’t exist, so every frame UIMovieClip is asking: “hey, have you changed size?”.

I’ll look at how this process can be improved in the future to better optimize these scenarios where you want lots of these flash components. Part of it is that the Flex Component Kit for Flash just wasn’t made for having so many of them at the same time. For now, I’d say you should monkeypatch UIMovieClip.as. Then, modify the file to either set trackSizeChanges to false or to remove the enterFrameHandler all-together.

Hope that helps,
Ryan

Ryan said...

Yikes, good to be aware of this limitation in stateful skinning. Thanks for the post. Hopefully Adobe will optimize this better in the future.

I've seen weird stuff in graphical skins in general, especially with components that are very wide or tall -- sometimes the extreme edges of the skin seem to fail to draw when it renders. Rolling over and back off sometimes fixes (or causes) it. (The scale9 grid seems to be correct, but who knows -- it's likely I am doing something wrong).

Anyway, all that to say that programmatic skins seem a lot more optimized and controllable to me. If SWF size/performance are crucial, I think this is the route to go. They are harder to create, though. (Degrafa can help with this, though it does add a little bit of overhead).

A final nitpick: you used the word "proscribe", but I don't think it means, what you think it means. The Dictionary on my Mac says this:

USAGE The verbs prescribe and proscribe do not have the same meaning. Prescribe is a much more common word than proscribe and means either ‘issue a medical prescription’ or ‘recommend with authority’: : the doctor prescribed antibiotics. Proscribe, on the other hand, is a formal word meaning ‘condemn or forbid’: : gambling was strictly proscribed by the authorities.

Greg said...

Thanks for the word meaning check, Ryan. I fixed it.

Guillaume Malartre said...

Great post, I went deeper on the subject and tried to fix part of the problem.
http://gmalartre.blogspot.com/2009/03/usign-uimovieclip-as-skin-huge-memory.html