简介
如果您是那种会关注浏览器工作原理等内容的人,那么您可能已经知道,最近有一些很棒的文章详细介绍了 Chrome 的 GPU 加速渲染器/合成操作。首先,Chrome 中的加速渲染:层模型很好地介绍了 Chrome 如何使用层概念来绘制页面;如需深入探究,请参阅 Chrome 中的 GPU 加速合成,其中讨论了 Chrome 如何使用这些层以及 GPU 来渲染页面。
哲学问题
我花了大量时间编写用于 3D 目的的软件光栅化器,因此我清楚地意识到,在绘制页面时,某些 CSS 属性应该具有不同的性能。例如,将小图片光栅化到屏幕上与在任意形状上绘制阴影是完全不同的算法操作。因此,问题就变成了:不同的 CSS 属性如何影响网页的渲染权重?
我的目标是根据绘制时间对大量 CSS 属性/值进行分类,以便我们了解哪些类型的 CSS 属性比其他属性更高效。为此,我用胶带和泡泡糖编写了一些自动化脚本,试图为 CSS 绘制时间添加数值可见性,其工作原理如下:
- 生成一套单独的 HTML 页面;每个页面都有一个 DOM 元素和一些附加到该元素的 CSS 属性排列。
- 运行一些自动化脚本,这些脚本会针对每个页面执行以下操作:
- 转储所有计时,并惊叹于这些数字。(这部分很重要…)
通过这种设置,我们生成了一套 HTML 页面,其中每个页面都包含 CSS 属性和值的唯一排列组合;例如,以下是两个 HTML 文件:
<style>
#example1 {
background: url(foo.png) top left / 50% 60%;
padding: 20px;
margin-top: 10px;
margin-right: 20px;
text-align: center;
}
</style>
<div id="example1">WOAH</div>
另一个则更复杂
<style>
#example1 {
background-color:#eee;
box-shadow: 1px 2px 3px 4px black;
border-radius: 50%;
background: radial-gradient(circle closest-corner, white, black);
padding: 20px;
margin-top: 10px;
margin-right: 20px;
text-align: center;
}
</style>
<div id="example1">WOAH</div>
下面是上一个示例的变体,其中我们仅更改了 radial-gradient 值:
<style>
#example1
{
background-color:#eee;
box-shadow: 1px 2px 3px 4px black;
border-radius: 50%;
background: radial-gradient(farthest-side, white, black);
padding: 20px;
margin-top: 10px;
margin-right: 20px;
text-align: center;
}
</style>
<div id="example1" style="padding: 20px; margin-top: 10px;margin-right: 20px; text-align: center;">WOAH</div>
然后,每个网页都会加载到全新的 Chrome 实例中(以确保计时不会因网页重新加载时的任何过时状态而产生偏差),并拍摄 Skia 图片 (*.SKP),以评估使用哪些 Skia 命令来绘制网页。为每个 HTML 文件生成 SKP 文件后,我们会运行另一个批处理,通过 Skia 基准测试应用(基于 Skia 源代码构建)推送 *.SKP 文件,该应用会输出渲染该网页所需的平均时间。
评估数据
由此,我们现在可以大致了解一组 CSS 属性的绘制耗时。或者,我们可以开始按 CSS 属性的绘制性能对其进行堆栈排名。下面是一个使用 Chrome 27 Beta 版截取的大图,其中显示了此过程中的所有计时数据。请注意,随着 Chrome 的速度越来越快,所有数据都可能会发生变化。
每个竖条代表具有单一 CSS 属性组合的网页的绘制时间(放大 100 倍;此图表的真实比例值为 0-1.56 毫秒)。虽然有很多漂亮的线条,但这种形式的数据并没有什么用,我们需要进行一些数据挖掘,找出有用的趋势。
首先,我们发现有证据表明,某些 CSS 属性的渲染成本明显高于其他属性。例如,在 DOM 元素上绘制阴影涉及使用样条线和其他各种复杂操作的多阶段操作,而透明度则应该更容易渲染。
其次,更有趣的是,CSS 属性组合的绘制时间可能大于其各部分绘制时间之和。从观察者的角度来看,这有点奇怪,我们预计 A+B = C,而不是 2.2C。例如,添加 box-shadow 和 border-radius-stroke:
真正有趣的是,这不仅仅是 box-shadow 属性本身,而是该特定值排列组合。例如,下图显示了 box-shadow : 50% 和 border-radius 的分组,其中包含值变体。
从数据来看,这种情况会持续一段时间。各种奇怪的组合不胜枚举,我的测试套件几乎无法涵盖所有组合;还有大量测试和组合可能会产生有趣的结果
查找网页的渲染权重
借助跟踪网页上每个元素的呈现时间的能力,开发者可以开始评估其网页呈现权重以及它对网站响应能力的影响;以下是一些入门提示
- 在 Chrome 开发者工具中使用 Chrome 的连续绘制模式,了解哪些 CSS 属性会产生开销。
- 将 CSS 审核纳入现有的代码审核流程,以发现性能问题 在 CSS 中查找您正在使用已知开销较大的元素(例如渐变和阴影)的位置。问问自己,我真的需要在这里添加这些内容吗?
- 如有疑问,请始终以提升性能为准。您的用户可能不记得您网站上各列的内边距宽度,但他们会记得访问您网站时的感受。
最后总结
此实验最有趣的一点是,时间会随着每个 Chrome 版本不断变化(希望越来越快 ;) 浏览器软件是一个不断变化的领域。今天慢,明天可能就快了。您可以从本文中了解到,应避免将 box-shadow: 1px 2px 3px 4px 放在已具有 border-radius:5 的元素上。不过,更重要的收获应该是 CSS 属性会直接影响页面绘制时间。