GPU vs CPU in iOS

96
雨_树
0.6 2018.07.10 17:30 字数 3241

一直以来,我们做产品的时候并没有特别的去考虑CPU/GPU的使用,最近为了提升可视化功能的性能,发现合理使用GPU也是一个可以好好研究的部分,这里总结一下一些有用的信息。

中央处理器 CPU (Central Processing Unit) 是通用型处理器,可以用于处理任何计算,可称为软件层面计算;而图形处理器 GPU (Graphics Processing Unit) 是专用设计来处理图形图像,它基于硬件对高度并行浮点计算做了优化,称为硬件层面计算,所以GPU处理屏幕渲染的任务会更高效。

基础知识

iOS图像处理的框架可以参见下面的架构图:


Core Animation

UIKit

UIKit 是一组 Objective-C API 用于管理图形用户界面,应用程序开发者一般直接使用UIKit来创建各种UI界面应用,因为UIKIt提供了丰富的界面控件比如 UIImage/UIColor/UIButton/UILabel等,使得开发者不需要自己手动创建这些标准控件。

Core Animation

UIKit对于动画的处理是基于Core Animation的:

Core Animation is a graphics rendering and animation infrastructure available on both iOS and OS X that you use to animate the views and other visual elements of your app.

With Core Animation, most of the work required to draw each frame of an animation is done for you. All you have to do is configure a few animation parameters (such as the start and end points) and tell Core Animation to start. Core Animation does the rest, handing most of the actual drawing work off to the onboard graphics hardware to accelerate the rendering. This automatic graphics acceleration results in high frame rates and smooth animations without burdening the CPU and slowing down your app.

Core Animation 也是一套Objective-C API,是iOS平台上负责图形渲染与动画的基础设施,可以实现视图和其他的可视元素的动画,比如提供显示内容的图层类 CALayer、动画和计时类 Animation/Timing等。是一组相对UIKit和动画绘制和动画自由度更大的API,构建于Core Graphics之上。iOS上的UIKit和动画效果大部分都是通过Core Animation实现的,比如UIView可以通过某些属性即可实现的动画。

Core Animation对应的是QuartzCore.framework,参看 QuartzCore.h 可见这个头文件包括 #include <QuartzCore/CoreAnimation.h>,而检查 CoreAnimation.h,可见:

#include <QuartzCore/CABase.h>
#include <QuartzCore/CATransform3D.h>

#import <Foundation/Foundation.h>
#import <QuartzCore/CAAnimation.h>
#import <QuartzCore/CADisplayLink.h>
#import <QuartzCore/CAEAGLLayer.h>
#import <QuartzCore/CAMetalLayer.h>
#import <QuartzCore/CAEmitterCell.h>
#import <QuartzCore/CAEmitterLayer.h>
#import <QuartzCore/CAGradientLayer.h>
#import <QuartzCore/CALayer.h>
#import <QuartzCore/CAMediaTiming.h>
#import <QuartzCore/CAMediaTimingFunction.h>
#import <QuartzCore/CAReplicatorLayer.h>
#import <QuartzCore/CAScrollLayer.h>
#import <QuartzCore/CAShapeLayer.h>
#import <QuartzCore/CATextLayer.h>
#import <QuartzCore/CATiledLayer.h>
#import <QuartzCore/CATransaction.h>
#import <QuartzCore/CATransform3D.h>
#import <QuartzCore/CATransformLayer.h>
#import <QuartzCore/CAValueFunction.h>

另注:

每个视图UIView都包含了一个图层CALayer属性,图层CALayer不处理用户交互,它是真正用于在屏幕上显示和动画,视图UIVIew是对它的封装,并提供了处理交互的功能及CoreAnimation底层方法的公开接口。

iOS基于UIView和CALayer提供两个平行的层级关系做职责分离,这样也能避免很多重复代码。在iOS和Mac OS两个平台上,事件和用户交互有很多的不同,基于多点触控的用户界面和基于鼠标键盘有着本质的区别,这就是为什么iOS有UIKit和UIView,但是Mac OS有AppKit和NSView的原因

Metal

OpenGL ES (Open Graphics Library for Embedded Systems):

Bring amazing graphics to life in your iOS and tvOS apps with the hardware-accelerated OpenGL ES API. The OpenGL ES API is simpler than its desktop counterpart but uses the same key concepts, including programmable shaders and extensions that will make your 3D app or game stand out.

OpenGL ES 是OpenGL的一个简化版本,用于二维/三维数据的可视化,是一种开放标准图形库,提供丰富的图形绘制API,并直接使用底层硬件 (GPU) 处理图形命令,它是 Core Animation 实现的一部分。

Metal 2:

Metal 2 provides near-direct access to the graphics processing unit (GPU), enabling you to maximize the graphics and compute potential of your apps on iOS, macOS, and tvOS. Building on an efficient low-overhead architecture with precompiled shaders, fine-grained resource control, and multithreading support, Metal 2 evolves to give the GPU even greater control of its graphics pipeline, accelerate neural network training, and provide powerful new tools that give deep insight into your shader code.

在iOS图像框架中,我们可以发现 Metal 已经取代了 OpenGL ES,Metal是和OpenGL ES类似的面向底层的图形编程接口,通过相应的API来直接操作GPU。Metal是iOS平台特有的所以有别于 OpenGL ES 的跨平台特性,但是能够更好的利用iOS Device的GPU的运算能力,可以被认为是Apple用来取代 OpenGL ES的一个框架。

Core Graphics

The Core Graphics framework is based on the Quartz advanced drawing engine. It provides low-level, lightweight 2D rendering with unmatched output fidelity. You use this framework to handle path-based drawing, transformations, color management, offscreen rendering, patterns, gradients and shadings, image data management, image creation, and image masking, as well as PDF document creation, display, and parsing.

Core Graphics 是一套 C API,使用CPU进行计算,用于绘制2D图形,类名都以 CG 开头,它定义了颜色、位置、字体、路径、图片等UIKit的常见属性,包括我们平时大量使用的 CGRect, CGPoint, UIFont(CGFont), UIImage(CGImage), CGFont等等,可以说是系统绘制界面、文字、图像等UI的基础。

UIKit库中所有UI组件都是由CoreGraphics绘制实现的,如果需要绘图,可以通过继承UIView并重写drawRect方法来实现。注意的是,Apple建议,如果不需要自定义绘图,就不要使用这个方法,而只需要通过设置视图和图层的属性来完成。

Core Graphics对应的是 CoreGraphics.framework,参看 CoreGraphics.h,可见:

#include <CoreGraphics/CGBase.h>

#include <CoreGraphics/CGAffineTransform.h>
#include <CoreGraphics/CGBitmapContext.h>
#include <CoreGraphics/CGColor.h>
#include <CoreGraphics/CGColorConversionInfo.h>
#include <CoreGraphics/CGColorSpace.h>
#include <CoreGraphics/CGContext.h>
#include <CoreGraphics/CGDataConsumer.h>
#include <CoreGraphics/CGDataProvider.h>
#include <CoreGraphics/CGError.h>
#include <CoreGraphics/CGFont.h>
#include <CoreGraphics/CGFunction.h>
#include <CoreGraphics/CGGeometry.h>
#include <CoreGraphics/CGGradient.h>
#include <CoreGraphics/CGImage.h>
#include <CoreGraphics/CGLayer.h>
#include <CoreGraphics/CGPDFArray.h>
#include <CoreGraphics/CGPDFContentStream.h>
#include <CoreGraphics/CGPDFContext.h>
#include <CoreGraphics/CGPDFDictionary.h>
#include <CoreGraphics/CGPDFDocument.h>
#include <CoreGraphics/CGPDFObject.h>
#include <CoreGraphics/CGPDFOperatorTable.h>
#include <CoreGraphics/CGPDFPage.h>
#include <CoreGraphics/CGPDFScanner.h>
#include <CoreGraphics/CGPDFStream.h>
#include <CoreGraphics/CGPDFString.h>
#include <CoreGraphics/CGPath.h>
#include <CoreGraphics/CGPattern.h>
#include <CoreGraphics/CGShading.h>

另注:

quartz是一个通用的术语,用于描述在iOS和MAC OS X中整个媒体层用到的多种技术,包括图形、动画、音频、适配。

Quart 2D 是一组二维绘图和渲染API,Core Graphic使用到这组API,不单独作为framework存在。

CoreAnimation/QuartCore 是一样的,可看做同义词

GPU vs CPU

iOS Core Animation: Advanced Techniques可知,iOS处理动画的几个阶段:

应用程序内:

布局 Layout 这是准备你的视图/图层的层级关系,以及设置图层属性(位置,背景色,边框等等)的阶段。
显示 Display 这是图层的寄宿图片被绘制的阶段。绘制有可能涉及你的 -drawRect:-drawLayer:inContext: 方法的调用路径。
准备 Prepare 这是Core Animation准备发送动画数据到渲染服务的阶段。这同时也是Core Animation将要执行一些别的事务例如解码动画过程中将要显示的图片的时间点。
提交 Commit 这是最后的阶段,Core Animation打包所有图层和动画属性,然后通过IPC(内部处理通信)发送到渲染服务进行显示。

应用程序外,屏幕显示前(一旦打包的图层和动画到达渲染服务进程,他们会被反序列化来形成另一个叫做渲染树的图层树。使用这个树状结构,渲染服务对动画的每一帧做出如下工作):

计算 Calculates 对所有的图层属性计算中间值,设置OpenGL几何形状(纹理化的三角形)来执行渲染
渲染 Renders 在屏幕上渲染可见的三角形

这六个阶段中,只有最后一个被GPU执行,而前五个阶段都是通过CPU在软件层处理。而我们事实上只能控制布局和显示这两个阶段,其他的是 Core Animation框架的内部处理。

GPU操作

GPU为一个具体的任务做了优化:它用来采集图片和形状(三角形),运行变换,应用纹理和混合然后把它们输送到屏幕上。现代iOS设备上可编程的GPU在这些操作的执行上又很大的灵活性,但是Core Animation并没有暴露出直接的接口。除非你想绕开Core Animation并编写你自己的OpenGL着色器,从根本上解决硬件加速的问题,那么剩下的所有都还是需要在CPU的软件层面上完成。

一般而言,CALayer的属性都是GPU绘制(Draw),包括图层背景、边框颜色、裁剪尺寸等,这些不需要软件层面的绘制。文中提到一些会降低GPU图层绘制的事情包括:太多的几何结构重绘离屏绘制过大的图片

CPU操作

大多数工作在Core Animation的CPU都发生在动画开始之前。这意味着它不会影响到帧率,所以很好,但是他会延迟动画开始的时间,让你的界面看起来会比较迟钝。

由此看来,CPU操作的主要问题是会延迟动画的开始时间,这些操作包括:布局计算(Layout Calculations)、视图懒加载(Lazy View Loading)、Core Graphics绘制(Core Graphics Drawing)、解压图片(Image Decompression)。

应用

再回到写这篇文章的最初的目的:做产品的时候并没有特别的研究CPU/GPU的使用,通过我们的测试发现我们的可视化产品主要的消耗都是CPU,而GPU仅有少部分使用,比如像Map这种类库自身使用了GPU,以及在使用 Core Animation时 CALayer的属性设置会自动使用GPU绘图。

简单的说,在代码中使用 Core Graphics代码的,即以"CG"开头的类,就是使用CPU,而使用了 OpenGL ES/Metal 的,会使用 GPU 处理图形。而我们的可视化应用基本都是使用了 Core Graphics 来实现的。

Core Graphics 在使用的时候会明显慢于使用硬件加速的渲染绘图方式,且会占用大量的内存,相对而言 CALayer只需要少量的内存。并且一旦我们实现了 view-drawRect:方法,或者 CALayerDelegatedrawLayer:inContext:方法,即便什么都不做,Core Animation也会创建占用与view同等大小的Backing Image,导致占用大量的内存:

Because software drawing is so expensive, you should avoid redrawing your view unless absolutely necessary. The secret to improving drawing performance is generally to try to do as little drawing as possible.

所以我们应该平衡CPU和GPU的使用来优化程序:

CA Layers
Core Animation 直接提供了一系列的图层,可参见前面CoreAnimation.h包含的图层,比如'CATextLayer', CAScrollLayer等。这些指定图层会使用硬件加速,并且不需要创建 Backing Image所以内存使用也更加高效。

OpenGL ES/Metal
直接使用 GLKit framework来实现 OpenGL ES的应用,从而使用GPU加速:

GLKView class manages OpenGL ES infrastructure to provide a place for your drawing code

GLKViewController class provides a rendering loop for smooth animation of OpenGL ES content in a GLKit view

CAEAGLLayer layer supports drawing OpenGL content in iOS and tvOS applications.

需要注意的是Apple已经Deprecate OpenGL ES,这就意味着iOS平台并不推荐继续使用OpenGL,取而代之,我们需要使用的Metal来代替OpenGL。

暂时还不知道怎么用,可参见 Framework: Metal

更高级的类库
一般只有做游戏的时候才需要使用:

Higher level game frameworks like SpriteKit, SceneKit, or Unity are built on top of a lower-level 3D graphics API like Metal or OpenGL ES. They provide much of the boilerplate code you normally need to write in a game, such as rendering a sprite or 3D model to the screen

另外需要注意的是,实际的应用中我们需要的是平衡CPU和GPU的使用,而不是一定要倾向于哪一类。并且,在具体的开发中,我们会存在很多设计和实现上的妥协:

  • 比如对于HeatMap,在支持Zooming/Scrolling的情况下,为了能够快速
    完成初次渲染,我们只渲染了当前可见范围内的内容,这种情况导致的一个结果就是用户在做Zooming/Scrolling的时候,我们就需要实时渲染,在这种情况下,CPU渲染和GPU渲染的效果就会相差很大。
  • 另外一种情况是对于NetViz这种,绘图本身就很复杂,因为它包含了大量的曲线函数计算,所以在初次渲染的时候,如果数据量过大,CPU渲染和GPU渲染的效果区别也就会很明显。

引用

Apple Documentation:

Core Animation Programming Guide
Framework: Core Animation
Framework: Core Graphics
Metal 2: Accelerating graphics and much more.
Framework: Metal
OpenGL ES for iOS and tvOS
Framework: OpenGL ES

iOS Core Animation: Advanced Techniques 中文译本 iOS核心动画高级技巧
CPU VS GPU

Metal 系列教程

OpenGL vs. Metal: The Past and Future of iOS Graphics APIs

iOS, Core Animation

🌲の随笔@iOS
Web note ad 1