Ludicrous Software

Speed Up Device Builds in Image-heavy Apps

I’m currently working on an image-heavy Corona iPad application for a client. And when I say “image-heavy”, I mean extremely image-heavy. The app is about 150MB in size, and about 99.8% of that is PNG images, and most of those images are fullscreen (i.e. 1024 x 768) images. It takes about eight(!) minutes to build the app using the latest public release of the Corona SDK, and I wanted to find a way to speed that up.

(The app is for internal use, so it won’t be released publicly and I can’t share specific images.)

From watching the console output as the app was built, it was clear that the bottleneck is the process of compressing/copying all the images into the app (Corona runs all PNG images through pngcrush to optimize them; this is also what Xcode does). Since copying a file isn’t particularly time-consuming, I assumed that the compression was the problem.

My initial thought was that it would be great if there were a way for Corona to cache any image optimizations that it does, so that it doesn’t have to do them again if the image changes. This would be similar to how Flash Pro will now cache the compressed version it generates from a WAV file during the build process.

I mentioned this to Carlos, and he suggested that I manually optimize the images ahead of time, and that he thought this would cause that step to be skipped during the build process. The optimization is performed by a program called iphoneos-optimize in the iOS SDK, so you’ll need to have the SDK installed. iphoneos-optimize is basically just a wrapper for pngcrush, passing in the optimal settings so you don’t need to do that yourself.

The major downside of this approach is that it renders your images unusable on the desktop - you can get some technical background on how the images are optimized in this blog post, but the short version is that an optimized PNG isn’t a ‘real’ PNG, so it looks like a corrupt file. If you optimize the images yourself and then try to test your app in the Corona Simulator, you’ll get a lot of “incorrect header check” error messages, and your images won’t show up.

So, this means that you’ll need to export your project to a separate folder so that you don’t destroy your original images. Here’s the basic process:

  1. Export your project to a separate folder; if you’re using git, then git checkout-index -a -f --prefix=/path/to/target/folder/, when run from the root folder of your project, is an easy way to do this.
  2. Open up the Terminal and change to the directory containing the copy of your project (cd /path/to/target/folder/)
  3. Run iphoneos-optimize on the folder. You need to provide the target folder, so just use the dot to pass in the current folder: /Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/iphoneos-optimize .
  4. Open the project in the Corona Simulator. Ignore the fact that none of your images show up.
  5. Build as usual.

So, what are the results for my ginormous image-heavy app?

The regular build process, where Corona handles the optimization, took 8 minutes, 8 seconds, just now.

Manually running the image optimization from the command line took 1 minute, 36 seconds.

Creating the device build with Corona using the pre-optimized images took 42 seconds.

So, clearly, even if you factor in the time spent copying images, etc., pre-optimizing images can save a lot of time during the build process. This is especially true if you’re debugging stuff that can’t be tested in the Simulator anyway (like multitouch), so it doesn’t matter if your app doesn’t ‘work’ in the Simulator.

Also, since these images aren’t going to be changing a lot, I don’t need to run the optimization every time, so things could be even faster if I were to write a shell script to move all my images out of my project folder and replace them with the optimized images. Then I could do the build and then call another script to copy all the images back again so I could have something that does work in the Simulator.

(It would be so nice to be able to automate the whole process from the command line! At least with Android builds you can go into the Simulator resource bundle and tweak the build.xml file. It’s not much, but it’s something. But that’s another blog post.)

So why the huge discrepancy here? I’m not sure why the ‘default’ build takes so long, but my best guess is that iphoneos-optimize can work a little more quickly because it’s doing things in batch mode - it iterates through the entire project just optimizes the images. Based on the output you get when Corona creates a device build, it looks like the build process iterates through every individual file in the project and decides what to do with it. So, images are getting optimized one at a time and then copied into the app. I’m not sure if that’s actually the problem, but that seems to be what’s happening.