Crankshaft和TurboFan
两者都是用于优化的编译器
关于调用优化
- Hot-code,即多次调用的函数或者循环
- 对函数单元或者循环单元进行优化
- 和主线程中的机器代码运行并行执行,runtime-profiler在其他的线程中计数并判断
- 也取决于函数或者循环代码的size,大概1000次或者10000次被调用的话,会成为优化对象
1 | function f() { |
判定为Hot-code
- TurboFan/Crankshaft会在hot的其他线程,再次变异对应区域的代码
- 但是也存在即使是hot-code,也不进行优化的情况(后述)
- 对(主线程运行中的)机器语言的jmp目的地址进行替换,切换到执行优化后的机器代码
1 | 函数切换为优化代码时,更新函数对象持有的指向JIT区域的指针 |
优化编译器的适用条件(主要的)
- 函数/循环中,未使用优化不支持的文法
- debugger语句,eval语句之类的
- 存在”use asm”语句的话,使用TurboFan
- 只有TurboFan可以优化asm.js
- 如果有Crankshaft不支持的文法,使用TurboFan
- try-catch语句,with语句之类的
- 默认使用Crankshaft
- 这是2016年的情况,现在Crankshaft被移除
Crankshaft
Crankshaft的特点
- Type-feedback
- 使用runtime-profiler收集的信息,确定类型进行加速
- 最终生成的优化代码包含类型检查
- 不能确定类型时,也能够回退到优化前的代码
- Hydrogen(基于High-Level的中间表现(HIR)的优化)
- AST以SSA格式展现
- 各种优化,例如将循环内不变的变量移到循环外
- Lithium(基于Low-Level的中间表现(LIR)的优化)
- 用于寄存器分配的快速算法
- 依赖CPU的优化,代码生成
细节参考:http://nothingcosmos.github.io/V8Crankshaft/src/blog.html
TurboFan
TurboFan的概览
参考资料:
- https://github.com/v8/v8/wiki/TurboFan
- https://speakerdeck.com/brn/source-to-binary-journey-of-v8-javascript-engine
- https://docs.google.com/presentation/d/1H1lLsbclvzyOF3IUR05ZUaZcqDxo7_-8f4yJoxdMooU/edit#slide=id.p
下图是2018年现在TurboFan的整体情况
TurboFan的特点
- Graph Building
- 根据AST,生成JavaScript节点的graph
- JSAdd,JSCallFunction,JSLoadProperty,IfTrue,IfFalse之类的
- 边生成graph边优化
- 根据AST,生成JavaScript节点的graph
- Optimization
- 对graph各种优化
- Code Generation
- 机器代码生成
TurboFan的优化
参考src/compiler/pipeline.cc
- inline
- 函数调用的inline化
- trimming
- 删除没有到达的节点
- typer
- 收集类型信息进行优化
- typed-lowering
- 根据类型将表达式和指令进行变换
- loop-peeling
- 循环内处理转到循环外
- simplified-lowering
- 用更具体的值来进行指令的简单转换
- branch-elimination
- 删除不必要的分支
- generic-lowering
- 将JS前缀指令转换为更简单的调用和stub调用
等等等等
出于某种原因,在名为GenerateCode()的函数中执行了对类型和graph的各种优化。
此外,尽管从CreateGraph()调用GenerateCode(),但这些函数原本应该是独立的。(在代码中还有三个独立的部分,job-> CreateGraph(),job-> OptimizeGraph(),job-> GenerateCode())
实际上,在V8的这个时间段中,每个phase都没有完全分离,因为优化和代码生成都是在CreateGraph()函数内部实现的。
优化编译器的确认方法
可以使用–trace-opt来检查Crankshaft/TurboFan
还可以使用–turbo-stats检查TurboFan的优化列表和统计数据:
可以使用d8 –print_code之类的检查编译结果: