Comments:"Layer Trees vs. Flat Drawing – Graphics Performance Across iOS Device Generations — Florian Kugler"
This view is 100 points in height and spanned the whole display width on the iPhone as well as on the iPad. It consists of an opaque gradient in the background, a 100x100 image on the left and two labels with a transparent background. I created three versions of this view which all produced an identical output.
The first version was constructed with several subviews. One for the gradient, an UIImageView
for the image and two UILabel
s for the text. The second version was constructed out of several sublayers. A CAGradientLayer
for the background gradient, a CALayer
for the image with its contents
property set to a CGImageRef
and two CATextLayer
s. The last version was drawn as flat texture with Core Graphics.
To test how fast these views could be brought on screen, I measured the time it would take to render 5 to 30 of these views in increments of 5, taking 60 samples during each step. In order to measure how many of these views could be animated at 60 frames per second once they were on screen, I incrementally rendered more and more of these views in my test app until the frame rate dropped below 60fps when animating their position randomly.
Measuring Technique
You can find the test project I used for these benchmarks on GitHub, so I'm only going to describe the the rough outline here. For both measurements I used CADisplayLink
to update the screen. The selector you specify when initializing CADisplayLink
gets called 60 times per second (if what your doing doesn't take too long) and conveniently the display link object, which you get as first and only argument, has a timestamp
property which tells you the timestamp associated with the last frame that was displayed.
To measure how fast new views could be brought on screen I simply removed and re-created the superview which contained a variable number of the test views.
- (void)setupDisplayLink {
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(nextFrame:)];
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)nextFrame:(CADisplayLink*)displayLink {
// ...
[view removeFromSuperview];
view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
for (NSUInteger i = 0; i < numberOfViews; i++) {
// add new views ...
}
[self.view addSubview:view];
}
After 60 iterations with a fixed number of views I logged the average time each cycle took, increased the number of views to be displayed by 5 and moved on to the next round.
To measure how fast the views which were already on screen could be animated, the method called by the display link was a bit different:
- (void)nextFrame:(CADisplayLink*)displayLink {
// ...
view.transform = CGAffineTransformMakeTranslation(self.randomNumber * 50 - 25, self.randomNumber * 50 - 25);
}
This simply sets a different, random translation transform on the container view with each display cycle. Since setting a transform like this doesn't hinder the display link from calling the method 16ms later, even if the resulting GPU operation takes longer, I measured the actual frame rate with the OpenGL ES Driver instrument and averaged the value over a couple of seconds.
Performance of first time rendering
iPhone
The following chart shows the average time it took to render one test view onto the screen on the iPhone 3G, 4, 4S and 5. The three bars in each group represent the different versions of the view: composed of subviews, composed of sublayers and plain Core Graphics drawing.