Your App’s Not As Fast As You Think
It was all going so well. Until suddenly it wasn’t.
I have an app that started as a experiment. A toy. It eventually evolved into a full fledged game. Because it started this way it was built using things I know well. Specifically CALayers and other assorted Core Animation goodies. Despite reading the occasional warning that I should be using OpenGL ES or one of the many 2D game engines, I pressed on. After all, periodic spot checking of performance revealed no issues. In fact the frame rate was great and I appeared to have plenty of headroom. Plenty.
Actually, I did at one point decide to create a small test app just to see how OpenGL might perform in comparison and was surprised to discover that it seemed to require more CPU. Seeing as I was fairly ignorant about OpenGL I chalked this up to Apple doing clever things to somehow optimize CA in certain situations. “Lucky me!”, I thought!
So I pressed on. At the start of development my primary phone was an iPhone 4S. Later I upgraded to a 5s, but continued to test on my old phone and all seemed well. Then I tried it on my son’s iPhone 4. That’s when the trouble started. The game, which was buttery smooth on my device, was unplayable on the 4. Granted the iPhone 4S offered a big boost in performance over the iPhone 4, but was it really that big of a difference?!
I fired up Instruments to get to the bottom of the problem and Instruments told me… everything is fine. CPU usage was maybe 30-40%. Graphics device utilization fine. Everything’s awesome! And yet clearly it wasn’t. What the?
On a hunch I decided to use the Activity Monitor in Instruments to look at CPU usage for all processes and discovered that backboardd was using ~70% of the CPU.
So what the hell is backboardd and why does it hate me?!
Google says backboardd is primarily responsible for event handling in iOS but it’s also seemingly responsible for doing much of the heavy lifting required by Core Animation. Remember back when I mentioned how my OpenGL experiment seemed to indicate it was slower than Core Animation? Well that’s because I was only looking at half of the picture.
So at this point I have a big problem. I suppose I could somehow limit support to iPhone 4S or newer and it would appear to run fine, but I’d be releasing an app that I now know to be fantastically inefficient. Or I can completely replace my rendering engine (such as it is). I decided to break down and learn Open GL ES. I could have also looked at Cocos2D or Unity but I’ve had “Learn OpenGL” on my todo list for far too long and I really want to understand what’s going on at the lowest levels. Mmm, shaders.
This was all not quite three weeks ago. At that time, on my iPhone 5s, the most complex level was using roughly 35% CPU while backboardd used 70%. GPU device utilization was around 10%. Since then I’ve learned enough OpenGL to be dangerous and replaced most of the Core Animation with my own rendering engine. I’m not quite finished and there is still tuning to do, but now I’m seeing about 40% CPU usage and 20% GPU utilization. Using more of the GPU is actually a good thing since I’m offloading work from the CPU that the GPU can perform more efficiently. Meanwhile backboardd has dropped to about 15%. Yeah there are still some lingering CALayers laying about that I’m not easily able to rid myself of yet.
So the takeaways from all this:
- Really, seriously, spot check performance on all the devices you plan to support and don’t assume. My assumptions were based on an incomplete picture of performance data.
- Keep an eye on CPU usage of all processes when running performance tests. With only one app visible at a time it’s easy to forget that your app doesn’t have the whole place to itself and needs to play well with others.
- Pay closer attention to those that have gone before you. I read many times how Open GL or a purpose built 2D rendering engine was the way to go but shrugged it off. I should have tried harder to discover why my results seemed so much better than expected.
- Open GL ES and Xcode’s Open GL analysis is pretty nice. I wish there were a Core Animation counterpart.
- Don’t write a game next time.
Why would WebGL run slower between machines if OpenGL shows a slight improvement?