我现在对 JavaScript 引擎感到困惑。我知道V8很重要,因为它将 JavaScript 编译为本机代码。
然后我开始阅读有关Mozilla SpiderMonkey的信息,据我了解,它是用 C 语言编写的,可以编译 JavaScript。那么这与 V8 有何不同,如果这是真的,为什么 Firefox 不这样做呢?
最后,Rhino是否真的将 JavaScript 编译为 Java 字节码,以便您获得 Java 的所有速度优势?如果不是,为什么人们在桌面上编写脚本时不运行 V8?
我现在对 JavaScript 引擎感到困惑。我知道V8很重要,因为它将 JavaScript 编译为本机代码。
然后我开始阅读有关Mozilla SpiderMonkey的信息,据我了解,它是用 C 语言编写的,可以编译 JavaScript。那么这与 V8 有何不同,如果这是真的,为什么 Firefox 不这样做呢?
最后,Rhino是否真的将 JavaScript 编译为 Java 字节码,以便您获得 Java 的所有速度优势?如果不是,为什么人们在桌面上编写脚本时不运行 V8?
JavaScript 执行有多种方法,即使在执行 JIT 时也是如此。V8 和 Nitro(以前称为 SquirrelFish Extreme)选择执行全方法 JIT,这意味着它们在遇到脚本时将所有 JavaScript 代码编译为原生指令,然后像编译的 C 代码一样简单地执行它。SpiderMonkey 改为使用“跟踪”JIT,它首先将脚本编译为字节码并对其进行解释,但会监视执行,寻找循环等“热点”。当它检测到一个,然后它只编译那个热路径到机器代码并在将来执行它。
这两种方法都有优点和缺点。全方法 JIT 确保所有执行的 JavaScript 都将作为机器代码编译和运行,而不是解释,这通常应该更快。但是,根据实现的不同,这可能意味着引擎花费时间编译永远不会执行的代码,或者可能只执行一次,并且对性能不是很重要。此外,这个编译后的代码必须存储在内存中,因此会导致更高的内存使用率。
与整体方法 JIT 相比,在 SpiderMonkey 中实现的跟踪 JIT 可以生成极其专业的代码,因为它已经执行了代码并且可以推测变量的类型(例如将 for 循环中的索引变量视为本机整数),其中整体方法 JIT 必须将变量视为对象,因为 JavaScript 是无类型的并且类型可能会改变(如果假设失败,SpiderMonkey 将简单地“脱落”跟踪,并返回解释字节码)。但是,SpiderMonkey 的跟踪 JIT 目前在具有许多分支的代码上不能有效地工作,因为跟踪针对单个执行路径进行了优化。此外,在决定编译跟踪之前监视执行会涉及一些开销,然后将执行切换到该跟踪。还,
V8 是最快的,因为它将所有 JS 编译为机器码。
SpiderMonkey(FF 使用的)也很快,但编译为中间字节码,而不是机器码。这是与 V8 的主要区别。编辑 - 较新的 Firefox 版本带有较新的 SpideMonkey 变体;追踪猴子。TraceMonkey 对关键部分进行 JIT 编译,也许还有其他智能优化。
Rhino 将 Javascript 编译成 Java 类,因此您基本上可以用 Javascript 编写“Java”应用程序。Rhino 也被用作在后端解释 JS 并对其进行操作的一种方式,并具有完整的代码理解,例如反射。例如,YUI Compressor 使用它。
之所以到处使用 Rhino 而不是 V8,可能是因为 V8 比较新,所以很多项目已经在使用 Rhino/Spidermonkey 作为他们的 JS 引擎,例如 Yahoo 小部件。(我假设这就是您所说的“桌面上的脚本”)
编辑- 这个链接也可能让我们深入了解为什么 SpiderMonkey 被如此广泛地采用。 你会在你的应用程序中嵌入哪个 Javascript 引擎?
如果您想查看浏览器内的各种 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 相比略胜一筹。至少它不会像 Firefox 3 SpiderMonkey(没有 JIT)那样比 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 个解释器层(interpreter-on-interpreter)或解释器^2。而您的大多数其他 Javascript 引擎都是用 C 编写的,因此应该更像解释器^1。与 C 或 C++(例如,参考 Perl 或 Python 或 Ruby)相比,每个解释器层可以增加 5-10 倍的性能下降,但使用 JIT,性能损失可能会低得多,大约为 2-4 倍。JVM 拥有有史以来最强大和最成熟的 JIT 引擎之一。
所以你的里程肯定会有所不同,如果你想在你自己的硬件和操作系统上为你的预期应用程序提供真正的答案,你可能会从做一些严肃的基准测试中受益。
Rhino 不能太慢,因为我知道很多人都在使用它。我认为它的主要吸引力不是它的速度,而是易于编码/轻量级/可嵌入/解释器的事实,它与 Java 库挂钩,这使得它非常适合您的软件项目的脚本/配置/可扩展性。UltraEdit 等一些文本编辑器甚至嵌入了 Javascript 作为替代宏脚本引擎。每个程序员似乎都能很容易地偶然发现 javascript,因此也很容易上手。
Rhino 的一个优势是它几乎可以在 JVM 运行的任何地方运行。以我的经验,尝试让独立的 TraceMonkey 或 SpiderMonkey 从命令行构建和运行在 Windows 等系统上可能会有点痛苦。并且嵌入到您自己的应用程序中可能会更加耗时。但是对于一个大型项目来说,拥有一种可嵌入语言的回报是值得的,而不是如果你想要这样做的话,就必须“推出你自己的”迷你脚本解决方案。
如果你有 Java 和 rhino jar,用 Rhino 编写脚本真的很容易,你只需编写你的 javascript 并从命令行运行它。我一直用它来完成简单的任务。
为了回答这个问题,为什么本机代码与字节代码......
本机代码更快,对于谷歌来说是一个战略选择,因为他们计划 JS 至少其中之一是 ChromeOS。
关于这个问题的一个很好的视频发布在 Channel9 上,对 V8 背后的人 Lars Bak 的采访可以在这里找到