遇到性能优化的问题,大抵都会从两个方向入手:行业标准优化手段、实际性能瓶颈问题;既能够从先进的模式、方法、套路吸收借鉴,又能够结合实际性能情况设计应对方案,已然是高质量工作的路径了,但是,路上还充斥着各种判断和选择,稍有不慎还是会泥足深陷,所以今天我们就从渲染视角,来深入解析性能优化这一难题
渲染视角
众所周知,HTML 和 CSS 定义了渲染的说法过于粗糙,精确的表述应该是: HTML 和 CSS 定义了渲染的内容,而 JavaScript 可以干预内容和渲染过程;没有 JavaScript 的干预,HTML 和 CSS 定义的渲染就是静态 HTML 页面的渲染,在 DHTML 和 XSLT 销声匿迹后,动态渲染更多是由 JavaScript 完成的,这体现了解耦后各司其职的优势,同时,把动态渲染能力从简单的 API 调用提升到编程语言的复杂逻辑控制,释放无限的可能性
综上所述,我把渲染视角拆分为:
- 渲染内容
- 渲染过程
接下来我会从这两个部分进行阐释
渲染内容
首先,WWW 的出现将人类从信息孤岛推向了互联互通的万维网时代,而信息的载体就是 Web 上的 HTML 俗称:超文本标记语言,彼时,将 HTML 渲染出来的核心是排版引擎,Web 标准也是围绕着排版展开的
随着技术发展,人们对于排版出来的静态内容逐渐疲劳,DHTML 和 XHTML(XML + XSLT)等技术和高级的 API 如
- HTTPWebRequest ……带来了动态能力
- Flash、Silverlight、JavaAplate ……等技术带来了富客户端能力
- 之后随着 Web 2.0 去中心化打破门户的垄断,整个互联网产业带来了空前的繁荣
随着行业的发展和技术的进步,渲染内容从最初的“文档排版”承载简单信息,到“富媒体”承载多媒体信息,再到今天的增强现实技术 WebXR 承载复杂的数字和现实混合信息,渲染内容对 渲染引擎、显示能力、硬件加速能力都提出了不同的要求
最简单的说,任何一个引擎都会把 Animation 相关 API 单独拎出来,从而区分这种高负载的渲染工作,在框架和底层引擎上进行特殊优化
此外,在显示能力上渲染内容的不同也会产生差异,最常见是分辨率、HDR……等内容对显示能力有特殊的要求;硬件加速则更容易理解,针对不同负载的渲染工作,首先降低 CPU 、内存、磁盘 I/O 的压力,其次是更多用 GPU 、DSP 等专业电子电路进行替代,从而达到更高的性能/功耗比
渲染过程
从成像原理上说,渲染过程包括:CPU 计算(UI 引擎或浏览器引擎、渲染引擎的工作)、图形渲染 API (OpenGL/Metal)、GPU 驱动、GPU渲染、VSYNC 信号发射,HSYNC 信号发射的过程
常见的渲染问题有: 卡顿、撕裂、掉帧……,卡顿、撕裂、掉帧通常都是渲染时间过长造成,渲染时间问题大多数情况下是耗费在 CPU 计算上,部分情况下是耗费在图形渲染上,怎么理解呢?
其实很简单,把一个渲染性能很差的复杂页面用高端机流畅渲染过程录屏,再拿个低端机播放录屏的视频并在同一个手机上打开页面让浏览器进行渲染,图像复杂度没有差异渲染性能上视频播放却比页面渲染快很多,这就是在展示 CPU、GPU 计算和图形渲染的耗时,因为视频的解码和渲染相较浏览器引擎更加简单且渲染过程更短(特殊编解码格式和高码率的视频除外)
因此,从渲染过程来看,性能优化的本质在首先于降低 CPU、GPU 计算负载;其次,如果有条件(实现差异对业务有影响需要说服业务方)通过不同的渲染内容构建方法去影响渲染过程的话,优先选择有 CPU、GPU 优化指令和专用电子电路加速的底层 API 来构建渲染内容;例如在 H.264 硬件加速普及的今天,是否应该用 X.265 / H.265 就值得商榷
在探讨渲染过程的时候,流畅性指标是首先需要关注的,根据 60Hz 刷新率下 16.6ms 的帧渲染速度,可以从时间角度定义 16.6ms x 2 (双缓冲)、16.6ms x 3(三缓冲)的 CPU 和 GPU 处理时间,压缩渲染过程来保证流畅度,接下来我们来详细说说 CPU 和 GPU
CPU 和 GPU
在屏幕成像的过程中,CPU和GPU起着至关重要的作用
CPU(Central Processing Unit,中央处理器)
- 对象的创建和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制(Core Graphics)
GPU(Graphics Processing Unit,图形处理器)
- 纹理的渲染
渲染卡顿解决的主要思路:
- 尽可能减少 CPU、GPU 资源消耗
- 尽量用轻量级的对象,比如用不到事件处理的地方,可以考虑使用CALayer取代UIView
- 不要频繁地调用UIView的相关属性,比如frame、bounds、transform等属性,尽量减少不必要的修改
- 尽量提前计算好布局,在有需要时一次性调整对应的属性,不要多次修改属性
- Autolayout会比直接设置frame消耗更多的CPU资源
- 图片的size最好刚好跟UIImageView的size保持一致
- 控制一下线程的最大并发数量
- 尽量把耗时的操作放到子线程
- 文本处理(尺寸计算、绘制)
- 图片处理(解码、绘制)
- 尽量避免短时间内大量图片的显示,尽可能将多张图片合成一张进行显示
- GPU 能处理的最大纹理尺寸是 4096x4096,一旦超过这个尺寸,就会占用 CPU 资源进行处理,所以纹理尽量不要超过这个尺寸
- 尽量减少视图数量和层次
- 减少透明的视图(alpha<1),不透明的就设置 opaque 为 YES
- 尽量避免出现离屏渲染
离屏渲染
在 OpenGL 中,GPU 有2种渲染方式
- On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
- Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作
离屏渲染消耗性能的原因
- 需要创建新的缓冲区
- 离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕
哪些操作会触发离屏渲染?
- 光栅化,layer.shouldRasterize = YES
- 遮罩,layer.mask
- 圆角,同时设置layer.masksToBounds = YES、layer.cornerRadius大于0
- 考虑通过CoreGraphics绘制裁剪圆角,或者叫美工提供圆角图片
- 阴影,layer.shadowXXX
- 如果设置了layer.shadowPath就不会产生离屏渲染
总结
如若遇到性能优化相关的系列问题怎么做?
四个字:防微杜渐;很多性能方面的问题都不是一朝一夕产生的,因为在开发的过程中,是需要不断的提升代码的质量,所以开发人员就必须要提高自己的开发水平,发现了问题,就要及时的解决
今天有关于 性能优化 中的 渲染机制的阐述就到这里了;为了帮助大家了解更多 性能优化 必备的技术知识,这里特别提供一份由腾讯大佬所整理的一张 Android 性能优化 思维导图及其配套的一份学习手册;有需要这份思维导图及学习手册 的朋友: 可以简信发送 “架构图” 或 “进阶” 即可 直达获取;希望大家看完之后,能够早日攻克性能优化这一难题
内容展示如下:
Android 性能优化思维导图
高清版 Android 性能优化 思维导图 获取方式:简信发送 “架构图” 即可 直达获取
UI 布局优化
- 优化思路
- 优化方案
- 选择合适的布局类型
- 尽可能少用 wrap_content
- 用 SurfaceView 或 TextureView 代替普通 View
- 使用 RenderJavascript
- 使用 OpenGL 绘图
- 布局调优工具
卡顿优化和布局优化
- 卡顿分析
- 刷新率
- PerfDog
- CPU Profile
- 布局优化
- 过度绘制
- 解决过度绘制
- 层级优化
- 使用 merge
- ViewStub
- 不要在 onDraw 里创建对象
- 异步加载布局
完整版 Android 性能优化 思维导图及学习手册 获取方式: 简信发送 “架构图” 或 "进阶" 即可 直达获取
崩溃优化
- 崩溃的收集
- ANR
- 应用退出
- 崩溃处理
- 崩溃现场
- 崩溃分析
- 系统崩溃
网络优化
- 网络指标
- 用户体验
- 网络监控
- Network Monitor
- Charles 抓包工具
- 优化 DNS 解析
- 连接池复用
- 数据压缩
- 弱网优化
- 网络安全
APK 瘦身方案
- 瘦身原因
- APK 组成
- 代码瘦身
- 代码混淆
- 三方库处理
- 移除无用代码
- 资源瘦身
- 冗余资源
- 图片处理
- 资源混淆
- SO 瘦身
- SO 移除
- 动态加载 SO
有需要这份 Android 性能优化 思维导图及学习手册的朋友:可以 简信发送 “架构图” 或 ”进阶“ 即可 直达获取
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命
Android 架构师之路还很漫长,与大家一同共勉
网友评论