August 12, 2011
The hardware capabilities of iOS devices have improved dramatically in the two most recent generations, which has helped fuel the growth of increasingly complex apps featuring augmented reality and photorealistic gaming. While such advances are great for the iOS platform, it is important for developers to consider older generations of iPhones and iPod touches that are still a significant portion of the installed base. This post will cover some important perfomance tweaks to improve the user experience on these slower devices.
Jerky scrolling and choppy animations are the most obvious symptoms of a performance problem on older hardware. They are commonly caused by overuse of transparency and alpha blending, which are very expensive CPU and GPU operations. It's also quite possible that an app might be blending many layers under the hood without the developer's knowledge. Using Instruments, we can set various kernel flags to reveal intentional or inadvertent blended views.
Open Instruments and choose the Core Animation template located under iOS / Graphics. First, click in the Core Animation instrument's timeline to reveal the bottom pane and find the section labeled "Debug Options". Check the "Color Blended Layers" box, which will show a red overlay over layers that were blended and a green overlay over layers drawn without blending.
This image shows a screenshot of an app with and without the blended layers overlay enabled.
You'll quickly notice that every UILabel
in the table header and in each cell has
a red overlay. The labels have a clear backgroundColor
, which forces the renderer
to blend the transparent pixels with the cell's own background color to produce the final
image drawn to the screen. Because the cell background is a flat color, we can save this
unnecessary calculation by setting the label to be opaque and the backgroundColor
to the same color as the cell background. The updated labels will draw the exact same pixels
to the screen with reduced processing time.
This speed up occurs for each label we fix, which in this view is 39 total labels (9 in the header and 5 per cell). On a 3rd-generation iPod touch, scrolling performance increased from 18.3 fps to 27.6 fps, a 50.8% improvement.
Another source of unintentional blending is misaligned views that force the renderer to
anti-alias before they are drawn. In this context, "misaligned" means that the requested
display point does not map directly to a screen pixel (for example, it could be between two
pixels), and therefore must be drawn to two neighboring pixels and anti-aliased to give the
illusion that it was drawn "between" the them. This almost always happens when a view's frame
is computed (rather than specified in Interface Builder) because a CGRect
's X
coordinate, Y coordinate, width, and height are CGFloat
s and therefore allow
fractional values. For example, a 100.8px by 50.1px box centered at (200.5, 35.5) is a valid
frame and the OS will try to render it the best it can manage. The additional interpolation
and anti-aliasing overhead required for just a single pixel is enough to severely hurt
performance on older hardware.
Under "Debug Options", check the "Color Misaligned Images" checkbox, which will show a magenta
overlay over layers that are positioned between pixels. This image shows a screenshot of an
app with and without the misaligned images overlay enabled. Each group in the table view has
a custom header that is positioned in code, and it changes based on the width of the label text.
All it takes to fix the misalignment is to call either floorf()
or
ceilf()
(either one will work, just be consistent) on every computed value of a
frame. You can also use CGRectIntegral()
for frames, which always rounds up.
The fixed views will render faster and often appear less blurry. On a 3rd-generation
iPod touch, scrolling performance increased from 32 fps to 43 fps, a modest 34.3%
improvement.
These two techniques allow developers to easily find and remove unnecessary graphics overhead and improve the user experience on older iOS devices. Even on newer devices where the user won't necessarily visually perceive improved performance, a reduction in overhead will improve battery life after extended use.