MotionMark 是 Apple 开发的一个浏览器图形性能测试套件,用来测试和评估 Safari/WebKit 的图形性能,这里的图形性能并不仅仅包括光栅化和合成,而是涵盖了完整的浏览器渲染流水线,包括 DOM API,样式计算,排版等。
MotionMark 虽然不能涵盖浏览器图形性能的所有方面,但是作为衡量浏览器图形性能的其中一项指标,还是有不错的参考价值。Chromium 目前也会使用 MotionMark 分数作为其中一项指标来衡量自身图形性能优化的成果。
关于浏览器渲染请参考我之前的一些文章,比如渲染流水线中的光栅化。
MotionMark 的一个主要特性是自适应动态调整测试的复杂度,从而更好地适应不同的设备,这些设备的性能可能差异很大,比如 PC 的性能通常几倍甚至十倍于移动设备。MotionMark 的每一项测试都包含一个连续的动画,它收集动画每一帧的复杂度和耗时等数据,然后计算出一个分数(复杂度越高,耗时越少,分数就越高),最后根据所有项目的成绩计算出一个总得分,分数越高代表性能越好。
因为 MotionMark 动态调整复杂度的缘故,导致测试的结果波动较大,所以除了分数外,测试还给出一个波动范围,通常需要测试多次,取波动值较小的为准。
这篇文章主要分析 MotionMark 每一项测试的测试内容,在 Chromium 上,该项测试的主要性能瓶颈在哪里,是在 DOM,是在样式计算和排版,还是在光栅化和合成。如果读者对浏览器的图形性能感兴趣,可以通过这篇文章更好地了解如何通过 MotionMark 的成绩来衡量浏览器的图形性能。
用于分析的 Chromium 版本是 Chrome 77 for Android,我们可以看到从 75 开始,Chrome for Android 已经正式开启了 OOPR,普通图层分块光栅化的主要耗时从原来的光栅化线程(CompositorTileWorker)相当大的一部分转移到了 GPU 线程(CrGpuMain),光栅化的耗时在两个线程上都有一定的比例。测试的设备是 Google Pixel 手机。因为不同的设备有可能有不同的性能瓶颈,所以文章的结论不是普适的,只能作为一个参考。
各项测试分析
Multiply
![Multiply](https://i.loli.net/2019/09/23/mMnclvw1I73i56T.png?x-oss-process=image/resize,w_1400/format,webp)
测试 CSS border radius,transform,opacity 动画的性能。测试过程中不断改变一组 div 元素的样式,包括背景色(带半透明),transform 和可见性,div 设置了 border radius 属性用来绘制圆角。
![Multiply](https://i.loli.net/2019/09/23/IkZtTlpNwcBv69r.png?x-oss-process=image/resize,w_1400/format,webp)
在 Chromium 上,主要的性能瓶颈是在 Renderer,耗时最多的部分是 DOM API(改变样式),样式计算,重排版,图层树更新(重新绘制),图层光栅化部分耗时也较多,但是相对而言较前者要少。留意上图 CrRendererMain 和 CrGpuMain 线程上调用耗时的比例。
Canvas Arcs
![Arcs](https://i.loli.net/2019/09/23/71RUKoaM9sgFGkt.png?x-oss-process=image/resize,w_1400/format,webp)
测试 Canvas 路径绘制(弧形路径,包括描边和填充)的性能。
![Arcs](https://i.loli.net/2019/09/23/VBXroxneQpUTO5s.png?x-oss-process=image/resize,w_1400/format,webp)
在 Chromium 上,主要的性能瓶颈是在 Renderer,耗时最多的部分是 DOM API(Canvas API),和 Skia 的路径绘制(Canvas 的光栅化)。
Leaves
![Leaves](https://i.loli.net/2019/09/23/VqGiSdhYt8MW5QF.png?x-oss-process=image/resize,w_1400/format,webp)
测试 CSS transform 动画的性能,大量的较小的 img 元素在动画过程被改变 transform。
![Leaves](https://i.loli.net/2019/09/23/8syuO9mcgqobdLr.png?x-oss-process=image/resize,w_1400/format,webp)
这个测试在 Chromium 上的结果比较吊诡,并不能真实反映 Chromium 在普通页面上 CSS transorm 动画的性能(通常只有少量元素同时做 transform 动画)。测试使用了大量尺寸较小的 img 元素,触发了 Chromium 的图层合并特性,将大量的小图层合并成一个大图层,它的副作用是导致了反复的图层重复光栅化。从上图可以看到,无论是在 Renderer 上的 DOM API(改变样式)和样式计算的耗时,还是图层光栅化的耗时(分布在光栅化线程和 GPU 线程)都不低,两者都可能成为性能瓶颈。
Canvas Paths
![Paths](https://i.loli.net/2019/09/23/WTCq4iGQB7pK1sR.png?x-oss-process=image/resize,w_1400/format,webp)
跟 Canvas Arcs 类似,也是测试 Canvas 路径绘制的性能,只是不是弧形而是各种不同的曲线。
Chromium 的性能瓶颈跟 Canvas Arcs 类似,主要的性能瓶颈是在 Renderer,耗时最多的部分是 DOM API(Canvas API),和 Skia 的路径绘制(Canvas 的光栅化)。
Canvas Lines
![Lines](https://i.loli.net/2019/09/23/6twk2cGuBVUmNrL.png?x-oss-process=image/resize,w_1400/format,webp)
跟 Canvas Arcs/Paths 类似,测试 Canvas 线段绘制的性能。
Chromium 的性能瓶颈跟 Canvas Arcs/Paths 类似,主要的性能瓶颈是在 Renderer,耗时最多的部分是 DOM API(Canvas API),和 Skia 的线段绘制(Canvas 的光栅化)。
Focus
![Focus](https://i.loli.net/2019/09/23/CEmQdktgZP7IcK5.png?x-oss-process=image/resize,w_1400/format,webp)
测试 CSS blur 和 opacity filter 动画的性能。动画过程中不断改变一组 div 元素的 transform 和 blur & opacity filter 的值。
![Focus](https://i.loli.net/2019/09/23/rejY1VnHw7NlbAu.png?x-oss-process=image/resize,w_1400/format,webp)
Chromium 的主要性能瓶颈是在 GPU 线程的合成上,也就是 blur & opacity filter 效果的绘制。Renderer 在 DOM API(改变样式)上也有一定的开销,但是相对合成来说还是较低。
Images
![Images](https://i.loli.net/2019/09/23/PUVlH7CdfXem6kg.png?x-oss-process=image/resize,w_1400/format,webp)
测试 Canvas getImageData 和 putImageData 的性能。动画过程中通过 get/putImageData 不断改变一组尺寸较小的 canvas 元素的内容。
![Images](https://i.loli.net/2019/09/23/4waR8TZi5gxv1yl.png?x-oss-process=image/resize,w_1400/format,webp)
Chromium 悲剧地因为 getImageData 被阻塞,GL readPixels 从 GPU 读取纹理数据到 CPU 内存非常耗时。Safari 在这项上应该会有巨大的优势 -_-p
Design
![Design](https://i.loli.net/2019/09/23/AfeoFGlvPXry2p1.png?x-oss-process=image/resize,w_1400/format,webp)
测试大量重复但是使用不同颜色的文本绘制的性能,重复的文本因为 transform 和 color 的差异形成投影的效果。动画过程中不断改变文本元素的 transform 和 color 属性。
![Design](https://i.loli.net/2019/09/23/U8jTeN2DbQdJfix.png?x-oss-process=image/resize,w_1400/format,webp)
Chromium 主要的性能瓶颈反而是在 Renderer 的样式计算上面,文本的光栅化耗时也不少,不过比起 Renderer 的耗时稍微略少,也可能成为瓶颈。
Suits
![Suits](https://i.loli.net/2019/09/23/neOE2K8b9kPzfC3.png?x-oss-process=image/resize,w_1400/format,webp)
大量使用不同的非矩形裁剪,不同渐变色,不同 transform 矩阵的 svg 元素的绘制,测试 svg 的绘制性能。
![Suits](https://i.loli.net/2019/09/23/YK2tJx9BobfZc6M.png?x-oss-process=image/resize,w_1400/format,webp)
Chromium 在图层光栅化(SVG 的绘制)和 Renderer 的样式计算上耗时都不低,两者都有可能成为性能瓶颈。
结语
跟很多人第一直觉并不完全符合的是,浏览器的图形性能瓶颈,很多时候并不在真正的“图形”上面,也就是说光栅化和合成有时并不是动画的性能瓶颈,有时性能瓶颈反而是在 JavaScript(包括计算和 DOM API 调用),样式计算,和重排版上。当然,MotionMark 是一项压力测试,实际的应用中我们可能很少同时改变那么多元素的属性,但是如果页面碰到性能问题,在检查性能瓶颈时,这也是需要考虑的一个因素。