V8的Turbofan的性能特点将如何对我们优化的方式产生影响
审阅:来自V8团队的Franziska Hinkelmann和Benedikt Meurer.
**更新:Node.js 8.3.0已经发布了V8 6.0和Turbofan.
Node.js依靠V8 JavaScript引擎来运行代码,其语言本身也是我们熟悉和喜爱的。V8 JavaScript引擎是Google为Chrome浏览器编写的JavaScript虚拟机。从一开始,V8的一个主要目标是让JavaScript运行地更快,或者至少比竞争对手更快。而对于一个高动态的松散类型的语言来说,这并不容易。本文介绍了有关V8和JS引擎性能的演变。
JIT(Just In Time)编译器是V8引擎的核心部分,它允许高速执行JavaSctipt代码。它是一个动态编译器,可以在运行时对代码进行优化。在一开始的V8引擎中JIT编译器被称为FullCodegen,后来V8团队实现了Crankshaft,其中包含了很多在FullCodegen中没有实现的性能优化。
修正:FullCodegen是V8引擎的第一个优化编译器,感谢Yang Guo提供。
作为JavaScript的局外人和用户,从90年代开始,似乎JavaSciprt中的快慢路径(无论何种引擎)看起来都违背常理,而JavaScript代码很慢的原因通常也难以理解。
近几年,Matteo Collina和我一直关注如何编写高性能的Node.js代码,这意味着我们必须知道在用V8 JavaScript引擎运行代码时哪些方法要快哪些方法要慢。
现在是时候挑战这些有关性能方面的假设了,因为V8团队已经编写了一个新的JIT编译器:Turbofan.
从众所周知的“V8杀手”(一段会导致optimazation bail-out的代码——该术语在Turbofan中已经没有意义)开始,以及Matteo和我围绕Crankshaft性能方面的一些发现,我们将对V8版本的进展进行一系列的观察并给出微基准测试结果。
当然,在进行V8的逻辑路径优化之前,我们应该首先关注API设计,算法和数据结构。这些微基准测试用来标识JavaScript在Node中的执行过程如何被改变。我们可以使用这些指示器来改变我们的代码风格以及在应用优化之后提高性能的方式。
我们将在V8的5.1,5.8,5.9,6.0和6.1版本上查看微基准测试的性能。
我们将把每个不同的版本放到对应的环境中:V8 5.1引擎使用Node 6和Crankshaft JIT编译器,V8 5.8使用Node 8.0和8.2并混合使用Crankshaft和Turbofan。
当前的6.0引擎属于Node 8.3(或者可能是Node 8.4),而V8的6.1是最新版(在编写本文时),它被集成到Node中,可以查看实验中的node-v8 repo。也就是说,V8 6.1版本最终将会出现在未来的Node版本中,有可能是Node.js 9。
我们来看看微基准测试,而另一方面我们也将讨论这些微基准测试对未来都意味着什么。所有的这些微基准测试都是通过benchmark.js来执行的,并且数值都是按秒绘制的,因此值越高越好。
try/catch的问题其中一个比较著名的去优化模式是使用try/catch块。
在这个微基准测试中,我们比较了以下四种情况:
有try/catch的function(sum try catch)
没有try/catch的function(sum without try catch)
在try块中调用function(sum wrapped)
简单调用一个function,没有try/catch(sum function)
代码:https://github.com/davidmarkclements/v8-perf/blob/master/bench/try-catch.js
'use strict' var benchmark = require('benchmark') var suite = new benchmark.Suite() function sum (base, max) { var total = 0 for (var i = base; i < max; i++) { total += i } } suite.add('sum with try catch', function sumTryCatch () { try { var base = 0 var max = 65535 var total = 0 for (var i = base; i < max; i++) { total += i } } catch (err) { console.log(err.message) } }) suite.add('sum without try catch', function noTryCatch () { var base = 0 var max = 65535 var total = 0 for (var i = base; i < max; i++) { total += i } }) suite.add('sum wrapped', function wrapped () { var base = 0 var max = 65535 try { sum(base, max) } catch (err) { console.log(err.message) } }) suite.add('sum function', function func () { var base = 0 var max = 65535 sum(base, max) }) suite.on('complete', require('./print')) suite.run()