Another RayJune

V8 二三事

被面试官问到了关于 V8 有哪些了解,索性把之前的做的功课总结起来,一起展示。

V8 是什么

V8 是一种 JavaScript 引擎。

Concise expression

JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,一般会附带在网页浏览器之中。

English edition

A JavaScript engine is a program or interpreter which executes JavaScript code. A JavaScript engine may be a traditional interpreter, or it may utilize just-in-time compilation to bytecode in some manner.Although there are several uses for a JavaScript engine, it is most commonly used in Web browsers

V8 为什么这么快?

Concise expression

V8 在运行之前将 JavaScript 编译成了机器码(JIT),而非字节码或是解释执行它,以此提升性能。更进一步,使用了如内联缓存(inline caching)等方法来提高性能。有了这些功能,JavaScript 程序与 V8 引擎的速度媲美二进制编译。

重点:V8 把 JavaScript 代码编译成机器语言。和其他引擎最主要的差别在于,V8 不会生成任何字节码或是中间代码

English edition

V8 compiles JavaScript directly into native assembly.

It’s speed comes from the fact that it compiles JavaScript directly into native assembly.

V8 compiles JavaScript directly to native machine code before executing it, instead of more traditional techniques such as interpreting bytecode or compiling the whole program to machine code and executing it from a filesystem. The compiled code is additionally optimized (and re-optimized) dynamically at runtime, based on heuristics of the code’s execution profile. Optimization techniques used include inlining, elision of expensive runtime properties, and inline caching

针对 V8 的性能优化

Concise expression

隐藏类复用:用相同的顺序为您的对象属性实例化

对象属性的顺序:永远用相同的顺序为您的对象属性实例化,这样隐藏类和随后的优化代码才能共享

1
2
3
4
5
6
7
8
9
10
11
function Point(x, y) {
this.x = x;
this.y = y;
}

var p1 = new Point(1, 2);
p1.a = 5;
p1.b = 6;
var p2 = new Point(3, 4);
p2.b = 7;
p2.a = 8;

你可能会说对 p1 和 p2 而言,它们会使用相同的隐藏类和类转换。其实不然。 对 “p1” 来说,先是属性 “a” 被添加,然后是属性 “b”。而对 “p2” 来说,先是属性 “b” 被添加,然后才是属性 “a”。这样, “p1” 和 “p2” 就在不同的转换路径作用下,有了不同的隐藏类。在这两种情形下,其实最好是用相同的顺序初始化动态属性,这样隐藏类就可以被复用了

尽量在构造函数中分配对象的所有属性

动态属性:在对象实例化后为其新增属性会导致隐藏类变化,从而会减慢为旧隐藏类所优化的方法的执行。所以,尽量在构造函数中分配对象的所有属性

内联缓存:重复执行相同方法的代码会比不同的方法只执行一次的代码运行得更快

重复执行相同方法的代码会比不同的方法只执行一次的代码运行得更快(由于内联缓存)

避免稀疏数组,预申请大型数组

4、数组:避免使用 keys 不是递增数字的稀疏数组(sparse arrays)。并不为每个元素分配内存的稀疏数组实质上是一个 hash 表。这种数组中的元素比通常数组的元素会花销更大才能获取到。此外,避免使用预申请的大型数组。最好随着需要慢慢增加数组的大小。最后,不要删除数组中的元素,因这会使得 keys 变得稀疏。

尽量避免装箱操作:即尽量只用 31 bit 的有符号数

5、标记值(Tagged values): V8 用 32 个比特来表示对象和数字。它使用 1 个比特来区分是一个对象(flag = 1)还是一个整型(flag = 0)(被称为 SMI 或 SMall Integer,小整型,因其只有 31 比特来表示值)。然后,如果一个数值大于 31 比特,V8 就会给这个数字进行装箱操作(boxing),将其变成 double 型,并创建一个新的对象将这个 double 型数字放入其中。所以,为了避免代价很高的 boxing 操作,尽量使用 31 比特的有符号数

English edition

Performance advice is addictive, and sometimes focusing on deep advice first can be quite distracting from the real issues.

You need to take a holistic view of the performance of your web application - before focusing on these performance tip, you should probably analyze your code with tools like PageSpeed and get your score up. This will help you avoid premature optimization.

The best basic advice for getting good performance in Web applications is:

  1. Be prepared before you have (or notice) a problem
  2. Then, identify and understand the crux of your problem
  3. Finally, fix what matters

  4. Order of object properties: always instantiate your object properties in the same order so that hidden classes, and subsequently optimized code, can be shared.

  5. Dynamic properties: adding properties to an object after instantiation will force a hidden class change and slow down any methods that were optimized for the previous hidden class. Instead, assign all of an object’s properties in its constructor.
  6. Methods: code that executes the same method repeatedly will run faster than code that executes many different methods only once (due to inline caching).
  7. Arrays: avoid sparse arrays where keys are not incremental numbers. Sparse arrays which don’t have every element inside them are a hash table. Elements in such arrays are more expensive to access. Also, try to avoid pre-allocating large arrays. It’s better to grow as you go. Finally, don’t delete elements in arrays. It makes the keys sparse.
  8. Tagged values: V8 represents objects and numbers with 32 bits. It uses a bit to know if it is an object (flag = 1) or an integer (flag = 0) called SMI (SMall Integer) because of its 31 bits. Then, if a numeric value is bigger than 31 bits, V8 will box the number, turning it into a double and creating a new object to put the number inside. Try to use 31 bit signed numbers whenever possible to avoid the expensive boxing operation into a JavaScript object.

V8 除了浏览器,还能用在哪

v8 除了用在 Chrome 中,还用在 node.js 和 MongoDB(版本 2 以后的)中。

Electron 是一个通过把 Chromium 和 node.js 结合为一个运行时来实现使用 HTML、CSS、JavaScript 构建桌面应用程序的开源库。

代表作品:VS Code, Atom

除了 V8 还有哪些 JavaScript 引擎

Rhino

written by Java ,它旨在用于服务器端,作为默认的 JavaScript 引擎嵌入在 Java SE 6 中,被 Mozilla 基金会作为开源软件管理

SpiderMonkey

Firefox 引擎,第一代引擎

JavaScriptCore

Safari 引擎

Chakra

Edge 引擎,IE 9 ~ 引擎

JerryScript

用于物联网的轻量级引擎,三星开发

V8 “花边”

  • V8 作者:Lars Bak 芬兰人,虚拟机大师
  • V8 在 github 上的 commit 数量第三是中国人 Yang Guo,为大牛点赞

Reference & Thanks

https://en.wikipedia.org/wiki/JavaScript_engine
https://zh.wikipedia.org/wiki/JavaScript%E5%BC%95%E6%93%8E

https://en.wikipedia.org/wiki/Chrome_V8
https://zh.wikipedia.org/wiki/V8_(JavaScript%E5%BC%95%E6%93%8E)

https://blog.sessionstack.com/how-javascript-works-inside-the-v8-engine-5-tips-on-how-to-write-optimized-code-ac089e62b12e
https://www.html5rocks.com/en/tutorials/speed/v8/

文章标题:V8 二三事

文章作者:RayJune

时间地点:上午 11:39 于 D295 郑州到福州的动车上

原始链接:https://www.rayjune.me/2018/03/02/All-about-V8/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。