javascript|V8 编译浅谈( 二 )


优化编译器
IR 本身可以做到多趟迭代从而优化源程序 , 在每一趟迭代的过程中可以研究代码并记录优化的细节 , 方便后续的迭代查找并利用这些优化信息 , 最终可以高效输出更优的目标程序:

优化器可以对 IR 进行一趟或者多趟处理 , 从而生成更快执行速度或者更小体积的目标程序(例如找到循环中不变的计算并对其进行优化从而减少运算次数) , 也可能用于产生更少异常或者更低功耗的目标程序 。 除此之外 , 前端和后端内部还可以细分为多个处理步骤 , 具体如下图所示:

3 两者的特性比较
解释器和编译器的具体特性比较如下所示:

需要注意早期的 Web 前端要求页面的启动速度快 , 因此采用解释执行的方式 , 但是页面在运行的过程中性能相对较低 。 为了解决这个问题 , 需要在运行时对 JavaScript 代码进行优化 , 因此在 JavaScript 的解析引擎中引入了 JIT 技术 。
4 JIT 编译技术
JIT (Just In Time)编译器是一种动态编译技术 , 相对于传统编译器而言 , 最大的区别在于编译时和运行时不分离 , 是一种在运行的过程中对代码进行动态编译的技术 。

5 混合动态编译技术
为了解决 JavaScript 在运行时性能较慢的问题 , 可以通过引入 JIT 技术 , 并采用混合动态编译的方式来提升 JavaScript 的运行性能 , 具体思路如下所示:

采用上述编译框架后 , 可以使得 JavaScript 语言:
启动速度快:在 JavaScript 启动的时候采用解释执行的方式运行 , 利用了解释器启动速度快的特性 运行性能高:在 JavaScript 运行的过程中可以对代码进行监控 , 从而使用 JIT 技术对代码进行编译优化 三 V8 的编译原理 V8 是一个开源的 JavaScript 虚拟机 , 目前主要用在 Chrome 浏览器(包括开源的 Chromium)以及 Node.js 中 , 核心功能是用于解析和执行 JavaScript 语言 。 为了解决早期 JavaScript 运行性能差的问题 , V8 经历了多个历史的编译框架衍变之后(感兴趣的同学可以了解一下早期的 V8 编译框架设计) , 引入混合动态编译的技术来解决问题 , 具体详细的编译框架如下所示:

1 Ignition 解释器
Ignition 的主要作用是将 AST 转换成 Bytecode(字节码 , 中间表示) 。 在运行的过程中 , 还会使用类型反馈(TypeFeedback)技术并计算热点代码(HotSpot , 重复被运行的代码 , 可以是方法也可以是循环体) , 最终交给 TurboFan 进行动态运行时的编译优化 。 Ignition 的解释执行流程如下所示:

在字节码解释执行的过程中 , 会将需要进行性能优化的运行时信息指向对应的 Feedback Vector(反馈向量 , 之前也被称为 Type Feedback Vector) , Feeback Vector 中会包含根据内联缓存(Inline Cache , IC)来存储的多种类型的插槽(Feedback Vector Slot)信息 , 例如 BinaryOp 插槽(二进制操作结果的数据类型)、Invocation Count(函数的调用次数)以及 Optimized Code 信息等 。
这里不会过多讲解每个执行流程的细节问题 。
2 TurboFan 优化编译器
TurboFan 利用了 JIT 编译技术 , 主要作用是对 JavaScript 代码进行运行时编译优化 , 具体的流程如下所示:

图片出处 An Introduction to Speculative Optimization in V8 。
需要注意 Profiling Feedback 部分 , 这里主要提供 Ignition 解释执行过程中生成的运行时反馈向量信息 Feedback Vector, Turbofan 会结合字节码以及反馈向量信息生成图示(数据结构中的图结构) , 并将图传递给前端部分 , 之后会根据反馈向量信息对代码进行优化和去优化 。