Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【译】首次有效渲染时间(Time to First Meaningful Paint) #1

Open
psycholali opened this issue Apr 15, 2021 · 0 comments
Open

Comments

@psycholali
Copy link
Owner

psycholali commented Apr 15, 2021

首次有效渲染时间(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][1]>
Status: draft Tracking bug
原文地址:[https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view#heading=h.p4gz01f31cgq][2]
翻译: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秒时完成。

image

图1:当加载谷歌搜索结果页面时,被逐步加载到布局树中的布局对象的数量。

image

图2:页面加载的视觉进展过程

从这个例子中,你可以观察到”布局对象”数量与页面加载的“完整性”密切相关。

(LayoutAnalyzer)通过几个计数器收集有关布局操作的信息,并释放他们作为(trace events)。我用这些计数器做实验并且发现 有大量的 “尚未布局的布局对象”*(LayoutObjectsThatHadNeverHadLayout)的计数器是“首次有效渲染”的一个很好的参考项。其他的计数器, 像“尺寸改变的布局块”( LayoutBlockSizeChanged)或“全都布局好了的布局对象”(TotalLayoutObjectsThatWereLaidOut)都对“首次有效渲染”参考更小。这意味着,按照渲染的重要程度, 新的布局对象的数量比“重新布局的布局对象”的数量(re­layouts)*更有意义。

所以,这是我们第一次求的近似的“首次有效渲染时间”:

“首次有效渲染时间 ”= 紧跟着“最大布局变化”之后的渲染时间点

“最大布局变化”*(Biggest layout change)*就是 有着大量 “尚未布局的布局对象” 的布局。
在图1中, “最大布局变化”是在1.86秒时, 所以下一个渲染时间点(1.907秒)就是“首次有效渲染时间”。

为什么用“布局对象”的总数来定义“首次有效渲染时间”呢?例如,我们能不能定义“首次有效渲染时间”为 百分之多少的布局已经完成呢?问题是我们不能可靠地区分这些东西,当页面完成时。我们不希望根据我们判断一个页面是否加载完成的不同, 使得计算的“首次有效渲染时间”也大幅度的不同。

算法

这一基本方法比“首次有内容的渲染”得到更好的结果 (见后文中的“评估单元”),但在许多情况下它仍然不能检测到真正有意义的渲染。

现在,存在一些这个基本方法不起作用的典型原因, 所以我们要通过添加一些算法,使我们即使在这些情况下也能显著地提高精确度。

1.缓解长页面

图3是http://us.weibo.com/gb页面在视觉上的加载过程([完整的WebPageTest结果](http://www.webpagetest.org/result/160329\_NZ\_GNA/))。
图4显示了在页面加载期间被添加的布局对象的数量。“首次有效渲染时间”是6.047 s(当在一堆文本内容出现时),但最大的布局变化大约是在23.8 s。接下来的渲染(24.25秒时)在视觉上是没有意义的,因为在23.8 s发生的布局变化时添加的对象在页面底部,到了可见区域之外。

image
image
图3: 网站http://us.weibo.com/gb的视觉上的加载过程

image

图4: 页面加载期间被添加的布局对象的数量变化图

我们该如何阻止这些“低于折叠的布局”(below-the-fold layouts)混淆我们的度量标准呢? 对于我们来说,最理想的是去检查每个布局是否是可见的, 但是如果想让这个变成一个UMA度量标准, 就必须避免布局期间所带来的昂贵的计算。所以, 当页面比屏幕高度长的时候,我们降低了页面的布局。具体来说,我们使用“布局意义”来代替“布局对象的原始数量”,如下:

“布局意义”( layout significance )= 添加的布局对象的数量/ max(1,页面高度/屏幕高度)

例如,在图4中最大的布局变化发生的时候(23.8秒),页面高度是屏幕高度的4.25倍, 所以这个“布局意义”是布局对象的数量 除以4.25。图5显示的布局过程与图4类似,但图5使用了上面定义的“布局意义”。现在,最重要的布局变化是在5.89秒, 就在“首次有效渲染”(6.047秒)之前。

image
图 5: 布局意义(布局对象数量由页面高度确定权重)

2.网页字体

图6是http://www.msn.com/页面视觉的加载过程([完整的WebPageTest结果](https://www.webpagetest.org/result/160329\_GA\_GNN/))。最大的布局变化是在2.51秒,但下面的截图没有文本内容,这是因为网络字体仍然加载。

image

图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/spreadsheets/d/1FEcDwXfgMf5rw1lNn07Cm04A­r7AdPzmt0aAufMsnnk/ edit?usp=sharing
截屏 (橙色高亮的是检测到的“首次有效渲染时间”): https://goo.gl/wOHl2s

表1总结了结果。“首次有内容的渲染FCP”是我们目前有的最好的近似值,它匹配了52.5%的“用户感知的首次有效渲染时间”[标注1]。而“基本布局对象”(不包含“页面高度算法”或“字体算法”)的准确率有57.1%,略优于“首次有内容的渲染” 。同时包含“页面高度算法”和“网页字体算法”这两个算法的计算准确率高达77.3%。

(标注1)在以前的评估中,我们使用了宽松定义的“首次有效渲染”(屏幕上的部分内容是可读的)。现在我们使用更严格的定义————页面的主要内容必须是易读的。

image

表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

“首次有效渲染时间”的值可以在“度量标准”侧板中看到
(见下图)。
image

另外,这些标准可以从一个跟踪文件里计算得到,通过使用
这个命令:

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): layout­based approach loading­dev thread implementation tracking bug

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant