首次有效渲染时间(Time to First Meaningful Paint)
一个基于布局的近似方法(A layout-based approach)
[Public] Created: April 12, 2016
Last updated: June 6, 2016 Owner: Kunihiko Sakamoto <ksakamoto@chromium.org>
Status: draft Tracking bug
原文地址:https://docs.google.com/docum...
翻译:psychola(第一次翻译此类文章,有翻译错误请指出哦,如若转载请注明出处哦,谢谢(^U^)ノ~)
背景
“首次有效渲染”(First Meaningful Paint)是当页面的主要内容出现在屏幕上花的时间。这将是我们对于用户感受到的加载体验的主要度量标准(Metric)。
“主要内容”(primary content)的定义根据不同页面而有所不同。对于博客里的文章,它是标题+首页可见的上半部分文章内容(above-the-fold text),并且其文本必须是可见的,而不是在等待字体加载。对于搜索引擎,它是搜索结果。如果一个图片对于页面来说很重要(例如e-commerce产品页),则 “首次有效渲染”需要这个图片是可见的状态。所以,一个渲染如果只有页面头部、导航栏,或加载提示标(如旋转的图标)就不是合格的“主要内容”。
“首次有内容的渲染”(First Contentful Paint)是当一些有内容的东西(如文字、图像、canvas画布或SVG)第一次被渲染花的时间。它在UMA和trace event中需要用到,并且它可以作为“首次有效渲染”时间的近似值,但它经常捕获没有意义的渲染,如头部和导航栏。
在本文中,我们提出一个基于布局的方法去近似求“首次有效渲染时间”,对于用户感知到的“首次有效渲染”时间,这个方法的准确度有77%(总共198个页面)。
设计
基本方法:计算布局对象(layout objects)个数
随着页面加载, 布局对象逐步被加到布局树(layout tree)中。
图1显示了当加载谷歌搜索结果页面时,被逐步加载到布局树中的布局对象的数量。图2是那个页面加载的视觉进展过程,从WebPageTest结果中可以看到。 (完整的WPT网页测试结果)。
“首次有内容的渲染”(1.577秒时)只有页面头部,并且到那个时候60个“布局对象”被添加到页面。在1.76 s时,顶部广告已渲染了一部分,并且“布局对象”总数是103个。 在大约1.86 s时, 搜索结果出现了, 并且261个“布局对象”被添加到“布局树”中。接下来的渲染(1.907秒时)是“首次有效渲染”。在那之后,剩下的搜索结果和页脚都出现在了下面那些不可见的区域中。视觉上,页面的渲染在2.425秒时完成。
图1:当加载谷歌搜索结果页面时,被逐步加载到布局树中的布局对象的数量。
图2:页面加载的视觉进展过程
从这个例子中,你可以观察到”布局对象”数量与页面加载的“完整性”密切相关。
(LayoutAnalyzer)通过几个计数器收集有关布局操作的信息,并释放他们作为(trace events)。我用这些计数器做实验并且发现 有大量的 “尚未布局的布局对象”(LayoutObjectsThatHadNeverHadLayout)的计数器是“首次有效渲染”的一个很好的参考项。其他的计数器, 像“尺寸改变的布局块”( LayoutBlockSizeChanged)或“全都布局好了的布局对象”(TotalLayoutObjectsThatWereLaidOut)都对“首次有效渲染”参考更小。这意味着,按照渲染的重要程度, 新的布局对象的数量比“重新布局的布局对象”的数量(relayouts)更有意义。
所以,这是我们第一次求的近似的“首次有效渲染时间”:
“首次有效渲染时间 ”= 紧跟着“最大布局变化”之后的渲染时间点
“最大布局变化”(Biggest layout change)就是 有着大量 “尚未布局的布局对象” 的布局。
在图1中, “最大布局变化”是在1.86秒时, 所以下一个渲染时间点(1.907秒)就是“首次有效渲染时间”。
为什么用“布局对象”的总数来定义“首次有效渲染时间”呢?例如,我们能不能定义“首次有效渲染时间”为 百分之多少的布局已经完成呢?问题是我们不能可靠地区分这些东西,当页面完成时。我们不希望根据我们判断一个页面是否加载完成的不同, 使得计算的“首次有效渲染时间”也大幅度的不同。
算法
这一基本方法比“首次有内容的渲染”得到更好的结果 (见后文中的“评估单元”),但在许多情况下它仍然不能检测到真正有意义的渲染。
现在,存在一些这个基本方法不起作用的典型原因, 所以我们要通过添加一些算法,使我们即使在这些情况下也能显著地提高精确度。
1.缓解长页面
图3是http://us.weibo.com/gb页面在...完整的WebPageTest结果)。
图4显示了在页面加载期间被添加的布局对象的数量。“首次有效渲染时间”是6.047 s(当在一堆文本内容出现时),但最大的布局变化大约是在23.8 s。接下来的渲染(24.25秒时)在视觉上是没有意义的,因为在23.8 s发生的布局变化时添加的对象在页面底部,到了可见区域之外。
图3: 网站http://us.weibo.com/gb的视觉...
图4: 页面加载期间被添加的布局对象的数量变化图
我们该如何阻止这些“低于折叠的布局”(below-the-fold layouts)混淆我们的度量标准呢? 对于我们来说,最理想的是去检查每个布局是否是可见的, 但是如果想让这个变成一个UMA度量标准, 就必须避免布局期间所带来的昂贵的计算。所以, 当页面比屏幕高度长的时候,我们降低了页面的布局。具体来说,我们使用“布局意义”来代替“布局对象的原始数量”,如下:
“布局意义”( layout significance )= 添加的布局对象的数量/ max(1,页面高度/屏幕高度)
例如,在图4中最大的布局变化发生的时候(23.8秒),页面高度是屏幕高度的4.25倍, 所以这个“布局意义”是布局对象的数量 除以4.25。图5显示的布局过程与图4类似,但图5使用了上面定义的“布局意义”。现在,最重要的布局变化是在5.89秒, 就在“首次有效渲染”(6.047秒)之前。
图 5: 布局意义(布局对象数量由页面高度确定权重)
2.网页字体
图6是http://www.msn.com/页面视觉的...完整的WebPageTest结果)。最大的布局变化是在2.51秒,但下面的截图没有文本内容,这是因为网络字体仍然加载。
图6: http://www.msn.com/页面的视觉...
当一个网页字体在加载时,文本已经布局了(备用字体Fallbackfont的字体度量标准是这样判断的),但文本其实并没有在字体阻塞期间渲染出来(默认情况下, 字体阻塞期间是从加载开始的3秒)。Blink浏览器引擎(Blink是一个由Google主導开发的开源浏览器排版引擎)布局层并不关心文本是不是可见的, 但由于文本对于用户感受到的加载体验是很重要的,所以我们的“首次有效渲染”度量标准应把页面字体的可见性考虑进去。
所以我们引入算法:如果当布局发生时还存在加载中的字体,布局的变化统计将会被推迟,直到该字体显示出来。(如果三秒内加载了就用该字体,否则超时了就用备用字体显示)。
但是,把这条规则应用到所有网站字体显得太勉强了——较小的字体,如图标字体并不会阻塞“首次有效渲染时间”。在我实验的实现中, 超过200个字节的网页字体才会阻塞“首次有效渲染时间”。
附加说明
● 这个测量只是基于新的布局对象,不考虑已布局的布局对象的大小或位置的改变。所以这并不适合“稳定的布局”(layout stabilized)的度量标准。
● 对于一些页面, “首次有效渲染”的关键因素是图片。对于等待中的图片,我们可能需要一些算法, 就像等待加载的字体用的算法一样。
● 由于这个指标是基于布局对象的,所以这是不受“未连接到布局树的DOM元素”的影响 (例如display:none的元素)。然而,一些页面渲染内容在覆盖物之下, (例如,www.flipkart.com的初始页),或在一个透明层之上(例如www.adobe.com)去实现一个fadein的加载效果。在这些情况下,我们的方法发现布局变化对用户来说是看不见的, 而且计时是毫无意义的。
● 页面高度算法使这个度量标准依赖于屏幕的大小。这是很正常的,因为我们的兴趣在是折叠渲染区域之上的部分, 但推导这个度量标准可能有点复杂。
评估
为了评估这个方法, 我使用修补版的ChromeAPK,已经在200个热门网站(基于alexa-top,包括子页面)上跑了WPT测试。然后我下载WPT 的trace.json文件,并且通过运行trace event来计算上文中提到的基于布局的“首次有效渲染时间”。WPT还生成截图幻灯片,所以我们可以比较计算的“首次有效渲染时间”和人为选择的“用户感受到的首次有效渲染时间”的区别。
结果: https://docs.google.com/sprea...r7AdPzmt0aAufMsnnk/ edit?usp=sharing
截屏 (橙色高亮的是检测到的“首次有效渲染时间”): https://goo.gl/wOHl2s
表1总结了结果。“首次有内容的渲染FCP”是我们目前有的最好的近似值,它匹配了52.5%的“用户感知的首次有效渲染时间”[标注1]。而“基本布局对象”(不包含“页面高度算法”或“字体算法”)的准确率有57.1%,略优于“首次有内容的渲染” 。同时包含“页面高度算法”和“网页字体算法”这两个算法的计算准确率高达77.3%。
(标注1)在以前的评估中,我们使用了宽松定义的“首次有效渲染”(屏幕上的部分内容是可读的)。现在我们使用更严格的定义————页面的主要内容必须是易读的。
表1:评估结果的总结
实现方案
第 1步: 基于trace的实现
实验的实现包括了:
(1)Blink浏览器引擎改变(Blink side change),这个改变 增强了LayoutAnalyzer对页面高度和网页字体可见性信息的trace event;
(2)一个脚本 ,该脚本处理trace event去计算最大的布局变化。
这个实现方案很容易变成一个TBMv2度量标准(metrics),而这个标准可用于像Page Cycler V2一样的基准。
第2步: UMA
过时了,请看 UMA design doc(UMA设计文档)
我们希望去收集“首次有效渲染时间”作为一个UMA度量标准。Finch和heartbeat依赖于UMA, UMA是唯一我们有的能理解真实用户体验的系统。
LayoutAnalyzer默认情况下是禁用的,因为它有一些计算开销。我们不需要所有的LayoutAnalyzer计数器来计算“首次有效渲染时间”, 只需要计算被添加到布局树的布局对象的数量,这个数量可以以不同的方式收集。
实现计划:PaintTiming持有一个渲染的时间戳,这个时间戳目前在最大的布局变化之后。当有更重要的布局变化时,时间戳会更新。这个时间戳会被发送到浏览器进程的PageLoadMetrics中,并且当用户从页面导航出去时它会作为UMA直方图。
注意, “首次有效渲染时间”的时间戳可以在页面加载期间更改几次。如果页面加载在完成之前失败, “页面加载度量标准”将报告暂时的时间,这个时间可能不是很有意义,例如导航栏或旋转图标。页面加载度量标准” 在单独的直方图里记录“失败的导航栏”的时间 (“页面加载失败时间” *)。对于失败的页面加载,我们需要非常小心地解读它的“首次有效渲染时间”度量标准。
Implementation status
第一步 (基于trace的实现) 来了, 并且它会作为一个TBMv2 metric(TBMv2标准)。现在,你可以从跟踪观察器来看“首次有效渲染时间”。
你可以去chrome://tracing使用此功能, 并勾选下方这两项去记录一个trace:
● blink.user_timing
● loading
“首次有效渲染时间”的值可以在“度量标准”侧板中看到
(见下图)。
另外,这些标准可以从一个跟踪文件里计算得到,通过使用
这个命令:
chromium/src$ third_party/catapult/tracing/bin/run_metric trace.json loadingMetric
第二步 (UMA): 第一个版本的UMA来了。目前, 直方图是作为实验的标记内容,在促进他们的实验之前,我们想做一些改进。(跟踪在https://crbug.com/632081).
引用
"Is it useful?" metric (aka. First meaningful paint): layoutbased approach loadingdev thread implementation tracking bug
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。