移动端浏览器性能优化探索

news2024/11/25 21:41:41

b70b9572c1788018925d8b2da013203c.gif

在移动端的页面开发过程中,我们经常提及页面性能优化、消除页面卡顿的话题,如何·确定优化策略,我们首先应当对页面卡顿的行为有所认知。

dd2a52b88801710981a612e5efdbed75.png

前言



页面的卡顿现象可以比较明确的分为三个类型,分别是 “画面撕裂” 、“丢帧不流畅”、“长时间未响应”。

“画面撕裂“ 现象给人直观的感觉是页面返回内容不一致,而造成这种现象的原因在于屏幕的刷新机制,屏幕的刷新遵循 “Z” 字刷新的方式,因此同一帧的数据在上屏时存在一定的时间差,当帧率大于刷新率时,屏幕对前一帧的数据上屏尚未结束而后台对后一帧的数据处理合成完毕,此时屏幕完成上屏的数据将会采用后一帧的数据,造成人眼同一时刻观测的画面数据来源于不同帧的现象,给人以撕裂感。



”画面丢帧“也被称为 Jank 现象,给人的直观感觉是画面不流畅,在动画或滚动时出现短暂停滞,而造成丢帧的原因在于屏幕每隔 16ms 发出一个 VSync 信号,在正常帧的流程中,CPU 接收到 VSync 信号后会先通过交换指针的方式进行前后缓冲区的数据同步(该过程时耗忽略不计)然后开始新一帧数据合成,当帧率小于刷新率时,下一个 VSync 信号到来时可用于交换的帧数据还没有通过 CPU 计算合成,此时前后缓冲区的数据不会发生交换,因此屏幕内出现了连续两帧使用同一帧数据渲染,给人以停顿和不流畅的感觉。



"长时间未响应"指的是画面长时间等待,等待网络请求或其他事件处理,这种情况通常并不是由于帧渲染导致的,但等待时长如果违背了 RAIL 模型,会严重阻塞(BLOCK)用户行为,该类型的卡顿通常会采用别的指标进行衡量。

a6295a5a7f3479794a2af0ffd7a96c19.png

  1. Response 响应时间:系统应当在 100ms 内对用户的输入作出响应,这种输入表示任何用户的交互行为,比如输入文本、点击、切换表单、开启动画等。在不阻塞用户交互行为的前提下可以对一些耗时昂贵的工作进行预运算,对于耗时 500ms 以上的工作项应当通过信息反馈提前告知用户。

  2. Animation 动画:对于动画的交互,如 touchmove、scroll 等需要在 16ms 内作出响应,而动画的帧率期望能够达到每秒 60 帧,即折算一帧 16.6ms,而实际上渲染一帧的动画过程浏览器还存在着大量的样式计算、布局计算、线程调度、图层合成、光栅化等过程,因此纯 JS 的线程执行耗时应当控制在 10ms 内。

  3. Idle 最大化空闲时间:空闲时间可以用来完成优先级不高的任务,通过 50ms 为基准将延迟任务进行分片分组执行,目的时为了能够预留 50ms 的空闲时间给到主线程来对用户的输入进行即时响应,从而完成 100ms 内响应用户的标准。

  4. Load:1s 内完成站点加载。



上文中提到的很多名词概念,在此也做简单介绍。

  1. 帧率(FPS):指的是一秒内合成帧的数量,常用 FPS 来描述,60FPS 表示一秒内合成 60 帧。

  2. 刷新率(Hz):指的是一秒内屏幕的刷新次数,安卓机通常为 60Hz。

  3. VSync(垂直同步):是一种定时中断技术,时间间隔为 16.6ms,VSync 信号保证了画面内的数据来源同步,磨平了屏幕刷新方式带来的上下屏数据来源的差异性。通常与双缓存、三缓存技术共同解决 ”画面撕裂“ 带来的卡顿。此处介绍一个可用于测试设备是否支持 VSync 的在线工具 设备测试 VSync。

  4. 双缓存机制:通过设计两种类型的缓存器 Back Buffer、Frame Buffer 分别为 CPU 数据处理以及屏幕读取数据服务,解决了帧渲染过程中同时存在的读写逻辑冲突。

  5. 三缓存机制:CPU 与 GPU 在 Back Buffer 的使用权上存在竞争关系,导致 GPU 占用时间段内 CPU 线程处于闲置状态,从而导致合成帧耗时较长,三缓存将 CPU 与 GPU 对缓存器的使用状态也进行了隔离。

c0a12ed241412026ffde25836df9a6ab.png

如何衡量卡顿

  FPS 与卡顿的关系

一般来说,我们通过页面的 FPS 来作为衡量页面卡顿的指标,但是用 FPS 来做卡顿的描述并不精确,举个例子:

  1. 电影的播放的 FPS = 24 ,但是在电影播放过程并不会出现卡顿现象,说明了 FPS 低于 30 也不表示画面卡顿,从定义上来看 FPS 仅描述了一秒内的画面绘制次数,如果一秒内页面没有任何绘制的需求,FPS = 0  是很正常的。

  2. 对应的,FPS = 60 的页面如果在前 200ms 仅仅完成了首帧的渲染而剩余 800ms 完成了剩余 59 帧的渲染,保证了一秒内的帧数 , 但是仍然会带来卡顿的现象。

  新的衡量指标

SM(SMoothes) 是 Android 端提出的相比于 FPS 更加准确的衡量卡顿现象的指标,SM 更好的考虑了单位时长内的有效帧数占比,SM 计算公式如下:

SM = FPS * (单位时长的总帧数 - 单位时长丢帧数) / 单位时长总帧数

9f4ebc2491115ad3c667debfc39dd333.png

浏览器动画渲染



为了能够更好的跟踪动画渲染过程的卡顿,我们应该更加清晰的去认知浏览器渲染的原理和过程,动画渲染的原理大图如下:

a108640a76d06d9d9a527a636dae3db6.png

对上图进行链路总结如下:

  1. CPU 负责处理 复杂的控制逻辑以及数据逻辑,在浏览器的渲染过程中生成 DOM 树、CSSOM 树、样式计算、布局计算、图层生成等,最后将矢量数据提交给合成器以及 GPU。

  2. GPU 接受图层图块信息,并基于强大的硬件能力对图层进行分割切片、栅格化。GPU 从结构上拥有更多的 ALU 单元,更擅长大量重复的计算以及矩阵变换,能够以流式并行的模式快速完成位图绘制,并将纹理数据存入缓存器。

  3. 显示器通过 VSync 信号强制拉齐帧率与刷新率,当信号来临时访问前缓存器拿到最新的位图数据并用于上屏。

  GPU扮演的角色

我们常说的硬件加速其实是通过 GPU 的特性来缓解 CPU 的计算压力,相比于 CPU,GPU拥有更多的 ALU(算数逻辑单元),其特有的流式并行计算模式在矩阵变换的计算上有天然的优势,同时采用 GPU 的硬件加速可以对待处理的元素进行图层提升,实现了在画布更新过程中隔离非合成元素的目的,等到生成结束后通过相应位置的替换完成图层的合成,从而减少了 CPU 线程内回流和重绘的计算过程。



值得注意的是,浏览器本身为我们完成了很多优化的策略,比如在使用 transform、opacity 属性的时候浏览器会自动开启 GPU 加速,因此更推荐在动画过程中通过上述属性进行处理。除此之外,通过 will-change 定义的 CSS 属性值,浏览器也会预先将其关联的元素提升到新的图层,从而避免刷新屏幕时出现回流和重绘的过程。

  合理避免回流和重绘

事实上,是否避免了回流和重绘的过程取决于对应的元素是否真正被提升到了合成层(Composite Layer)。即使使用了 transform、opacity 两个属性,如果浏览器不支持硬件加速导致图层没有被提升,那么在更新的过程中仍然会因此回流和重绘。



在此对常用于提升图层的方法进行罗列:

  1. 3D transforms: translate3d, translateZ 等;

  2. video, canvas, iframe 等元素;

  3. 通过 Element.animate() 实现的 opacity 动画转换;

  4. 通过 СSS 动画实现的 opacity 动画转换;

  5. position: fixed;

  6. will-change;

  7. filter;

  8. 有合成层(Composite Layer)后代同时本身 overflow 不为 visible(如果本身是因为明确的定位因素产生的 SelfPaintingLayer,则需要 z-index 不为 auto)

73c1cb23a1ad6858cfd19262266cae1d.png

浏览器工作流程

到此我们明确了在浏览器动画绘制过程中 CPU 以及 GPU 扮演的角色,为了更加清晰的明确一帧内 CPU 与 GPU 负责的具体工作,我们需要对浏览器内核的工作有一定的了解。



首先,浏览器是多进程工作的,进程结构如下:

f641d77d511df089e8c6005b42010ee5.png

我们常说的浏览器内核其实就是渲染进程(渲染引擎)。渲染引擎将从网络层获取请求的文档内容并解析渲染,整个渲染引擎的工作流程是渐进的,渲染引擎的工作流程如图所示:

f885c6a672db96b2f371392ad8d8e5ca.png

对于其中的每一个部分展开解释有些复杂,本文不多做赘述。

本文还是更加聚焦于画面渲染的 Layout 以及 Painting 过程,从这个角度来看,单帧内的渲染引擎主线程过程的 Pipline 如下图:

bf1819625879dd3f6474df9a80872973.png

引入 GPU 加速计算后的主线程 Pipline 如下图:

33668ee02fb699b3d50e42ee78ceddef.png

对上述 Pipline 的各节点解读如下:

  1. Input event Handlers 代表浏览器的输入事件回调,该事件会被浏览器进程(Brower Process)捕捉,随后浏览器进程会将事件类型(如touchstart)及其坐标发送给渲染进程(Renderer Process),渲染进程通过查找事件目标并运行附加的事件侦听器来处理事件。

  2. requestAnimationFrame允许定义一个回调函数,可提供给用户在当前帧布局计算以及绘制之前做一些预处理操作。

  3. CPU 在 ParseHTML  过程负责将浏览器不能识别的 HTML 文本转换为浏览器能识别的 DOM 对象。

    591e6d66dd293509eb3d343c0b1bccf2.png

  4. CPU 在 RecalcStyles 过程会解析 CSS 文件,依据 CSS 的样式继承以及层叠规则计算 DOM 节点的每一个元素的具体样式,并保存在 ComputedStyle 结构中。

    190d99b8b08012a9fec1e8c84c0f4c89.png

  5. CPU 在 Layout 过程基于 DOM Tree 以及 Compouted Style 计算元素布局信息并生成 Layout Tree(RenderObject), 在该过程中不可见的 DOM 节点会被忽略,例如 head 标签下的全部内容以及 display = none 的 DOM 节点。

    35ae21b24a711d6a8547fa03f2c36260.png

  6. CPU 在 Update Layer Tree 过程负责将相同 z 空间坐标的 Layout Tree(RenderObject) 归并到相同的 Paint Layer(RenderLayer),从而保证页面元素的合成顺序。通常情况下,并不是布局树的每个节点都包含一个图层,如果一个节点没有对应的层,那么这个节点就从属于父节点的图层,但不管怎样,最终每一个节点都会直接或者间接地从属于一个层。

    0c3f160a389b015c933d96599a21a977.png

  7. CPU 在 Paint 过程干了两件事:第一件事是完成绘制(Painting),即生成新生成 / 修改元素的绘制信息,包括图形信息,文本信息。第二件事是完成栅格化(Rasterization),对 Painting 的绘制信息进行消费,对于没有 GPU 加速的情况下,浏览器会通过 CPU 进行软件栅格化,软件渲染器没有使用 GL 将内容纹理块复制到后台缓冲区,而是使用 Skia( 2D 绘图库)的软件光栅化器来执行复制(并执行任何必要的矩阵计算和裁剪)。

  8. CPU 在 Composite 过程将图层信息提交(commit)到 合成线程(Compositor Thread) 中,在此线程中会对图层进行分块生成图块(图块是栅格操作的单位)同时借助 GPU 对额外的属性(如:will-change 声明的元素,启用硬件加速的 canvas)进行处理(图层变换、合成)。

  9. GPU 在 Rasterize 过程对图块纹理进行 栅格化 处理。

  10. 合成线程会在所有图层被 Rasterize 后打包发送到 GPU 进程,此时标志着一帧结束。

  11. 页面的所有信息在 GPU 内被处理后传入双缓存的 Back Buffer 中,下次垂直同步信号到达后互换前后缓存区位置,完成上屏。

到此,可以回答本章节开篇提出的问题,CPU 给 GPU 传递的数据是经过布局、绘制计算的到的矢量图信息,而 GPU 只负责对矢量图进行像素化(光栅化)以及着色后将其存放到帧缓存区,等待下一个 VSync 信号的同步。

0ef9265c72c16f610232ddb57f0aba3f.png

解决方案

回到移动端本身的问题,低端机面临的性能瓶颈问题往往由于其硬件能力不足导致的,如 CPU 性能(主频大小、Cache 容量)、内核数、内存大小、DRAM 大小、是够支持 GPU 加速以及 GPU 核数等。对于同样的页面逻辑,在低端机上执行,往往会带来更高的 CPU占有率从而导致帧数丢失、更高的内存占有率从而带来更多的数据交换损失,而一些不支持 GPU 的机型中,单帧内的动画会依赖 CPU 进行软件渲染,这个过程会大量占用 CPU 主线程从而带来丢帧。



在了解低端机面临的瓶颈问题后,我们应该通过以下策略最大限度的降低性能损耗:

  1. 开启适当的硬件加速(GPU 加速),合理使用 CSS 属性进行动画绘制,如 transform、opacity、filter等。

  2. 采用 will-change 对比较复杂的元素处理进行单独图层的提升(切忌对所有元素使用,过量的图层会加剧 GPU 合成时的功耗)。

  3. 最小化动画的范围,可以有效减少帧数据(位图)的大小,降低 GPU 访问主存的时间损耗。

  4. 使用脱离文档流的方式对元素进行动画,减少回流。

  5. 避免通过父级元素对子元素进行访问,样式的访问链路过程会加剧样式树计算时的时间损耗。

  6. 尽可能少的访问 offsetWidth、 top 等元素样式属性,因为这些属性会引发回流和重绘,如果必须访问时,可以进行数据缓存,同时可以在 rAF 阶段进行访问,因为 rAF 后浏览器会进行布局的 recalculate 。

  7. 对于没有 GPU 加速的机型,可采用降低动画的影响范围,降低纹理的尺寸的方式等加速主线程,从而提升页面响应速度。

  8. 长任务切片,将连续串行的任务通过优先级设置和任务调度的方式切割,从而减少长任务占用主线程时无法及时对用户行为作出响应。

1fa1976d6975c2a52e7753d9d8bcf18d.png

团队介绍

我们是大淘宝技术营销与平台策略技术团队,是大淘宝技术的核心部门之一,负责手机淘宝核心用户产品——搜索、拍立淘,并且支撑双11、618等大型活动,聚划算、百亿补贴、天天特卖、淘宝好价等营销产品。这里有基于商品的搜索引擎建设对搜索与终端智能业务的探索,还有招商、选品、搭建、投放的活动支撑链路建设以及优惠券、跨店满减、直降、会员卡等多种营销工具的创新和沉淀。我们的目标是以平台化、数据化、智能算法等方式支撑大淘宝为主的集团核心平台营销场景,全力为淘宝天猫打造有乐趣的购物体验,为数亿用户提供优质服务。

¤ 拓展阅读 ¤

3DXR技术 | 终端技术 | 音视频技术

服务端技术 | 技术质量 | 数据算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/566249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

让你不再好奇怎么给小说配音

你是否曾经想象过,当你在读小说时,你可以听到人物的声音,感受到情感和气氛的变化?有声书的出现已经让这一切成为可能。然而,如何为小说创造生动的配音效果却是一个需要仔细考虑的问题。如果你还不知道怎么给小说配音的…

酷开会员丨版权时代,酷开科技打造更多优质内容服务消费者

以版权产业为核心的文化产业,需要重视版权、鼓励创新,才能形成文化创新的环境与氛围,这也是版权时代的发展趋势。在版权时代,付费观看是基本意识,比如电视内容供应方提供了大量免费的资源,观众为观看更精良…

数据结构之二叉树的基本实现

在我们之前已经了解的堆这样的完全二叉树的实现,也对树型结构有了一些了解,那么今天我们来看看二叉树的一些性质。 因为二叉树是一种每个节点至多只有两个子树(即二叉树的每个节点的度不大于2),并且二叉树的子树有左右…

二、Django REST Framework (DRF)序列化反序列化数据校验

参考: DRF 官方文档: Serializers - Django REST framework中文站点 为什么要学DRF和什么是REST API | 大江狗的博客 上一章: 一、Django REST Framework (DRF)& RESTful 风格api_做测试的喵酱的博客-CSDN博客 下一章:…

【C++】类和对象(中上):类的六个默认成员函数——构造函数、析构函数、拷贝构造函数!

目录 前言: 一、类的默认成员函数: 二、构造函数: 1.特性: 构造函数调用规则: 1.无参数的构造函数(默认构造函数): 2.带参数的构造函数: 3.全缺省的构造函数&…

Qt编写视频监控系统75-计算实时码率并显示

一、前言 做监控摄像头的实时视频显示,一般还会要求统计实时码率显示在通道画面上,一个是为了测试下整个软件的性能,同时也看下当前到底是主码流还是子码流,设备到底是不是真的按照设定的码流大小来传输视频数据的。视频码率就是…

【Mysql】 数据类型

文章目录 【Mysql】 数据类型数据类型分类数值类型1. tinyint类型2. bit类型3. 小数类型 字符串类型1.char2.varchar3. 日期和时间类型4. enum 和 set 【Mysql】 数据类型 mysql中数据类型的作用: 约束操作者的行为更清晰的代码逻辑不同的功用 – 例如&#xff0c…

【JavaSE】Java基础语法(八)

文章目录 🍓1. 类和对象🍹🍹1.1 类和对象的关系🍹🍹1.2 类的定义 🍓2. 对象内存图🍹🍹2.1 单个对象内存图🍹🍹2.2 多个对象内存图2.3 多个对象指向相同内存图…

统计学_贾俊平——思考题第9章 分类数据分析

1.简述列联表的构造与列联表的分布。 答:列联表是将两个以上的变量进行交叉分类的频数分布表。 列联表的分布可以从两个方面看,一个是观察值的分布,又称为条件分布,每个具体的观察值就是条件频数;一个是期望…

【数据结构】树的认识

一个人的未来不是预测出来的,而是创造出来的。 -- 亚当詹姆斯目录 🍁前言: 🍀一.什么是树? 🍑二.树有什么用? ❤️1. 数据库 🧡2. 文件系统 &#x1…

chatgpt赋能python:PythonUSB摄像头-拍摄更美好的瞬间

Python USB摄像头 - 拍摄更美好的瞬间 在过去的几年中,摄影已经迅速成为了一种爆炸性的趋势。人们希望能够记录下人生中的美好瞬间,分享给全球的亲朋好友。而USB摄像头的普及与发展使得照片拍摄变得更加便利。而在这其中,Python也扮演了一个…

spingboot+jsp仓储型物流企业车辆运输管理系统

随着时代的进步,物流车辆运输行业也逐渐变得庞大起来。当然,物流车辆运输公司要想做大做强,就有必要有自己完整的一套物流车辆运输管理系统。这必将为物流管理公司提供规范化的管理模式,在各个部门之间有效的协调、合作过程中必将为物流车辆公司提供大量的客户生源,争取赢得最大…

6.4_7关键路径

上一节我们学的叫做AOV网(activity on vertex) 这一节我们是(activity on edge network) 顶点表示事件是一瞬间发生的事情。边上的权值表示完成该活动的开销。 AOE网中,有些事情是可以并行的。 前后活动之间存在依赖关系,我i们要知…

python绘制密度图

本期目录 1、绘图参数2、使用 matplotlib 库绘制密度图时常用的参数3、案例4、 运行结果python绘图往期系列文章目录 1、绘图参数 可以使用多种库来绘制密度图,其中最常用的是 seaborn 和 matplotlib。以下是使用 seaborn 库绘制密度图时常用的参数: i…

简历上,我写精通 JUC 的底气

真的假的,你简历上敢写精通 JUC ? 是真学到精通了,还是说只学到了个皮毛就写精通,从而争取一个面试机会。 我相信,当很多人看到文章标题的第一反应也会如上面的一样,质疑、好奇。这很正常,如果…

手把手教你用Python调用彩云机器翻译API

一、引言 彩云这个小而美的机器翻译一直很低调,它让人眼前一亮的是之前我们分享的网页翻译插件,可以把外文网站翻译成英中对照的样式,便于我们学习。之前我们也写过文章介绍过: PythonFan:如何用Google翻译英文网页成…

c++学习——类和对象

类和对象的基本概念 类是自定义数据类型&#xff0c;是C语言的结构体进化而成的 对象是类实例化出的&#xff0c;用数据类型定义一个变量 #define _CRT_SECURE_NO_WARNINGS #include <iostream> using namespace std;class Maker //这个是类 { public:int a;//成员属性…

PostgreSQL EDB 公司推出新服务,ORACLE 平移到 POSTGRESQL 一体化服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

创建本地LocalHost-SSL证书

mkcert 使用方法 mkcert 是一个开源工具&#xff0c;用于快速生成有效的本地开发证书。它可以帮助开发人员在本地环境中使用 HTTPS 加密来模拟真实的生产环境。 安装 首先&#xff0c;你需要安装 mkcert 工具。以下是在常见操作系统上安装的命令&#xff1a; macOS 使用 Homebr…

集简云数据表无需代码连接抖音的方法

使用场景 抖音作为自媒体时代的主流平台&#xff0c;越来越多的企业选择通过短视频来推广自己的产品或者吸引更多粉丝。那么随时关注抖音视频下的评论&#xff0c;了解用户的想法和需求&#xff0c;并针对不同的评论提供更好的回应是每一位运营的重点工作之一&#xff0c;但是运…