跳到主要内容

V8

V8 是 Google 的开源高性能 JavaScript 和 WebAssembly 引擎,用 C++ 编写。 它用于 Chrome 和 Node.js 等。 它实现了 ECMAScriptWebAssembly,并在 Windows 7 或更高版本、macOS 10.12+ 以及使用 x64、IA-32、ARM 或 MIPS 处理器的 Linux 系统上运行。 V8 可以独立运行,也可以嵌入到任何 C++ 应用程序中。

语言的分类

解释执行

  • 编译执行是指在程序运行之前将源代码整体转换为机器代码的过程。编译器会对源代码进行词法分析、语法分析和语义分析,生成中间表示形式(如抽象语法树或字节码),然后将其转换为机器代码。生成的机器代码可以直接在目标平台上执行,因此执行效率较高。常见的编译执行语言包括C、C++、Rust等。
  • 启动快,执行慢

编译执行

  • 解释执行是指程序在运行时逐行解释执行源代码的过程。解释器会逐条解释源代码,并将其转换为机器代码或直接执行。解释执行通常不需要事先将源代码编译为机器代码,因此具有更好的跨平台性。然而,由于每次执行都需要解释器进行解释和执行,解释执行的效率通常较低。常见的解释执行语言包括JavaScript、Python、Ruby等。
  • 启动慢,执行快

需要注意的是,实际上编译执行和解释执行并不是严格区分的,很多语言实现采用了混合的方式。例如,Java 在编译阶段将源代码编译为字节码,然后在运行时使用解释器或即时编译器将字节码转换为机器代码执行。

选择编译执行还是解释执行取决于多个因素,包括执行效率、开发效率、平台兼容性等。有些语言提供了即时编译器(JIT)的技术,将编译执行和解释执行相结合,以在执行效率和开发效率之间取得平衡

V8执行过程

V8 引擎采用了混合的执行方式(JIT),结合了编译执行和解释执行的特性。

解析器Parse

1. 词法分析

在 V8 引擎中,Scanner 就是负责进行词法分析的组件。它会读取源代码,一次一个字符地分析,然后将这些字符组合成 tokens

var name = "diqiu" 通过词法分析会被分解成以下的 tokens:

  • var:这是一个关键字,用于声明变量。
  • name:这是一个标识符,用于表示变量的名称。
  • =:这是一个运算符,用于赋值。
  • "diqiu":这是一个字符串字面量,表示一个字符串的值。
  • ;:这是一个分隔符,用于分隔语句(在 JavaScript 中,分号可以省略,如果省略了,JavaScript 会在解析时自动插入)。

2. 语法分析

解析器将 token 组成的序列转换为抽象语法树(AST)。解析器会根据语法规则构建 AST,表示代码的结构和关系。

作用域

此时完成词法分析和语法分析后,编译器在这两个阶段会收集所有的变量和函数声明,并根据它们在源代码中的位置来确定它们的作用域。

$ d8 --print-scopes 1.js
Global scope:
global { // (000001D42188BEC0) (0, 38)
// will be compiled
// 1 stack slots
// temporary vars:
TEMPORARY .result; // (000001D42188C730) local[0]
// local vars:
VAR c; // (000001D42188C670)
VAR b; // (000001D42188C538)
VAR a; // (000001D42188C448)
}

解释器Ignition

Ignition 是一个基于寄存器的解释器,它的主要任务是将 JavaScript 源代码解析为字节码。Ignition 的设计目标是减小内存占用并提高启动速度。它使用一种更紧凑的字节码格式,这有助于减小内存占用,而解释执行字节码则可以避免编译过程中的延迟,从而提高启动速度。

  • 解析阶段:Ignition 解释器首先将 JavaScript 源代码解析为字节码。字节码是一种中间代码,比源代码更接近于机器语言,但又不是特定于任何一种机器的机器代码。这一步是将 JavaScript 源代码转换为更易于执行的格式。

  • 执行阶段:接下来,Ignition 解释器解释执行这些字节码。这意味着 Ignition 解释器会读取字节码,然后根据字节码的指令执行相应的操作。

Ignition 解释器解释执行字节码的同时,V8 引擎会通过一个叫做 "Profiler" 的组件监视正在运行的代码。这个 Profiler 会收集关于代码执行的统计信息,例如哪些函数被调用了多少次,哪些循环执行了多少次等。

Profiler 发现某些代码段(例如某个函数或循环)被频繁执行(也就是所谓的 "热点" 代码)时,这些代码段会被标记为优化候选。然后,这些被标记的字节码会被传递给 TurboFanTurboFanV8 的另一个重要组件,负责将字节码优化编译为机器代码。

var a = 10;
var b = 20;
var c = a + b;
$ d8 --print-bytecode  index.js
[generated bytecode for function: (0x20d90019b7cd <SharedFunctionInfo>)]
Bytecode length: 36
Parameter count 1
Register count 3
Frame size 24
0x20d90019b864 @ 0 : 13 00 LdaConstant [0] # 从从常量池中加载索引0的常量(这里是全局对象)到累加器中
0x20d90019b866 @ 2 : c4 Star1 # 将累加器中的值存储到寄存器 r1 中。
0x20d90019b867 @ 3 : 19 fe f8 Mov <closure>, r2 # 将当前函数(闭包)的引用移动到寄存器 r2 中。
0x20d90019b86a @ 6 : 65 6a 01 f9 02 CallRuntime [DeclareGlobals], r1-r2 # 调用运行时系统的 DeclareGlobals 函数,声明全局变量。参数是寄存器 r1 和 r2 中的值。
0x20d90019b86f @ 11 : 0d 0a LdaSmi [10] # 加载小整数(Small Integer,Smi)10 到累加器中。
0x20d90019b871 @ 13 : 23 01 00 StaGlobal [1], [0] # 将累加器中的值(即10)存储到全局变量 a 中。这里的 [1] 是全局变量 a 的引用,[0] 是属性描述符的引用。
0x20d90019b874 @ 16 : 0d 14 LdaSmi [20] #
0x20d90019b876 @ 18 : 23 02 02 StaGlobal [2], [2] # 类似上述步骤,声明并初始化全局变量 b 为20。
0x20d90019b879 @ 21 : 21 01 05 LdaGlobal [1], [5] # 加载全局变量 a 到累加器中。
0x20d90019b87c @ 24 : c4 Star1 # 将累加器中的值(即 a 的值)存储到寄存器 r1 中。
0x20d90019b87d @ 25 : 21 02 07 LdaGlobal [2], [7] # 加载全局变量 b 到累加器中。
0x20d90019b880 @ 28 : 38 f9 04 Add r1, [4] # 将寄存器 r1 中的值(即 a 的值)和累加器中的值(即 b 的值)相加,结果存储在累加器中。
0x20d90019b883 @ 31 : 23 03 09 StaGlobal [3], [9] # 将累加器中的值(即 a+b 的结果)存储到全局变量 c 中。
0x20d90019b886 @ 34 : 0e LdaUndefined # 加载 undefined 到累加器中。
0x20d90019b887 @ 35 : aa Return # 返回累加器中的值(即 undefined),结束函数的执行。
Constant pool (size = 4)
Handler Table (size = 0)

编译器TurboFan

在优化阶段,V8 引擎使用 TurboFan 编译器将热点字节码编译成高效的机器代码。这个阶段涉及到的主要步骤包括:

  • 收集类型反馈:在代码执行期间,V8 引擎收集关于变量类型的反馈。例如,如果一个特定的变量总是被用作数字,那么这个信息就会被收集起来。这个过程被称为 "类型反馈",这些信息将被用于后续的优化步骤。

  • 生成优化的中间表示:基于字节码和类型反馈,TurboFan 生成一种叫做 "Sea of Nodes" 的中间表示。这种中间表示以图的形式表示程序,使得编译器可以更容易地进行各种优化,例如常量折叠、死代码消除等。

  • 执行优化:在这个阶段,TurboFan 执行各种优化策略,例如内联函数、循环展开、消除冗余的计算等。这些优化策略都是基于之前收集的类型反馈和生成的中间表示。

  • 生成机器代码:最后,TurboFan 将优化过的中间表示编译为机器代码。这个机器代码是特定于目标平台的,可以直接被 CPU 执行。

这个过程使得 V8 引擎可以根据实际的代码执行情况,动态地优化代码,提高执行效率。

blog

v8相关文章

Understanding V8’s Bytecode Firing up the Ignition interpreter V8引擎中的Maglev优化编译器