使用Xcode Instruments诊断和解决性能问题

我们将创建一个简单的iOS应用程序,该应用程序在表视图中使用名称和头像来显示模拟动物列表。

阿凡达图片存储在我们的应用程序捆绑包中。 为了使应用程序表现得更真实,我们将实时分别加载化身图像,而不是使用imageNamed:方法预加载化身图像。

我们可以假设瓶颈是图像加载。 我们正在从闪存驱动器中实时加载图像,而不是对其进行缓存。 所以这可能就是为什么它很慢吧? 可以使用一些很棒的代码来修复该问题,这些代码将使用GCD异步地推测性加载我们的图像,然后对其进行缓存。

在开始编写代码之前,让我们测试一下我们的假设。 让我们使用仪器工具来分析应用,以识别问题。


首先,我们对工具感兴趣,例如:

  • Time Profiler:用于测量CPU使用率,按方法/功能细分。
  • 核心动画:用于调试各种核心动画性能问题。

时间分析器

Time Profiler是用于监视CPU使用率的工具。 它使我们可以了解应用程序中哪些方法消耗最多的CPU时间。 高CPU使用率并不总是您应该意识到的-您最可能希望动画例程非常占用CPU,因为动画往往是iOS设备上最苛刻的任务之一。

如果您遇到性能问题,那么查看CPU时间是确定性能是否受CPU限制以及哪些方法需要优化的好方法。

核心动画

核心动画工具用于监视核心动画性能。 它给出了定期采样的FPS的细分,并考虑了发生在我们应用程序之外的部分动画。

核心动画工具还提供了许多调试选项,以帮助调试渲染瓶颈。 我们对选项感兴趣,例如:

  • 颜色混合层:此选项突出显示正在发生混合的屏幕的任何区域,根据严重程度从绿色变为红色。 混合不利于GPU性能。 它会导致透支,并且是滚动或动画帧速不佳的常见原因。
  • 颜色 shouldRasterize 绿色, shouldRasterize 红色:使用shouldRasterize属性时,将缓存昂贵的图层图形并将其渲染为单个展平的图像。 当必须重新生成缓存时,此选项以红色突出显示栅格化的图层。 如果频繁重新生成缓存,则表明光栅化可能会对性能产生负面影响。
  • 屏幕外渲染的颜色为黄色:以黄色突出显示需要屏幕外渲染的任何层。 这些层可能是使用诸如shadowPathshouldRasterize优化的候选shouldRasterize

我们的考虑可以归结为这种问题可能与图像加载有关的想法,所以让我们从Time Profiler工具开始。

tableView: cellForRowAtIndexPath:方法(这是我们加载图像)中花费的CPU时间的总百分比仅为6%。 那真的不是那么高。 这可能是一个建议,说明CPU / IO不是此处的限制因素。


让我们看看这是否是GPU问题。 我们将检查GPU利用率。

GPU的平均硬件利用率约为72%。 看起来GPU不得不非常努力地渲染动物列表。


为什么GPU使用率如此之高? 让我们使用“核心动画”工具的调试选项检查屏幕。 首先,启用“ 颜色混合图层”选项。

屏幕上所有的红色表示文本标签上的混合水平很高,这并不奇怪,因为我们必须使背景透明才能应用阴影效果。 这就解释了为什么渲染器利用率如此之高。


屏幕外画呢? 启用“核心动画”工具的“ 屏幕外颜色-黄色”选项。

现在,我们可以观察到所有表格单元格内容都在屏幕外呈现。 我们应用于图像和标签视图的阴影可能会导致这种情况。


让我们通过在代码中注释掉阴影来禁用阴影,看看这是否有助于解决性能问题。

问题解决了。 没有阴影,我们可以流畅滚动。 我们的动物清单现在看起来不像以前那么辛辣。 因此,这里的问题是–我们如何保持图层阴影并仍然具有良好的性能?


好吧,每一行的文本和头像都不需要更改每一帧,因此UITableViewCell层似乎是缓存的理想选择。 我们可以使用shouldRasterize属性来缓存图层内容。 一旦离开屏幕,这将渲染该图层,然后保留结果,直到需要更新为止。 让我们尝试应用它。

正如预期的那样,我们看到大多数行都是绿色的,当它们移到屏幕上时只是短暂地闪烁红色。 因此,我们的帧速率现在更加平滑。


综上所述,我们最初的直觉是错误的。 事实证明,图像的加载毕竟不是瓶颈,而花在复杂的多线程加载和缓存实现上的任何工作都将被浪费掉。 很好,我们已经验证了以前的问题的根源,并试图解决它!


谢谢阅读! 我希望这些信息对您有用。 如果您觉得本文有用,请不要忘记to。