Firefox 4:通过延迟框架构建提升性能

这是来自 Timothy Nikkel 博客 的转载。

延迟框架构建是 Gecko 中的新特性,它允许许多 DOM 操作(appendChild、insertBefore 等)不立即触发重排。这可以极大地提高非常复杂的网页的交互性能。如果您想测试它,您应该获取一个 Firefox Nightly 版本

延迟框架构建最近已合并到 mozilla-central。为了解释这意味着什么以及它如何改进问题,我们需要一些背景知识。网页 DOM 树中的每个节点都有一个为其创建的框架,用于确定节点在页面上的绘制位置及其大小。框架与 CSS 规范中的盒子概念 紧密相关。我们过去会急切地为 DOM 节点创建框架;也就是说,一旦节点插入到文档中,我们就会为其创建框架。但在许多情况下,这可能会造成不必要的开销。例如,如果脚本将大量节点插入到 DOM 中,我们会在插入每个节点时为其创建一个框架。但是,使用延迟框架构建,我们可以一次性批量处理所有这些节点,从而节省开销。此外,创建这些框架所需的时间不再阻塞该脚本,因此脚本可以继续执行其需要执行的操作,并在需要时创建框架。在其他情况下,脚本会将节点插入文档并立即删除它们,因此无需为这些节点创建框架,因为它们永远不会绘制在屏幕上。

因此,现在当节点插入文档时,节点会被标记为需要为其创建框架,然后在下一次刷新驱动程序发出通知时(当前为 20 毫秒间隔),框架就会被创建。刷新驱动程序也是驱动网页重排和 CSS 及 SVG 动画的程序。

让我们看看延迟框架构建有助于的两个示例。

在这个示例中,我们插入 80000 个 div 元素,然后刷新所有挂起的布局以计算脚本完成更改并对用户可见所需的时间。脚本可以继续执行而无需刷新布局,但我们在这里这样做是为了衡量实际工作所需的时间。

var stime = new Date();
var container = document.getElementById("container");
var lastchild = document.getElementById("lastchild");
for (var i = 0; i < 80000; i++) {
  var div = document.createElement("div");
  container.insertBefore(div, lastchild);
}
document.documentElement.offsetLeft; // flush layout
var now = new Date();
var millisecondselapsed = (now.getTime() - stime.getTime());

使用延迟框架构建,我们能够通过一个操作处理所有 80000 个 div 元素的插入,从而节省了 80000 次不同插入的开销。在没有延迟框架构建的版本中,我得到的平均时间为 1358 毫秒,使用延迟框架构建,我得到 777 毫秒。

此示例来自一个真实的网页。我们追加一个 div,然后设置“div.style.position = 'absolute';”,并重复 2000 次,然后刷新所有挂起的布局以计算脚本完成更改并对用户可见所需的时间。

var stime = new Date();
var container = document.getElementById("container2");
for (var i = 0; i < 2000; i++) {
  var div = document.createElement("div");
  container.appendChild(div);
  div.style.position = "absolute";
}
document.documentElement.offsetLeft; // flush layout
var now = new Date();
var millisecondselapsed = (now.getTime() - stime.getTime());

使用延迟框架构建,我们甚至在将位置设置为绝对之前都不会费心为 div 创建框架,因此我们不会浪费任何精力。在没有延迟框架构建的版本中,我得到的平均时间为 4730 毫秒,使用延迟框架构建,我得到 130 毫秒。

关于 Paul Rouget

Paul 是一位 Firefox 开发人员。

更多 Paul Rouget 的文章…


40 条评论

  1. myname

    18327(测试后浏览器冻结)与 7437(浏览器无问题)

    干得好!

    2010 年 5 月 26 日 06:24

  2. Pino

    第二个测试确实给出了令人印象深刻的结果,但第一个测试在 Firefox 3.6.4(测试版本)和 Minefield 上的分数均为 8000 毫秒。不知道我的情况是否特殊,或者是否存在问题……

    2010 年 5 月 26 日 06:32

  3. Pavel Dudrenov

    这真是太酷了,这是朝着我一直希望在 DOM 中看到的东西迈出的一步。那就是原子操作,例如:document.atomic(function(){/* 这里有大量的 DOM 操作 */}),其中原子操作不会进行任何重绘,或者类似的东西。当然,您现在可以使用 innerHTML 实现其中的一些功能,但并非全部。总之,这有点跑题了。

    延迟框架构建是一个好主意,它将使许多页面速度更快。干得好,伙计们。

    2010 年 5 月 26 日 07:42

    1. Boris

      Pavel,所有 JS 脚本在您描述的意义上都是“原子”的。唯一的问题是 UA 能否设法将其延迟到脚本执行结束。几乎任何现代 UA 都会延迟布局、样式计算、绘制。有些会延迟渲染对象构建(这就是本文讨论的内容)。

      2010 年 5 月 26 日 09:07

      1. Pavel

        我想“原子”这个名称是不合适的。我的意思是不会发生任何重绘,因为在您的脚本中间确实会发生一些重绘。

        2010 年 5 月 26 日 10:55

        1. Boris

          Pavel,目前在任何我所知的现代浏览器中,重绘都不会发生在脚本中间(不考虑同步 XHR 和模式对话框)。

          2010 年 5 月 26 日 12:36

          1. pavel

            例如 el.offsetHeight 呢?

            2010 年 5 月 26 日 15:31

          2. Boris

            如果您执行 el.offsetHeight,那么浏览器必须进行布局(但不绘制)。无论如何都会发生这种情况,即使添加了您建议的“原子”API(因为任何其他操作都可能导致奇怪的竞争条件,并且脚本是否工作取决于硬件、操作系统、一天中的时间等细节)。

            2010 年 5 月 26 日 19:03

  4. Richard Le Poidevin

    非常令人印象深刻。我得到
    测试 1:10489 -> 1333
    测试 2:3288 -> 507

    2010 年 5 月 26 日 09:04

  5. LonerGoth

    测试一结果 – 25731
    测试二结果 -6851

    Firefox 3.6.4。
    Windows 7 家庭高级版 32 位

    三星 R720 笔记本电脑,搭载
    奔腾双核 CPU T4300 @ 2.10GHz
    4.0 Gb 内存
    ATI Mobility Radeon HD 4300 系列显卡。

    2010 年 5 月 26 日 09:48

  6. LonerGoth

    3.7a5pre 更新于 2010 年 5 月 26 日 17:50
    测试一:1686 毫秒
    测试二:665 毫秒

    显著改进。谢谢!

    2010 年 5 月 26 日 09:55

  7. LonerGoth

    抱歉,我在英国,我使用了当地格林尼治标准时间 + 0(UTC + 0)时间……

    2010 年 5 月 26 日 09:56

  8. CaChi

    测试 1 -> 3994
    测试 2 -> 5755

    2010 年 5 月 26 日 10:32

  9. homm

    Firefox 3.6
    2500 毫秒
    5500 毫秒

    Firefox 4 此版本
    1660 毫秒
    193 毫秒

    Chrome 5.0
    184173 毫秒!!!
    111 毫秒

    Opera 10.54
    1300 毫秒
    50 毫秒

    Firefox 4 在测试后 CPU 使用率很高,并且在滚动页面和切换到其他应用程序时会冻结。Opera 仍然运行得很快。

    2010 年 5 月 26 日 11:29

  10. sawrub

    Firefox 3.5.9 [Fedora] 版本。
    测试 1 在第一秒和第三次尝试中大约花费了 4k。
    测试 2 在第一次尝试中大约花费了 9k,在第二次尝试中花费了 60k,并且在第三次尝试中不得不终止 Firefox,因为 CPU 使用率在超过 3 分钟的测试中一直保持在 100%。

    使用 Chrome 5 [Fedora] 测试了相同的脚本,我看到的最大区别是无论花费多少时间都会冻结。在 Firefox 中运行脚本会冻结整个应用程序,无法切换到 FF 中的其他选项卡。在 Chrome 中执行相同的操作只会冻结该选项卡,而其他选项卡可以独立于正在测试的选项卡使用。在 Chrome 中进行测试感觉像是不同的浏览器,尽管是在与 FF 相同的行上进行测试的。
    看到 Firefox 冻结真的很遗憾。

    2010 年 5 月 26 日 19:59

  11. Lumi

    Seamonkey 2.1a1pre 版本:20100509010325
    测试 1 – 5433
    测试 2 – 2108

    2010 年 5 月 27 日 00:35

  12. carol

    第一次测试 1056 毫秒
    第二次 466 毫秒

    我不知道它在现实世界中的价值,比如在常规测试中,但任何能提高如此多结果的东西在我看来都应该很好,所有条件都相对平等!

    2010 年 5 月 27 日 08:26

  13. Wurdebalg Hurrst

    我想知道这是否与 IE 测试中心的六边形图块显示测试有关,即它是否使 Firefox 在此特定测试中表现更好。

    2010 年 5 月 27 日 12:00

  14. Benben

    我无法在页面中找到 id 为“lastchild”的元素
    这是故意的吗?

    2010 年 5 月 28 日 13:35

  15. Tyler Durden

    我认为这些微基准测试对于跨浏览器比较是不公平的。

    无论如何,使用 document.createDocumentFragment() 进行另一个更真实的基准测试会很好。事实上,我以为这个方法一开始就是为了解决这个问题而创建的。

    我同意 Pavel Dudrenov 的观点:拥有一个新的 document.atomic 函数允许开发人员控制浏览器重排会很有趣。“原子”重排可能有利于许多 Web 应用程序,但如果使用不当,也可能导致很多麻烦……。

    2010 年 5 月 29 日 14:54

  16. Opperhert

    第一次得分约为 2508
    第二次 4609
    第三次(解冻后)67000

    http://i49.tinypic.com/293htnn.png

    2010 年 6 月 7 日 05:43

  17. carol

    第一次测试 981
    第二次测试 407
    不确定这是不是故意的,但如果我在不刷新页面的情况下再次尝试测试
    这些数字每次都会翻倍(或多或少),因此它可以很快变得非常庞大!
    感谢您的研究,我爱那些试图简化而不是复杂化的程序员
    (例如 html5)而不是学习他们手头工具的全部功能,比如 ecmascript5。继续努力!

    2010 年 6 月 11 日 08:46

  18. Ali

    “然后在下一次刷新驱动程序发出通知时(当前为 20 毫秒间隔),框架就会被创建。刷新驱动程序也是驱动网页重排和 CSS 及 SVG 动画的程序”

    所以这意味着页面最多只能以 50 FPS 渲染,对吗?

    Flash Player 也不能比这更快地渲染吗?

    Ali

    2010 年 7 月 6 日 14:49

  19. Gilfuin

    Chrome 中为 72 毫秒;)

    2010 年 7 月 7 日 06:15

  20. Boris

    > 所以这意味着页面最多只能以 50 FPS 渲染,对吗?

    是的。确切的时机可能会发生变化;有一个很好的论点认为正确的数字是 60Hz,而不是 50Hz。

    请注意,您典型的现代 LCD 显示器仅以 60Hz 刷新,因此比这更快的绘制完全没有意义。

    > Flash Player 也不能比这更快地渲染吗?

    它无法以超过 60Hz 的速度更新屏幕显示,不能。当然,它可以以比这更高的速率推送位,但没有人会看到其中的一些位。在 Gecko 中推送位也没有问题;只是没有用。

    2010 年 7 月 7 日 17:36

  21. Ali

    Boris 的解释很有帮助。谢谢。

    Ali。

    2010年7月7日 23:09

  22. […] lazy frame construction 的实现 […]

    2010年7月28日 02:35

  23. 匿名用户

    抱歉,有些内容偏离主题。其他浏览器怎么样?
    Opera 10.54:第一次测试:1335毫秒,第二次测试:143毫秒
    Opera 10.10:第一次测试:1255毫秒,第二次测试:155毫秒
    Opera 9.64:第一次测试:2608毫秒,第二次测试:163毫秒
    Opera 8.54:第一次测试:3950毫秒,第二次测试:100毫秒

    Internet Explorer 7.0:第一次测试:3400毫秒,第二次测试:6775毫秒。

    是不是很神奇?

    2010年8月25日 17:33

  24. Marc Dix

    结果差异很大……

    Firefox 4.0,Beta 4:768毫秒/330毫秒
    Firefox 3.6.8:1799毫秒/1293毫秒
    Chrome 5.0.375.127:34719毫秒/68毫秒
    Safari 5.0.1:33190毫秒[冻结]/64毫秒
    Internet Explorer 8.0.7600.16385:2231毫秒/687毫秒
    Opera 10.61:578毫秒/87毫秒

    2010年8月29日 23:19

  25. Luis Alvarado

    在 Ubuntu 10.04.1 上测试得到以下结果

    测试 1(初始测试)
    Firefox 3.6.10 – 测试 1 – 2840毫秒
    Firefox 3.6.10 – 测试 2 – 3850毫秒
    Chrome 6.0.472.62 – 测试 1 – 31143毫秒
    Chrome 6.0.472.62 – 测试 2 – 71毫秒

    测试 2(刷新页面)
    Firefox 3.6.10 – 测试 1 – 2830毫秒
    Firefox 3.6.10 – 测试 2 – 4675毫秒
    Chrome 6.0.472.62 – 测试 1 – 29153毫秒
    Chrome 6.0.472.62 – 测试 2 – 64毫秒

    测试 3(重新打开浏览器)
    Firefox 3.6.10 – 测试 1 – 2767毫秒
    Firefox 3.6.10 – 测试 2 – 4652毫秒
    Chrome 6.0.472.62 – 测试 1 – 31186毫秒
    Chrome 6.0.472.62 – 测试 2 – 63毫秒

    2010年9月20日 00:53

  26. edorivai

    Google Chrome 崩溃了,而 Minefield 获得了令人印象深刻的 1400 毫秒!

    顺便说一下,在 Linux 上工作。

    干得好,伙计们!

    2010年9月23日 14:54

  27. Michel

    Windows 7
    Firefox 4 beta 7:第一次测试 1360毫秒 - 第二次测试 550毫秒
    Internet Explorer 9 beta:第一次测试 3749毫秒 - 第二次测试 319毫秒
    Google Chrome Beta 8:第一次测试 冻结 - 第二次测试 211毫秒

    2010年11月14日 07:36

  28. Shane Bundy

    在 Windows 7 上;
    Opera 11.10 (b2039) – 678毫秒
    Minefield (2011/03/11) – 1473毫秒
    Chromium (r77944) – 152688毫秒

    我不知道是否应该对 Opera 的时间感到惊讶,但我真的对 Chromium 的时间感到惊讶。我确定 WebKit 也使用 LFC,还是 Chromium 中 Google 的 WebKit 修改导致了这种情况?

    2011年3月13日 09:51

  29. Shane Bundy

    加载此页面后,我重新加载并重新进行了测试,这次 Minefield 的结果是 160 毫秒(在一台旧的赛扬 M 530 上!)

    2011年3月14日 16:48

    1. Shane Bundy

      没有意识到有两个测试。我的错。

      在 Minefield 上;
      测试 1 – 1683毫秒
      测试 2 – 162毫秒

      :-)

      2011年3月14日 16:50

  30. […] 使用延迟帧构造更具响应性的页面渲染 […]

    2011年3月18日 22:59

  31. psxlover

    每次我在 Firefox 4 中运行其中一项测试时,我得到的结果都比之前大……

    2011年3月22日 14:05

  32. […] 使用 HTML 历史 API 在不重新加载页面的情况下更新 URL 字段使用延迟帧构造更具响应性的页面渲染链接历史查找是异步执行的,以便在页面加载期间提供更好的响应能力CSS […]

    2011年4月10日 22:44

  33. […] 使用延迟帧构造更具响应性的页面渲染 […]

    2011年7月6日 15:38

  34. dang ky ten mien

    感谢分享

    2012年1月15日 10:48

本文评论已关闭。