关于 JavaScript 引擎组件的快速说明

最近有很多关于JägerMonkey (JM) 文章的帖子,其中一些帖子对 Mozilla JM 工作中使用的技术部分有一些细微的错误。 所以这里有一个关于我们正在使用的技术、各个部分的功能以及它们来源的快速概述。

1. SpiderMonkey. 这是 Mozilla 的核心 JavaScript 解释器。 该引擎将原始 JavaScript 代码转换为中间字节码。 然后解释该字节码。 SpiderMonkey 负责 Firefox 3 及更早版本的所有 JavaScript 处理。 我们继续改进该引擎,因为它仍然是我们许多在 Firefox 3.5、3.6 及更高版本中所做工作的基础。

2. 跟踪. 跟踪是在 Firefox 3.5 之前添加的,它负责我们在性能方面取得的大幅提升。(尽管其中一部分是因为我们也改进了底层的 SpiderMonkey 引擎。)

这是我们跟踪的方式

  1. 在执行期间监控解释的 JavaScript 代码,查找使用次数超过一次的代码路径。
  2. 当我们找到使用次数超过一次的代码片段时,优化该代码。
  3. 将该优化后的表示形式组装成机器代码并执行它。

自从 Firefox 3.5 以来,我们发现当我们处于完全跟踪模式时,速度非常快。 当我们必须“回退”到 SpiderMonkey 并解释 + 记录时,速度很慢。

跟踪的难点之一是生成快速运行的代码。 这由一段称为 Nanojit 的代码完成。 Nanojit 是最初是 Tamarin 项目的一部分的代码。 Mozilla 不使用大部分 Tamarin 有两个原因:1. 我们没有发布 ECMAScript 4,2. Tamarin 的解释部分比 SpiderMonkey 慢得多。 对于 Firefox 3.5,我们采用了最好的部分——Nanojit——并将其与 SpiderMonkey 连接起来。

Nanojit 做两件事:它接受 JavaScript 的高级表示形式并进行优化。 它还包含一个汇编程序,用于将优化后的表示形式转换为用于机器级执行的本机代码。

Mozilla 和 Adobe 继续在 Nanojit 上进行合作。 Adobe 使用 Nanojit 作为其 ActionScript VM 的一部分。

3. Nitro 汇编程序. 这是一段我们从 Apple 的 webkit 版本中获取的代码,用于生成用于执行的本机代码。 Nitro 汇编程序与 Nanojit 非常不同。 虽然 Nanojit 接受高级表示形式、进行优化然后生成代码,但 Nitro 汇编程序只生成代码。 所以它很复杂,是底层代码,但它没有执行 Nanojit 所做的事情。

我们正在使用 Nitro 汇编程序(以及许多其他新代码)来构建每个人都拥有的东西——编译后的 JavaScript——然后我们将做与 Firefox 3.5 相同的事情——将跟踪与之连接起来。 所以我们希望拥有所有最好的东西:SpiderMonkey 生成用于执行的本机代码,就像其他 VM 一样,并且能够继续跟踪以获得更紧密的内循环以获得更高的性能。

我希望这有助于解释我们正在使用哪些技术片段以及它们如何融入 Firefox 的 JS 性能的整体图景。


33 条评论

  1. soumynano

    所以我可以说,您打算用本机编译器 Nitro(以及相关支持代码)替换 SpiderMonkey 的解释器吗?

    2010 年 3 月 8 日 下午 3:08

  2. Magne Andersson

    我想问一下,这将使 Firefox 在 JavaScript 速度方面与 Safari、Google Chrome 和 Opera 10.5 相比如何? 您有什么猜测吗? 是否有可能它甚至可以比其中至少一个更快?

    我的另一个问题是,您认为我们什么时候可以看到一个稳定的实现? 我们是在说 Firefox.next 还是更遥远的版本?

    谢谢!

    2010 年 3 月 8 日 下午 3:12

  3. Boris

    Magne,我们在很多不同的情况下已经比所有这些浏览器都快了。

    所以你真正的问题是关于某个特定基准测试吗?

    2010 年 3 月 8 日 下午 3:33

    1. Magne Andersson

      好吧,Opera(以及之前的 Safari 和 Google Chrome)最近一直声称自己是速度最快的。 我猜想这是基于最流行的基准测试的平均值,所以是的。

      我在自己的开发中没有使用太多 JavaScript,也不太熟悉现有的基准测试,但是如果要我列举我所知道的并且似乎目前很重要的基准测试,那就是 Sunspider、Dromaeo 和 V8 基准测试套件。 我还知道另一个基准测试,但现在记不起名字了,它也被提到了。

      2010 年 3 月 8 日 下午 4:02

  4. Drazick

    我们可以由此得出结论,下一个 Firefox 的 JS 性能将至少与 Safari 一样好?

    感谢您出色的工作。

    2010 年 3 月 8 日 下午 4:01

  5. 匿名

    @Boris:您能提供一些例子吗? 因为 Christopher 早些时候也提到了某些优越的测试,但没有提供任何证据 :(
    附注:验证码:“reproves matters”——很有趣 :)

    2010 年 3 月 8 日 下午 4:04

  6. Boris

    Magne,这些说法很大程度上是基于一个特定的基准测试:Sunspider(以及在 Chrome 的情况下,某种程度上的 V8)。

    Dromaeo JS 基准测试的结构使得其分数会紧密跟踪 Sunspider(约占分数的 60%),V8 套件的输入占比次之(约占 30-35%)。 特定 Dromaeo 部分的子分数是相关的,但总分数并不会为您提供任何真正的新信息。

    所以为了回答您的问题,这项工作的主要目标是避免像现在这样出现任何病态缓慢的情况。 预计这还将使我们能够在 Sunspider 上做得更好(考虑到它在我们的 js 引擎中的工作负载与其他引擎不同,以及它从根本上有利于预先编译而不是跟踪,因为它是如何进行计时的方式)。 这在实践中意味着什么,我们拭目以待。

    2010 年 3 月 8 日 下午 4:13

    1. Magne Andersson

      我明白了。 但是,您能提供一些“现实世界”中的例子吗? 在这些例子中,我们可以看到速度的提升? 也就是说,在将从 JägerMonkey 项目中获益的常规互联网应用程序中。

      2010 年 3 月 8 日 下午 5:04

  7. Slobo

    我怀疑这不会对 DOM 操作速度产生太大影响。 目前,WebKit 在我正在开发的 Web 应用程序中大幅领先 Gecko(足以让我考虑解决 Gecko 的一些特定问题,并向我们的 intranet 用户推荐 Chromium)。

    在 Firefox 中监控 javascript DOM 操作性能开发进度的最佳位置在哪里? 谢谢

    2010 年 3 月 8 日 下午 4:21

  8. Boris

    我怀疑http://www.apejet.org/aaron/blog/2008/10/04/browser-benchmarking-using-go-game/(这是一个基准测试,但基于实际的围棋 AI)将受益。 同样,人们似乎想用 JavaScript 编写的各种模拟器也将受益。 总的来说,最受益的是高度分支的代码。

    2010 年 3 月 8 日 下午 5:56

  9. Paul

    抱歉,我仍然没有完全明白。 我明白——Nanojit 接受高级内容,进行优化并生成本机代码,而——Nitro 只生成本机代码。

    所以,您有两个编译器在生成本机代码,对吗? 这是因为 Nanojit 和 Nitro 在 javascript 的不同领域生成最佳代码吗?

    2010 年 3 月 8 日 晚上 8:34

    1. Richard Barrell

      Nitro 和 Nanojit 都生成原生代码。你可以这样理解:Nitro 是一个快速的代码生成器,它生成中等水平(比较慢)的原生代码。Nanojit 是一个慢速的代码生成器,它生成高质量(更快)的原生代码。

      据我了解,其理念是在所有 JavaScript 代码上运行 Nitro,因为它成本低廉,因为 Nitro 生成的原生代码运行速度会比解释字节码快。然后,跟踪引擎会介入这些代码中的热点,并更积极地对其进行优化。因此,你可以获得所有 JavaScript 代码的运行速度适中、生成成本低的代码,并且你可以投入 CPU 时间来获得一小部分代码(占用了大部分运行时间)的极快速度。

      2010 年 7 月 15 日 上午 3:26

  10. jpvincent

    了解事物在幕后是如何运作的总是很有趣。

    然而,作为一名 Web 开发人员,了解如何利用这种演变(如果有的话)会更有意思,我的意思是,例如,我们应该在哪些地方看到改进。

    2010 年 3 月 9 日 上午 2:07

  11. Mark Lee Smith

    Boris

    请原谅我的直言不讳,但这对你来说是不是很方便,因为你可以这样说(我在这里改述一下):"我们已经拥有最快的 JavaScript 引擎","只是其他人使用的基准测试显示我们是最慢的","这里有一个地方我们可以领先"。

    证据呢?

    抱歉,但这对我来说并不令人信服。

    2010 年 3 月 9 日 上午 2:23

  12. chris

    所以让我确认一下

    现在
    SpiderMonkey 解释 -> 跟踪优化 -> Nanojit 编译

    有了 JägerMonkey

    跟踪优化 -> Nitro 编译

    所以基本上,SpiderMonkey 解释器已经死了?因为根本不需要解释器了?

    这个改变是否让 Mozilla JS 执行与 WebKit JS 相同的功能?我有点糊涂了,如果是这样,为什么不直接使用 WebKit 的 JS 引擎?

    2010 年 3 月 9 日 上午 7:53

  13. Juha

    现在这个目标仅仅是针对 x86,还是已经针对 ARM 进行了代码优化?

    2010 年 3 月 9 日 上午 8:29

  14. Tristan

    Chris,信息丰富,非常有帮助。这是个好工作,我知道让复杂的东西变得易懂有多难 :-)

    2010 年 3 月 9 日 上午 11:14

  15. Boris

    Slobo,你说的没错,这对大多数 DOM 操作影响很小。也就是说,大多数 "DOM" 问题实际上并不是 DOM 问题,而是布局问题。你是否在你的 Web 应用中提交了有关性能问题的错误报告?如果没有,你可以提交一个错误报告(并 CC “:bz”)或者直接通过 bz at mit dot edu 告诉我如何重现问题?

    Paul,Nanojit 做了很多在使用 Nitro 汇编器的简单代码生成路径中无法轻松完成的优化。所以是的,将有两个不同的原生代码生成路径,它们将在不同的情况下使用。JM 代码路径(使用 Nitro 汇编器进行代码生成)用于基线代码生成,Nanojit 用于热点。

    Mark Lee Smith,我什么时候说过我们拥有最快的 JS 引擎?我们有一个 JS 引擎,在某些情况下非常快,而在其他情况下则不那么快。我们在其他浏览器开发者创建并针对其引擎进行优化的基准测试中速度较慢(有时在向世界发布基准测试之前要花费几年的时间来优化)。我们在许多现实情况下速度较慢(这就是 JM 项目正在进行的原因)。我们在许多其他现实情况下速度要快得多。例如,我尝试过的通过画布(图像过滤器等)进行的大多数像素操作在 Gecko 中速度都要快得多,尤其是在 Vlad 对画布图像数据进行了补丁,使其不再占用 4 倍于所需内存的情况下。一般来说,我们在数学密集型基准测试中速度更快,只要代码的分支不是太多。这显然是一个很大的假设,这就是为什么我们要开发 JM 的原因。

    Chris,新的设置有两个独立的编译步骤。首先使用我们正在编写的编译器并使用 Nitro 汇编器进行代码生成进行编译。然后对热点进行跟踪和 Nanojit 重新编译。SpiderMonkey 解释器还没有死,但目标是努力消灭它,是的。我不确定你的倒数第二个问题是什么意思。为了回答你的最后一个问题,WebKit 的 JS 引擎不具备 SpiderMonkey 目前拥有的许多功能(例如,它必须在一个线程上运行,它不支持 SpiderMonkey 支持的所有功能等)。

    Juha,Nitro 汇编器有一个 ARM 后端,所以它在 ARM 上的运行效果应该与在 x86 上一样好。

    2010 年 3 月 9 日 下午 2:47

  16. Boris

    Mark Lee Smith,以防我理解错了,你只是想知道 TM 在至少在我的硬件上比 JSC 或 V8 或两者都快的例子,那就试试这些:

    http://dromaeo.com/?dromaeo-object-string
    http://dromaeo.com/?dromaeo-object-array
    http://web.mit.edu/bzbarsky/www/mandelbrot-clean.html
    http://www.galbraiths.org/benchmarks/pixastic.html(尤其是在包含我上面提到的 Vlad 补丁的版本中)

    我最近还看到有人在 Google 表格中计时,发现它在 Gecko 中比在 Chrome 或 Safari 中快得多,但这其中可能会有各种噪声(DOM、布局、不同的代码路径)...

    2010 年 3 月 9 日 下午 2:58

  17. njn

    Chris 说

    "Nanojit 做两件事:它接受 JavaScript 的高级表示并进行优化。它还包括一个汇编器,可以将该优化后的表示转换为机器级执行的原生代码。"

    不完全是... Nanojit 处理的是一个相当低级的中间表示,称为 LIR,它有点像抽象汇编语言。跟踪编译器负责将 JS 字节码转换为 LIR。Nanojit 的工作是优化 LIR,然后生成原生代码。

    2010 年 3 月 9 日 下午 7:45

  18. bob

    很高兴看到在保持跟踪猴子工作的同时进行这样的计划。
    我希望你们能尽快实现目标(如你所知,人们受 "酷" 因子的驱使)

    2010 年 3 月 10 日 上午 5:05

  19. Junebug

    哇。感谢上帝浏览器开发者不尊重专利。乔布斯在谈论知识产权时说得对。他们有一些非常聪明的想法。

    Mozilla,来吧,你的创造力在哪里?你们在实施方面做得很好,我尊重这一点,但你们上一次用什么震撼世界?在技术方面?你们在让人们协同工作方面做得很好,但苹果却让我大开眼界。专注的开发有其优点。

    2010 年 3 月 10 日 上午 8:31

  20. Dan

    Boris,我认为 Chris 的问题中你可能没有完全理解的部分是:现在在 Web 浏览器中将会有一个 JS 引擎吗?就功能、怪癖和行为而言。这是我理解这个问题的方式。我确实认为用 WebKit 的一部分替换浏览器的一部分会降低浏览器之间的良性竞争,但我认为这只是整个 JS 系统中的一小部分。

    2010 年 3 月 10 日 下午 6:44

  21. Andrew

    Boris,

    我有一个关于 TraceMonkey 的问题:Nanojit 或 Nitro 是否会进行诸如死代码消除和反混淆之类的优化?还是它们之前是你的责任?

    2010 年 3 月 10 日 下午 6:47

  22. Oskar

    不错!期待看到 JägerMonkey 带来的改进。
    希望它能在今年(Firefox 4.0)发布。
    Firefox 很快,对我来说,有了 Direct2D,它是使用过的最快的浏览器。(尝试过所有最新的浏览器)
    继续努力!:)

    2010 年 3 月 10 日 下午 7:22

  23. Boris

    Dan,JS 引擎的数量将与以前一样多。从短期来看,Mozilla JS 引擎将包含三个部分:解释器、方法 JIT、跟踪 JIT。从长远来看,至少会有方法 JIT 和跟踪 JIT;解释器是否保留尚不清楚。

    是的,从 WebKit 中使用的那一部分只是整个 JS 引擎中非常小的一部分。

    2010 年 3 月 10 日 下午 8:14

  24. Erik Harrison

    如果有任何坐在电脑前敲键盘的黑客仍在阅读这里的评论,那么偶然看到提交日志和一些 Bugzilla 票据后,它看起来像是可以构建一个方法 JIT/跟踪 JIT Firefox 的。确实可以,所以我做了。

    以下是我在运行 Xubuntu 的超级小巧的 Dell Latitude 上的一些 Sunspider 数据

    Firefox 3.6:88.14 次/秒
    (http://dromaeo.com/?id=96279)

    方法 JIT Firefox:36.55 次/秒
    (http://dromaeo.com/?id=96271)

    方法 JIT Firefox + 跟踪:126.10 次/秒
    (http://dromaeo.com/?id=96272)

    对于那些在家玩的人来说,这是一个 50% 的提升,尽管我确信还有很多我不知道的注意事项。

    如果你想参加与其他浏览器的速度比拼。

    Chrome 不稳定版:288.11 次/秒
    http://dromaeo.com/?id=96273

    是的,V8 仍然表现出色,但如果你查看单个基准测试,你会发现 Firefox 在许多任务中速度更快。这个方法 JIT 似乎保留了这种优势,同时平滑了其他方面的曲线。这真是太棒了,我期待将来能够看到更多的好东西。感谢大家!

    2010 年 3 月 10 日 下午 11:17

  25. Brandon jones

    如果我理解错了,请纠正我,但开源的全部意义不就在于能够出于任何原因使用来自任何地方的代码吗?为什么要重新发明轮子,仅仅因为轮子是由竞争对手制造的?使用你能用到的东西并根据自己的需要进行调整比从头开始做所有事情更有意义。

    2010 年 3 月 13 日 下午 3:17

  26. njn

    我有一个关于 TraceMonkey 的问题:Nanojit 或 Nitro 是否会进行诸如死代码消除和反混淆之类的优化?还是它们之前是你的责任?

    Nanojit 会进行死代码消除、CSE、一些常量折叠和一些死存储消除。我不知道什么是反混淆。

    2010 年 3 月 14 日 下午 9:32

  27. Boris

    Andrew,Nanojit 肯定会进行死代码消除。我不确定它还会进行哪些其他优化,但有一些。

    Nitro 汇编器只是一个汇编器。据我了解,它根本不进行任何优化。我不确定在这种设置中寄存器分配是如何工作的;那些一直在使用它的人可能更了解它。

    2010 年 3 月 15 日 上午 6:33

  28. [...] 是复杂的东西,很难做到正确。所以 Chris Blizzard 写了一篇文章,纠正了一些误解。我认为,从主要系统图中更容易看到我们正在做的事情 [...]

    2010 年 3 月 15 日 下午 5:30

  29. Stifu

    @Brandon jones
    “如果我没记错,开源的意义不正是能够从任何地方获取代码,并根据自己的意愿使用吗?(…)”

    是的,这就是他们从 Webkit 中借用一些部分来构建 JägerMonkey 的原因。对于其余部分,他们有一些尚未探索的想法(使用跟踪 JIT 和方法 JIT,而不是只使用其中之一),这就是他们继续走自己的路的原因。这有什么不对吗?
    他们既没有重新发明轮子,也没有忽视竞争对手提供的开源代码,他们只是从其他人那里挑选出好的想法,以创造出有潜力比目前任何东西都更好的东西。我认为这没什么可抱怨的。

    2010 年 3 月 16 日 上午 7:04

  30. 论文

    这确实是一篇好文章。据我了解,其想法是在所有 JavaScript 代码上运行 Nitro,因为它非常便宜,因为 Nitro 生成的原生代码运行速度会比解释字节码快一些。然后,跟踪引擎会介入到这些代码的热点区域,并对其进行更激进的优化。这样一来,您就可以为所有 JavaScript 代码获得速度合理的、易于生成的代码,并且可以投入系统资源时间来获取少数几个占用大部分时间的代码段的极快代码。谢谢,Steve

    2011 年 6 月 12 日 上午 2:43

本文的评论已关闭。