Skip to content
🔗 内容纲要:

V8: 连接 Ignition 与 Turbofan

目录

原文

Ignition + Turbofan pipeline

为什么做个新的 pipeline?

  • 减少内存使用量

image

  • 减少启动时间

image

parsing 和 compiling 总共占用总时间的 33%。

  • 降低复杂性

Compiler Pipeline (2008)

image

2008 Full-Codegen:

  • 具有隐藏类(hidden classes)和内联缓存(inline caching),快速遍历 AST 的 JIT 编译器(AST-walking JIT compiler )
  • 缺点:无优化的即时编译(non-optimizing JIT)

2008 年,此时是将源代码直接编译为机器码,因此称之为 Full-Codegen ;JIT 编译器还不够成熟,没有优化的及时编译,生成半优化的代码。

Compiler Pipeline (2010)

image

2010 – Crankshaft:

  • 使用类型反馈和去优化(deoptimization),优化即时编译器(Optimizing JIT compiler)。
  • 缺点:不能扩展到现代 JavaScript,严重依赖去优化,有限的静态类型分析(limited static type analysis),与 Codegen 紧密耦合(tightly coupled to Codegen),高移植开销(high porting overhead)。

2010 年使用 Crankshaft 的 V8 有了优化编译器,同时有了去优化的概念,但是 Crankshaft 的优化存在一定的局限性。

Compiler Pipeline (2015)

image

2015 – TurboFan

用类型和范围分析(type-and-range analysis)优化即时编译器,节点海(sea of nodes)。

Compiler Pipeline (2016)

image

2016 年,在 Crankshaft 和 TurboFan(涡轮增压器风扇)的基础上,出现了 Ignition(点火,点燃) ,其中 Ignition 将不同于 Full-codegen,Ignition 将 AST 转换为字节码,而 TurboFan 将字节码转换为机器码。

Compiler Pipeline (Svelte Devices)

image

Compiler Pipeline (2017)

image

显然在 2017 年之后,Ignition 和 TurboFan 成为了主线,而 Full-codegen 和 Crankshaft 则被淘汰。在 Parser 将 source code 转换为 AST 之后,随后的 Compiler 工作就交给了 Ignition 和 TurboFan,Ignition 将 AST 转化为 Bytecode,Bytecode 将被优化,随后 TurboFan 将 Bytecode 转化为 Native Code。

Bytecode 到 graph

Ignition 到 Turbofan

image

在这里我们可以看到源代码文本在经过 Ignition 的编译下变成字节码,并且子节点在 TurboFan 中被构建为 “节点海”。

字节码对简化节点图构建的限制

  • 始终选择字节码
  • 作用域良好的基本块
    • 异常处理程序覆盖单个线性范围的字节码
  • 没有不可约(irreducible)控制流
  • 单个反向分支到循环报头
  • 以循环闭合形式寄存器

字节码的静态分析

  • 构建图之前的字节码预分析
    • 活性分析 (用于去优化框架状态)
    • 环路分配分析 (对于环路的缺陷)
  • 不要生成无用节点
    • 避免内存过载(每个节点 40 + 字节)
    • 避免节点遍历

活性分析

  • 基本块的预先迭代遍历
    • 在图形构建过程中创建活动映射和状态值节点
    • 之后基于活性重新创建状态值节点
  • 现在迭代遍历字节码数组
    • 只创建一次状态值节点

解决复杂控制 (生成器)

  • Javascript 生成器可以在任意点生成(yield)表达式
    • 会引入不可约控制流
    • 解决方案:在开头和循环头的隐藏 tokens 上转换为 switch 语句
    • 结果:Turbofan 不需要知道任何关于 generator 控制流程的知识

image

解决复杂控制 (try-finally)

  • try-finally 异常处理器
    • 根据触发 finally 块的内容不同,以不同方式退出 (fall-through, return 或 throw)
    • 解决方法:在 finally 块的末尾的 switch 语句
    • 结果:Turbofan 不需要知道任何关于 try-finally 控制流的信息

image

性能结果

代码内存使用(真实的网站)

image

Ignition vs Full-Codegen

image

Octane Performance

image

时间花在了哪里(真实的网站)

image

总结

  • Ignition + Turbofan 是 V8 的未来
  • 对字节码的限制可以简化优化的图创建
  • 针对现实世界的优化暴露了不同的权衡

参考

Released under the MIT License.