为啥 -drawRect 比为 UITableViews 使用 CALayers/UIViews 更快?
Posted
技术标签:
【中文标题】为啥 -drawRect 比为 UITableViews 使用 CALayers/UIViews 更快?【英文标题】:Why is -drawRect faster than using CALayers/UIViews for UITableViews?为什么 -drawRect 比为 UITableViews 使用 CALayers/UIViews 更快? 【发布时间】:2012-08-10 21:18:11 【问题描述】:我已经能听到一千名 ios 开发者的痛苦内心。
不,我不是菜鸟。
为什么 -drawRect 比拥有多个视图的 UITableView 性能更快?
我了解合成操作在 GPU 上进行。但是合成是一次性的操作;一旦层被提交到内存,它与缓存缓冲区没有什么不同,从 GPU 的角度来看,它会被转换进出视图。将此与在 drawRect 中使用 Core Graphics 进行比较,后者在 CPU 上使用未知数量的操作来生成最终被缓存在 CALayers 中的像素。如果最终都被缓存和展平,有什么区别?
此外,如果您正确处理单元重用,则无需在每次调用 -cellForRowAtIndexPath 时重新生成视图。事实上,通过 UIView/CALayer 对象缓存状态数据(字体、字体大小、文本颜色、属性等)可能比在 -drawRect 期间不断重新创建它们有性能优势。
为什么对 drawRect 如此***?谁能给我指点?
【问题讨论】:
【参考方案1】:当您谈论优化时,您需要提供具体的情况、条件和限制。因为优化都是关于微观管理的。否则就没有意义了。
你的更快的基础是什么?你是怎么测量的?数字是多少?
例如,无操作或非常简单的-drawRect:
可以更快,但这并不意味着它总是如此。
我也不知道 CA 的内部设计。以下是我的猜测。
如果是静态内容
您的绘图代码不断被调用,这很奇怪。因为CALayer
缓存了绘制结果,直到你发送setNeedsDisplay
消息才会再次绘制。如果您不更新单元格的内容,则与单个位图图层相同。应该比多个合成层更快,因为它不需要合成成本。如果您只使用少量足以同时存在于池中的单元,则无需更新。随着最近型号的 RAM 变得更大,它更有可能在最近的型号中发生。
如果是动态内容
如果它不断更新,则意味着您实际上是在自己更新它们。因此,也许您的图层合成版本也会不断更新。这意味着它正在为每一帧再次合成。它可能会因复杂和庞大而变慢。如果它复杂且大并且有很多重叠区域,它可能会更慢。我猜如果 CA 无法确定可以忽略哪些区域,它会严格绘制所有内容。不像你可以选择画什么或不画什么。
如果实际绘图是在 CPU 中完成的
即使您将视图配置为多个层的纯组合,最终也应该绘制每个子层。并且不能保证在 GPU 中绘制它们的内容。例如,我相信CATextLayer
正在 CPU 中绘制自己。 (因为在当前的移动 GPU 上用多边形绘制文本在性能方面没有意义)还有一些过滤效果。在这种情况下,总成本将相似,而且还需要合成成本。
在CPU和GPU负载均衡的情况下
如果您的 GPU 因层数过多或直接 OpenGL 绘图而非常忙于繁重的负载,则您的 CPU 可能处于空闲状态。如果您的 CG 绘图可以在空闲 CPU 时间内完成,那么它可能比给 GPU 提供更多负载要快。
这些都不是你的情况?
如果您的情况不是我上面列出的情况,我真的很想看看和检查 CG 代码绘制速度比 CA 合成快。我希望你附上一些源代码。
【讨论】:
【参考方案2】:好吧,如果在基于 GPU 的渲染器和基于 CPU 的渲染器之间来回切换,您的程序很容易最终会移动和转换大量像素数据。
同样,很多层会消耗大量内存。
【讨论】:
【参考方案3】:我在这里只看到了一半的对话,所以我可能误解了。根据我最近优化 CALayer 渲染的经验,以及调查 Apple 确实(不)优化您希望优化的内容的方式...
如果最终全部缓存并展平,有什么区别?
Apple 最终为每层创建了一个单独的 GPU 元素。如果你有很多层,你就有很多 GPU 元素。如果你有一个drawRect,你就只有一个元素。 Apple 通常不会将这些扁平化,即使它们可以(并且可能“应该”)扁平化。
在许多情况下,“大量元素”不是问题。但是如果它们变得很大......或者它们足够多......或者它们对于 OpenGL 来说是不好的尺寸......并且(见下文)它们被存储在 CPU 中而不是 GPU 上,那么事情就会开始变得令人讨厌.注意:根据我的经验:
“足够”:内存中超过 40 个 “大”:100x100 点(200x200 视网膜像素)Apple 的 GPU 元素/缓冲区代码在大多数地方都得到了很好的优化,但在少数地方却很糟糕。性能下降就像跳崖一样。
此外,如果您正确处理单元重用,则不需要 在每次调用 -cellForRowAtIndexPath
时重新生成视图
您说“正确”,除了... IIRC Apple 的文档告诉人们不要那样做,他们采用更简单的方法(恕我直言:弱文档),而是在每次通话时重新填充所有子视图。到什么时候……你节省了多少?
最后:
...这一切在 iOS 6 中不是都发生了变化,创建 UIView 的成本大大降低了吗? (我还没有分析它,只是从其他开发者那里听说过)
【讨论】:
以上是关于为啥 -drawRect 比为 UITableViews 使用 CALayers/UIViews 更快?的主要内容,如果未能解决你的问题,请参考以下文章