浏览器渲染,CPU和I/O瓶颈

321 阅读5分钟

浏览器渲染

渲染相关任务:

  • DOM:将HTML解析为DOM树,开发者可以在浏览器控制台输入document感知到它的存在。

  • Style:解析CSS,开发者可以在浏览器控制台输入document.styleSheets感知它的存在。

  • Layout:构建布局树,布局树会移除DOM树中不可见的部分,并计算可见部分的几何位置。

  • Layer:将页面划分为多个图层,一些层叠上下文CSS属性(如:z-index、opacity、position)。

  • Paint:为每一个图层生成包含“绘制信息”的绘制列表,将绘制列表提交给渲染进程的合成进程用于绘制。

执行上述任务的流程被称为渲染流水线。每次执行流水线时,上述所有任务并不一定会全部执行,比如:

  • 当通过JS或CSS修改DOM元素的几何属性(比如长度、宽度)时,会触发完整的渲染流水线,这种情况称为重排。
  • 当修改的属性不涉及几何属性(比如字体颜色)时,会省略流水线中的Layout和Layer过程,这种情况称为重绘。
  • 当修改不涉及重排、重绘的属性(比如transform属性)时,会省略流水线中的Layout、Layer、Paint过程,仅执行合成线程的绘制工作,这种情况称为合成。

​ 从上面描述不难看出来性能由高到低排序为:合成 > 重绘 > 重排。这也就是CSS动画性能优于JS动画性能的原因,前者仅涉及合成,而后者会设计重排、重绘。

​ 绘制的出来的是一张图片,这张图片发送给显卡然后显示在屏幕上。假设屏幕的刷新率为60Hz,即每秒刷新60次,当屏幕的刷新率和显卡的更新频率一致时就不会感到卡顿。然而JS的执行和渲染流水线都是宏任务,如果JS执行时间过长,就会导致流水线绘制图片的速度跟不上屏幕刷新的频率,这就是造成卡顿的原因,换言之就是“CPU不够用”。


CPU瓶颈

原因:

​ 在React中造成“CPU不够用”的原因很大一部分是关于VDOM的相关工作,例如一个列表,其中有很多li元素,那么就需要较长时间去创建对应数量的VDOM,这个时间肯定会大于16.6ms,也就是一帧的时间,这样就会造成卡顿。

解决方法:

​ 不同的框架在该问题的上的解决方法是不相同的,Vue3是通过利用AOT在编译时减少运行时代码流程,而React作为“重运行时”的框架,其解决方法同样在“运行时”进行解决,做法就是:将一个很长的宏任务可能会导致掉帧卡顿的,分解成多个独立的宏任务,将每个宏任务的执行时间限制在一定范围内(初始一般为5ms),这样做就会降低掉帧的可能,这一技术就是Time Slice(时间切片)。

​ 下面是同步更新、采用Time Slice、采用Debounced、采用Throttle情况下掉帧的情况及调用栈火焰图动图。

同步更新

tongbu.gif

Time Slice

timeslice.gif

Debounced

debounce.gif

Throttle

throttle.gif


I/O瓶颈

出现瓶颈的原因

​ 在前端框架开发中,最为主要的I/O瓶颈为网络的延迟,因为在前端框架中是不是需要向服务器请求或者上传一些数据,请求下来的数据又要进行实时的更新,如果网络的延迟较高,那么可能就会导致卡顿。因为网络延迟是客观存在的,不可能降为0,所以减少网络延迟对拥护的影响呢?React的做法是借鉴的人机交互的成果,将其整合到UI中。

解决方法

​ 换言之就是用户对不同的时间的敏感程度是不同的,例如用户相对一个输入框进行输入,那么用户对”鼠标的悬停“和“键盘的输入到输入框的显示”是十分敏感的,只要存在一点延迟用户就会觉得卡顿,而点击提交按钮时,从“点击按钮”到“显示数据”之间即使存在较长的加载时间,用户并不会感觉到卡顿。

​ 有了这个了解那么就可以来解决这个I/O瓶颈。具体包含的要点有:

  • 为不同的操作造成的“自变量变化”赋予不同的优先级;
  • 所有优先级统一调度,优先处理最高优先级的更新;
  • 如果更新正在进行(即进入VDOM相关工作),有更高优先级的更新产生,则会中断当前更新,优先处理高优先级的更新。

​ 为了实现这三个点,React需要实现:

  • 用于调度优先级的调度器(Scheduler);
  • 调度的算法;
  • VDOM支持可中断。

这个处理让我想到的是《计算机组成原理》里的中断优先级相关操作。

如何判断哪个优先级较高,在React的位运算中会有,打上不同的flags,通过位运算判断优先级。


总结

​ 上述两个瓶颈其实之间有一定的关系,当一个VDOM工作来临的时候,将其拆分成多给短的宏任务,当上一个宏任务结束时,检查当前执行上下文中的微任务队列并且执行,之后在判断是否应该中断。所以不管是解决“CPU瓶颈”还是“I/O瓶颈”其底层的述求还是需要去实现Time Slice。