V8 引擎和 JavaScript 优化技巧

V8是谷歌编译我们的JavaScript的引擎。Firefox有自己的引擎叫做SpiderMonkey,它与V8非常相似,但存在差异。

关于V8发动机的一些事实:

JavaScript 旅程

那么,当我们发送我们的JavaScript来通过V8引擎进行审查时,会发生什么(这是在它被缩小,恶化和你对JavaScript代码做任何其他疯狂的事情之后)?

我创建了以下图表,显示了所有步骤,然后我们将详细讨论每个步骤:

The JavaScript Journey diagram

在本文中,我们将讨论 JavaScript 代码是如何被解析的,以及如何将您的 JavaScript 尽可能多地转移到优化编译器中。 优化编译器(也称为 Turbofan)将我们的 JavaScript 代码转换为高性能机器代码,所以我们可以给出的代码越多,我们的应用程序就越快。

使用 JavaScript

因此,我们 JavaScript 代码的第一个处理是对其进行解析,让我们仔细讨论解析是什么。

有两个阶段进行分析,这些阶段是:

  • Eager (full-parse) - 它立即解析每个行
  • Lazy (pre-parse) - 做最低限度,解析我们需要的内容,然后留下剩下的时间

哪个更好?一切都取决于。

让我们来看看一些代码。

 1// eager parse declarations right away
 2const a = 1;
 3const b = 2;
 4
 5// lazily parse this as we don't need it right away
 6function add(a, b) {
 7  return a + b;
 8}
 9
10// oh looks like we do need add so lets go back and parse it
11add(a, b);

因此,在这里,我们的变量声明将是渴望解析,但然后我们的函数是轻松解析

要立即渴望分析``添加函数,我们可以做到:

 1// eager parse declarations right away
 2const a = 1;
 3const b = 2;
 4
 5// eager parse this too
 6var add = (function(a, b) {
 7  return a + b;
 8})();
 9
10// we can use this right away as we have eager parsed
11// already
12add(a, b);

这就是你使用的大多数模块是如何创建的。

内置功能

Chrome有时基本上会重写您的JavaScript,其中一个例子是插入正在使用的函数。

让我们以以下代码为例子:

 1const square = (x) => { return x * x }
 2
 3const callFunction100Times = (func) => {
 4  for(let i = 0; i < 100; i++) {
 5    // the func param will be called 100 times
 6    func(2)
 7  }
 8}
 9
10callFunction100Times(square)

上面的代码将由V8引擎如下优化:

 1const square = (x) => { return x * x }
 2
 3const callFunction100Times = (func) => {
 4  for(let i = 100; i < 100; i++) {
 5    // the function is inlined so we don't have 
 6    // to keep calling func
 7    return x * x
 8  }
 9}
10
11callFunction100Times(square)

正如您从上面所看到的,V8基本上正在删除我们称之为func的步骤,而不是square的体格。

函数 inlining gotcha

有了这种方法,有一点 gotcha,让我们以以下代码为例:

 1const square = (x) => { return x * x }
 2const cube = (x) => { return x * x * x }
 3
 4const callFunction100Times = (func) => {
 5  for(let i = 100; i < 100; i++) {
 6    // the function is inlined so we don't have 
 7    // to keep calling func
 8    func(2)
 9  }
10}
11
12callFunction100Times(square)
13callFunction100Times(cube)

因此,这一次我们已经调用了平方函数100次,然后我们将调用平方函数100次。在平方函数可以调用之前,我们必须首先消除callFunction100Times的优化,因为我们已经调用了平方函数体。

物体

至于对象,V8在帽子下有一个类型系统来区分你的对象:

垄断主义

对象具有相同的密钥,没有差异。

1// mono example
2const person = { name: 'John' }
3const person2 = { name: 'Paul' }

多元化

对象具有相似的结构,但存在一些小差异。

1// poly example
2const person = { name: 'John' }
3const person2 = { name: 'Paul', age: 27 }

宏观主义

物体是完全不同的,不能比较。

1// mega example
2const person = { name: 'John' }
3const building = { rooms: ['cafe', 'meeting room A', 'meeting room B'], doors: 27 }

所以现在我们知道V8中的不同对象,让我们看看V8如何优化我们的对象。

隐藏的班级

隐藏的类是V8如何识别我们的对象。

让我们把这一点分成步骤。

我们声明一个对象:

1const obj = { name: 'John'}

V8 将对该对象声明一个classId

1const objClassId = ['name', 1]

然后我们的对象被创建如下:

1const obj = {...objClassId, 'John'}

然后,当我们访问我们的对象上的名称属性时,如下:

1obj.name

V8 做出了以下观点:

1obj[getProp(obj[0], name)]

这是V8在创建我们的对象时所经历的过程,现在让我们看看如何优化我们的对象并重复使用classIds

创建对象的技巧

如果可以,你应该 **在构建器中声明你的属性,这将确保对象结构保持不变,因此V8可以优化你的对象。

1class Point {
2  constructor(x,y) {
3    this.x = x
4    this.y = y
5  }
6}
7
8const p1 = new Point(11, 22) // hidden classId created
9const p2 = new Point(33, 44)

您应该 保持房地产订单不变,以以下示例为例:

 1const obj = { a: 1 } // hidden class created
 2obj.b = 3
 3
 4const obj2 = { b: 3 } // another hidden class created
 5obj2.a = 1
 6
 7// this would be better
 8const obj = { a: 1 } // hidden class created
 9obj.b = 3
10
11const obj2 = { a: 1 } // hidden class is reused
12obj2.b = 3

一般优化技巧

所以现在让我们进入一些一般提示,这将有助于更好地优化您的JavaScript代码。

定义函数论点类型

当参数被传输到函数时,重要的是它们是相同的类型. Turbofan 将在 4 次尝试后放弃尝试优化您的 JavaScript,如果参数类型不同。

举以下例子:

1function add(x,y) {
2  return x + y
3}
4
5add(1,2) // monomorphic
6add('a', 'b') // polymorphic
7add(true, false)
8add({},{})
9add([],[]) // megamorphic - at this stage, 4+ tries, no optimization will happen

另一个提示是确保 在全球范围内声明类别:

 1// don't do this
 2function createPoint(x, y) {
 3  class Point {
 4    constructor(x,y) {
 5      this.x = x
 6      this.y = y
 7    }
 8  }
 9
10  // new point object created every time
11  return new Point(x,y)
12}
13
14function length(point) {
15  //...
16}

结论

所以我希望你能学到一些关于V8如何在帽子下工作以及如何写出更好的优化JavaScript代码的知识。

Published At
Categories with 技术
Tagged with
comments powered by Disqus