Crankshaft和TurboFan

两者都是用于优化的编译器

关于调用优化

  • Hot-code,即多次调用的函数或者循环
    • 对函数单元或者循环单元进行优化
    • 和主线程中的机器代码运行并行执行,runtime-profiler在其他的线程中计数并判断
    • 也取决于函数或者循环代码的size,大概1000次或者10000次被调用的话,会成为优化对象
1
2
3
4
5
6
function f() {
return 1;// hot-code(有成为hot-code的可能性)
}
for (var i=0; i<10000; i++) {
func(); // hot-code(有成为hot-code的可能性)
}

判定为Hot-code

  • TurboFan/Crankshaft会在hot的其他线程,再次变异对应区域的代码
    • 但是也存在即使是hot-code,也不进行优化的情况(后述)
  • 对(主线程运行中的)机器语言的jmp目的地址进行替换,切换到执行优化后的机器代码
1
2
3
4
5
6
7
8
9
10
函数切换为优化代码时,更新函数对象持有的指向JIT区域的指针
function f() {
return 1;// hot-code(都有成为hot-code的可能性)
}
for (var i=0; i<10000; i++) {
func(); // hot-code(都有成为hot-code的可能性)
}
在循环中,当从中间切换到优化代码时,可以将jmp目的地址切换到循环的顶部
但仍然存在名为OSR(On-Stack-Replacement)的切换方法。
但这里省略,参考这篇文章:https://wingolog.org/archives/2011/06/20/on-stack-replacement-in-v8)

优化编译器的适用条件(主要的)

  1. 函数/循环中,未使用优化不支持的文法
    • debugger语句,eval语句之类的
  2. 存在”use asm”语句的话,使用TurboFan
    • 只有TurboFan可以优化asm.js
  3. 如果有Crankshaft不支持的文法,使用TurboFan
    • try-catch语句,with语句之类的
  4. 默认使用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的概览

参考资料:

下图是2018年现在TurboFan的整体情况

TurboFan的特点

  • Graph Building
    • 根据AST,生成JavaScript节点的graph
      • JSAdd,JSCallFunction,JSLoadProperty,IfTrue,IfFalse之类的
    • 边生成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之类的检查编译结果: