|< Previous: What are we working with?||Next: Fixing the bugs: slow shadows, leaking UITableViewCells >|
Press Cmd+I to run your app using Instruments, and after a few seconds a window will appear offering you a variety of profiling templates. Please select Time Profiler then click Choose. When the new window appears, click the red circle in the top-left corner to start recording of the app.
Your app will launch on your device (or in the simulator) and Instruments will begin monitoring its usage in realtime. You'll see a spike in Instruments' readings to begin with, which reflects the huge amount of work any app does just to start up. We don't care about that for now, we're more interested in the workload of the app once it's running.
So, scroll around a bit, tap an image, go back, scroll around some more, tap another image, and so on. Aim to get about 10 seconds or so of real app usage, then press the Stop button, which is where the record button was earlier.
The top half of your Instruments window is showing readings from your app; the bottom half is showing details on those readings. By default, the detail view shows everything in the app run that was captured, but I want you to click and drag in the top window to select part of the readings when you tapped on an image. All being well (or as well as can be expected in this broken code!) you should see the readings noticeably spike.
When you select an area of the readings like this, the detail view now shows information just on the part you chose. You should see that the vast majority of the time that was selected was spent on the main thread, which means we're not taking much advantage of having multiple CPU cores.
Immediately to the left of "Main thread" in the detail view is a disclosure arrow. You can click that to open up all the top-level calls made on the Main Thread, which will just be "Start", which in turn has its own calls under its own arrow. You can if you want hold down Alt and click on these arrows, which causes all the children (and their children's children) to be opened up, but that gets messy!
Instead, there are two options. First, you should have a right-hand detail pane with three buttons: record settings, display settings and extended detail (accessible through Cmd+1, Cmd+2 and Cmd+3). Select "Main thread" in the detail view then press Cmd+3 to choose the extended detail view: this will automatically show you the "heaviest" stack trace, which is the code that took the most time to run.
In the picture below you can see the bottom half of Instruments after running a time profile. The pane on the right is showing the heaviest stack trace, and on the left you can see all the threads that were executing as well as what they were doing.
If you scroll down to the bottom, you'll see it's probably all around either in the table view’s
cellForRowAt or the detail view’s
viewDidLoad() depending on whether you spent more of your time flicking through the table view or going into the detail controller.
This heaviest stack trace is the part of your code that took up the most time. That might not always be your problem: sometimes something that runs slowly is just something that's always going to be slow, because it’s doing a lot of work. That said, it's always a good place to start!
The second option is inside the display settings view (Cmd+2) and is called Invert Call Tree. This will show you the heaviest code first, with a disclosure arrow revealing what called it, and so on. Chances are you’ll now see lots of image-related functions:
argb32_sample_argb32() is called by
argb32_image_mark(), which is called by
argb32_imag(), and so on.
There is a third option, but it's of mixed help. It's called "Hide system libraries" and is near to the "Invert Call Tree" checkbox. This eliminates all time being used by Apple's frameworks so you can focus on your own code. This is great if you have lots of complicated code that could be slow, but it doesn't help at all if your app is slow because you're using the system libraries poorly!
Quit Instruments and return to Xcode, then press Cmd+I again to re-run Instruments. This time I'd like you to choose the Core Animation instrument then press record to begin. The Core Animation instrument at the top will show a few FPS (frames per second) to begin with, then settle at 0 – that doesn't mean your app is running at zero frames per second, just that it's not doing anything right now.
Scroll around the table view a little and watch how the Core Animation instrument responds. Ideally you'll get a constant 60fps, but that's only likely on the very latest generations of iPhones – iPhone 8 might be able to come close, but older iPhones have no chance.
Now, although profiling your code is best done on a real device, using the simulator gives us some bonus functionality that will help us identify performance problems. So, try running the app in the simulator now.
Under the Debug menu on your Mac you’ll see a few options. Two in particular are very useful:
Broadly speaking, you want "Color Blended Layers" to show as little red as possible, and "Color Offscreen-Rendered Yellow" to show no yellow.
Try these options on both the table view and the detail view to see how things look.