JavaScript引擎的优势

71

我现在对JavaScript引擎感到困惑。我知道V8非常重要,因为它将JavaScript编译为本地代码。

然后我开始阅读关于Mozilla SpiderMonkey的文章,据我所知,它是用C编写的,并且可以编译JavaScript。那么这与V8有什么不同,如果是这样的话,为什么Firefox不这样做呢?

最后,Rhino是否真正将JavaScript编译为Java字节码,以便您能获得Java的所有速度优势? 如果不是,为什么人们不在桌面上运行V8来编写脚本?


1
Rhino可以编译成Java字节码。https://developer.mozilla.org/en/Rhino_JavaScript_Compiler - Thilo
你是一个先锋,你的思想推动了今天我们在桌面上编写脚本。https://github.com/stealify 甚至将其作为内核的环形0使用,所有Linux桌面环境都在使用JS。 - frank-dspeed
5个回答

99

在执行 JavaScript 时有各种方法,即使在进行 JIT 时也是如此。V8 和 Nitro (之前称为 SquirrelFish Extreme) 选择使用整体方法 JIT,这意味着它们在遇到脚本时会将所有 JavaScript 代码编译成本地指令,然后简单地执行它,就像执行已编译的 C 代码一样。而 SpiderMonkey 则采用“跟踪”JIT,首先将脚本编译为字节码并解释执行,但监视执行,寻找“热点”(如循环)。当检测到一个热点时,它会将该热路径编译为机器码,并在将来执行。

这两种方法都有优点和缺点。整体方法 JIT 确保所有执行的 JavaScript 都将被编译为机器代码而不是解释,通常应该更快。但是,根据实现,这可能意味着引擎花费时间编译永远不会被执行或只执行一次的代码,且性能不关键。此外,必须将编译后的代码存储在内存中,因此这可能导致更高的内存使用率。

SpiderMonkey 中实现的跟踪 JIT 可以与整体方法 JIT 相比产生极其专业化的代码,因为它已经执行了代码并可以推测变量的类型(例如将 for 循环中的索引变量视为本机整数),而整体方法 JIT 必须将变量视为对象,因为 JavaScript 是无类型的,类型可能会更改 (如果假设失败,SpiderMonkey 将简单地“跳出”跟踪并返回解释字节码)。然而,SpiderMonkey 的跟踪 JIT 目前在具有许多分支的代码上效率不高,因为跟踪是针对单一执行路径进行优化的。此外,在决定编译跟踪之前,监视执行需要一些开销,并且要切换到该跟踪的执行也会带来一些开销。此外,如果追踪程序做出后来被违反的假设(如变量更改类型),则从跟踪中退出并切换回解释的成本很可能比使用整体方法 JIT 更高。


21
请注意,Firefox在这些年中也改用了一种名为“JaegerMonkey”的整方法JIT,并放弃了追踪JIT。 - Ted Mielczarek

18

V8是最快的,因为它将所有JS编译成机器码。

SpiderMonkey(FF使用的引擎)也很快,但它编译为中间字节码而不是机器码。这是与V8的主要区别。编辑 - 较新的Firefox版本配备了较新的SpideMonkey变体; TraceMonkey。TraceMonkey对关键部分进行JIT编译,可能还有其他智能优化。

Rhino将Javascript编译成Java类,从而允许您基本上用Javascript编写“Java”应用程序。Rhino还用作在后端解释JS并操作它以及具有完全代码理解(如反射)的一种方式。例如,YUI压缩器就是使用它。

之所以在大多数地方使用Rhino而不是V8,可能是因为V8相对较新,因此许多项目已经在使用Rhino / Spidermonkey作为其JS引擎,例如Yahoo小部件。 (我假设这就是您所指的“桌面上的脚本”)

编辑-此链接还可以提供一些有关为什么如此广泛采用SpiderMonkey的见解。 Which Javascript engine would you embed in your application?


2
Umm TraceMonkey 还会将代码 JIT 编译成机器码...而且我认为说 V8 将 JavaScript "编译" 成机器码并不准确 - 它更像是 TraceMonkey 的 JIT 方法。 - Pointy
@Pointy,据我所知TraceMonkey和V8之间的区别在于TraceMonkey编译为中间代码,其中一些代码在执行时被JIT编译为机器代码。而V8直接将所有内容编译为机器代码。 - Matthew Crumley
1
V8 在第一次执行 JavaScript 源代码时直接将其编译为机器码。没有中间字节码,也没有解释器。基本上就像 C 编译器所做的那样进行编译。此外,V8 对所有 JS 进行编译,而 TraceMonkey 则进行即时编译。 - adamJLev
我知道这很老,但是来自Webkit的JavaScriptCore目前是最快的。 - matronator

5
如果您想查看各种浏览器内置的JavaScript引擎的性能如何,可以安装Safari 4(是的,现在也可以在Windows上运行!),Chrome V8,Firefox 3.5和IE 8(如果您使用Windows)并运行基准测试:http://www2.webkit.org/perf/sunspider-0.9/sunspider.html。我认为正如Pointy所说,新的Firefox 3.5使用TraceMonkey,它也会实时编译为中间代码,使用某种形式的JIT。因此,它应该与V8相比较有利。至少它不会像没有JIT的Firefox 3 SpiderMonkey那样比V8慢10倍。对于我来说... Safari 4.0.3在Win XP上比Firefox 3.5.3中的Tracemonky快2.5倍。IE8要慢得多。我目前没有安装Chrome。关于Rhino是否编译为Java字节码,我不确定。如果它仍然解释JavaScript的动态特性,例如能够在运行时向对象实例添加属性(例如obj.someNewAttribute =“someValue”在JavaScript中是允许的),那么可能不完全是“编译”为字节码,并且除了您不必每次运行JavaScript时都从JavaScript源代码文本编译之外,您可能不会获得更好的性能。请记住,JavaScript允许非常动态的语法,例如eval(“x = 10; y = 20; z = x * y”)这意味着您可以构建在运行时编译的代码字符串。这就是为什么我认为即使您编译为JVM字节码,Rhino也将是混合模式解释/编译的原因。JVM仍然是一个解释器,尽管它具有JIT支持的很好。因此,我喜欢将Rhino-on-JVM视为2个解释器层(解释器-on-解释器)或解释器^2。而大多数其他JavaScript引擎都是用C编写的,因此应该表现得更像解释器^1。每个解释器层可以添加5-10倍的性能降低,与C或C ++相比(例如Perl、Python或Ruby),但使用JIT,性能损失可以降低到2-4倍。而JVM是有史以来最强大和成熟的JIT引擎之一。因此,您的效果肯定会有所不同,如果您想要在自己的硬件和操作系统上进行真正的应用程序测试,那么您可能会从中受益。Rhino不能太慢,因为我知道很多人正在使用它。我认为它的主要吸引力不在于速度,而在于易于编码/轻量级/可嵌入/解释器,具有Java库的钩子,非常适合脚本化/配置/扩展您的软件项目。一些文本编辑器(如UltraEdit)甚至将JavaScript作为替代宏脚本引擎进行嵌入。每个程序员似乎都能很容易地使用JavaScript,因此也很容易掌握。
Rhino的一个优势是它几乎可以在JVM运行的任何地方运行。在我的经验中,尝试从命令行构建和运行独立的TraceMonkey或SpiderMonkey在Windows等系统上可能会有些困难。而嵌入到您自己的应用程序中可能需要更多的时间。但是,如果您正在进行大型项目并且需要嵌入式语言,则回报将比制作自己的小型脚本解决方案要好得多。

如果您拥有Java和rhino jar,使用Rhino编写脚本非常容易,您只需编写JavaScript并从命令行运行即可。我经常用它来完成简单的任务。


我在我的XP机器上安装了Chrome 4,并且它的Sunspider基准测试跑得比Firefox 3.5.3 Tracemonkey快了约3倍。我还发现相比于我的先前对SpiderMonkey的经验,V8很容易下载和构建。当然,您需要svn + python 2.4 + scons 1.0.0 + Visual Studio 2005/2008(VC ++ 2008免费版也可以)这些我已经在我的开发人员PC上运行。为了公平起见,也许我会回去再尝试一下TraceMonkey构建,看看它现在表现如何。 - linguanerd
3
请注意,Sunspider 不是唯一的答案,它只是一个 JavaScript 基准测试(尽管 JavaScript 引擎作者已经对其进行了大量优化)。 - Ted Mielczarek
VC++ 2008 express(免费版)可以用于使用scons编译v8,我在本周早些时候做过。 - Fire Crow

3
为了回答这个问题,为什么本地代码比字节码更快... 本地代码更快,对于谷歌来说是一种战略选择,因为他们计划将JS应用到至少一个ChromeOS中。有一个很好的视频关于这个问题发布在Channel9上,其中采访了Lars Bak,他是V8背后的人,可以在这里找到:这里

1

2022 更新

V8 和 SpiderMonkey 都支持多通道编译和单通道编译。

Java Rhino 已经被弃用,并被一个名为 Truffle Framework 的新概念所取代,它是 GraalVM Stack 的一部分,实际上是添加到 JVM 中的 JVMCI,其中 CI 表示 Compiler Interface。这允许 Truffle 语言实现框架在 MultiPass 和 Single Pass 中实现任何语言并将其转换为 Java 字节码。ECMAScript 实现称为 GraalJS,有两种风味。

GraalJS 是 1:1 Rhino 替代品,包括增量采用的兼容模式和 graal-node,其中 v8 被 GraalJS 引擎替换的 NodeJS Fork。

正如所说,Truffle 是一个 Polyglot 语言实现框架,因此可以使用任何 GraalVM 支持的语言。

希望这有所帮助并且有些意义,因为当整个堆栈具有对齐版本时,jar 版本才能发挥最佳作用,您应该始终安装完整的堆栈:

https://github.com/graalvm/graalvm-ce-builds/releases

下载并安装,然后使用所谓的gu命令添加GraalJS,请按照此处的说明操作:https://github.com/oracle/graaljs

是的,GraalVM通常可以编译所有Polyglot语言的单个二进制可执行文件,这称为AOT编译,它具有更快的启动时间但吞吐量较低,它还可以进行JIT,并且可以混合使用两者。


Rhino 何时被弃用了?我在 https://github.com/mozilla/rhino 上找不到相关信息。 - Rubén
哦,它并不是官方的,但Graal有一个兼容模式专门用于这种情况。没有人强制执行过期,但使用它时比Graal慢40倍毫无意义。此外,Graal提供了所有其他集成,但由于它是Java,它将在未来20年内存在,但用户群体将会减少。 - frank-dspeed
谢谢您的回复。您是否与GraalJS / GraalVM的开发有所涉及? - Rubén
我是GraalVM / GraalJS / graal-node的贡献者,没错,但我也是许多JS引擎(如来自Chromium的V8)的语言实现者。总体而言,我致力于整个Web平台和可使用Javascript脚本的操作系统的工作。 - frank-dspeed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接