|Home » Learning Curve » Developers Workshop
Those Damned Milliseconds
They all add up.
How many bounces does your Cocoa application take to launch? Five? Six? As many as Apple Mail or Firefox? Do you wait with full initialisation until your first window is on screen or can you get it all in place on time?
Generic user interface guidelines recommend a program should never make the user wait more than two or three seconds - the user must always 'be in control' or at least think that's the case. And even for that time your program is supposed to give the user an indication things are temporarily in a 'wait state'.
Do you think a bouncing dock icon is a good enough indication? Do you think your patient users are satisfied with that? Are you yourself satisfied?
There is one thing above all else that drags on an application launch: disk access. Median disk accesses are measured in milliseconds - units representing 1/1000 of a second. CPU operations are faster by an order of magnitude.
Every disk access your app needs to start involves a lugubrious painful time-consuming process.
- Get the controller to the right cylinder. This is great if the file is contiguous which it often is not - especially with small files. [Apple's built-in defragger doesn't worry about files smaller than 20 KB - which is what most of your image files are.]
- Figure out which tracks on which heads you want. Sweep it in and move to the next extent if any.
What does your application need to load? You should know - you wrote it. First off it's going to want a shitload of Apple system frameworks. You might get away with fewer disk accesses if they're already loaded - the virtual memory system might be able to map code into your address space from images already in memory.
Your own frameworks are another matter. Today every grandma and her friends want Growl and Sparkle linked in and these frameworks are seldom if ever tweaked.
Then there are your own frameworks. And here is where some overly enthusiastic developers really lose it. Try to imagine the startup of a Cocoa application with three dozen or more frameworks of its own - it's painful, isn't it?
If those frameworks represent your own work and if you embed them in a standalone application then you'd best sit down and ask yourself why the F you need so many (or any at all). Why they can't be combined into one or a very few. And so forth.
Then of course there are the 'wee bastards' - your image files. These insidious creatures do more harm than you can imagine.
A single puny 400 byte TIFF file still needs an entire cluster (4096 bytes) read in. Your hard drive controller still has to find it, wait for the right sectors to pass by, and pick it up. And do this for each and every image file in your Cocoa application bundle.
Windows executables are a single file, resources included. Everything is staked out in the file headers. The program loader reads it all, finds it in the file, and loads it accordingly. Apple's NeXTSTEP application architecture will never be like that but there's no reason to let things get fully out of hand.
The Image Library
For the past seven years Rixstep have been using 'image libraries' across the board for all ACP applications. There are no ACP image files on disk save for the application icons (which have to be there). No image is redundant, each image found anywhere is sourced a single time, and all images are located in the same single file.
Loading this image library takes about as long as loading a single 400 byte TIFF file. After that - and no matter how many other ACP applications load it - it takes no time at all. It's memory that won't get dirty. It's mapped into the address space of each successive client. A few nanoseconds wasted - not those damned milliseconds.
Safari 4 has 155 (one hundred fifty five) TIFF images, 291 (two hundred ninety one) PNG images, and 21 (twenty one) JPEG images. That's a shitload of images. That's 467 (four hundred sixty seven) separate disk accesses needed to get Safari 4 on screen and in full working order. That's just plain nuts.
Were the image files found instead in a single image library then Safari 4's launch time speeds up by a whole 466 (four hundred sixty six) of those disk access and disk read ops - and this still doesn't take into account the 413 (four hundred thirteen) image files in WebKit. For all told that's 880 (eight hundred eighty) separate disk accesses / ops needed for what's already a fast launching application, 878 (eight hundred seventy eight) of which are totally unnecessary. Time for a new hard drive?
Getting images out of an image library is eminently straightforward - you use the NSImage method initWithData: and your data is what's returned to you from the image library for the name of the image you want.
Getting images into an image library is not much more difficult. Open your image library as a mutable dictionary, create an NSImage object initiated with the contents of your image file, compress it if it's a TIFF (and most images can be TIFFs) which gives you a pointer to an NSData object, then set it for its name in your dictionary. Then write the dictionary back to disk.
Tweak things even more by compressing your image library afterwards with plutil.
ACP applications launch instantaneously. They're not only 'one bounce' launches - they're on screen and completely initialised before their dock icons even get to the top of their first bounce, much less come back down again. Using image libraries is one of the reasons why.