什么是method JIT? 什么是DFG JIT? JSC的DFG JIT与V8 Crankshaft相比有何优劣?
JIT:一个方法式的JIT(a method jit)
*关于method JIT,可以参考另一份档案。
关于解释器(interpreter)和堆栈(stack,也就是寄存器文件,register file)都是应用了方法式JIT(method JIT). 所谓简单方法JIT(确实没什么特别的名字)做得事和字节码解释器完全一样,只不过它的结果是机器码,而不是虚拟的机令集。
也没什么好说的,JIT的结果可以减少调度的开销,特别是允许对特定上下文进行编译时(原文:jitting the code has the result you would expect, reducing dispatching overhead, while at the same time allowing some context-specific compilation), 就比如当给一个变量赋个整数常量。这个JIT只能算是凑合(quick-and dirty), 所以传统的Method JIT,像HotSopt C1及C2, 并没有明显的好处。相对而言,寄存器式的VM字节码却保证可以为JSC带来显著的提升,不过到目前为止,JSC在这个方向上并没做什么。
再思考一下,我认为对于JavaScript而言,CSE只有在你知道类型时才可能有作用,毕竟JS的valueOf()并不那么靠谱!
DFG:JSC的Crankshaft? (dfg: a new crankshaft for jsc)?
由Gavin Barraclough和Filip Pizlo开发的数据流图JIT(data flow graph,DFG JIT)是一种推测性优化技术。比如,当有如下JS代码:
a[i++] = 0.7*x;
那么a很可能是一个浮点型数组,而i则很可能是一个整数。你应该可以想到,如果使用原始的数组和整数操作,就能达到很好的性能,于是尝试着将这份代码按照这个假设编译出了一个新的版本。如果这个方法最终失败了(有不符先前假设的情况发生了),就把这份代码中全身而退(bail out),再回到原先的method JIT。
事实上,解释器(interpreter)和simple method JIT有一个清晰的字节码语义模型来确保在必要时从DFG JIT中轻易地退出来。只需要重建虚拟寄存器和Register Windows(奇怪的名字,和register file一样. ^_^)的状态,然后跳转回原先的代码即可。 (V8 称之为"逆优化(deoptimization)"; DFG则称之为"投机不成(speculation failure)".)
还有另一种从simple JIT跳转到DFG JIT的方法,叫栈上替换(on-stack replacement, OSR). DFG JIT就是这样做的。我就听说用了OSR就可以在Kraken(一个JS性能测试器)测试中胜出,这个测试器会使用大量紧凑的循环,所以你必须能够在不依赖函数的反复进入的情况下优化你的代码(Method JIT就是这么干的!)。
当DFG JIT启用后,解释器(如果有的话)和简单方法(simple method) JIT会对性能数据进行判断,它会记录代码各部分的流转类型(flow types)。如果一个循环被执行相当次数(目前是大于1000次),或者一个函数会调用了很多次(目前是70次), DFG JIT就会行动了。它将相应的字节码解析为SSA(Static Single Assignment)形式, 然后沿着执行路径收集类型信息。这个过程很像我在另一篇文章中所说明的情形。
JSC和Crankshaft的区别在于Crankshaft从Inline Caches中直接获得类型信息,而不是从代码中检测。我认为Crankshaft的实现更为优雅一些,但当GC(垃圾回收,garbage collection)释放了缓存(cache)后,它就会得很弱了。
我以前提到过inlining, DFG JIT就会使用它,并且像HotSpot一样在解析时使用。类型分析(type profiling) (也称为value profiling)组合运用一些简易的静态分析技术,使得DFG可以使int32和双精度数据类型区分开来。
有一件事DFG JIT目前还做不到,就是代码移动中一些情况(code motion,又是一个编译领域的名词)。它虽然可以做去除死代码和公共表达式,但它的前提条件是必须能做类型分析(value profile)。而针对循环不变量的code motion,这是做不到的(因为值不变嘛)。
另外,DFG的寄存器分配器(register allocator)不及Crankshaft的好。这是由于JSC汇编造成的的阻碍。在构造良好的、健壮的代码片段的同时,JSC的汇编程序使用双地址的接口(interface)而不是三址的。这意味着,不需要像add(dest,op1,op2)的方法,而是add(op1,op2),其结果被默认存在第一个操作数中。它是针对x86指令集的,但对于有更多寄存器的系统(如x86 - 64),这并不算什么。
对于计算结果,需要更多代码的基于计数的优化触发方式并不是绝对必要, 但这种策略确实有个很好的特性:DFG的性能是可预见的且可衡量的。而另一方面的Crankshaft则使用统计方式触发,其性能是一种统计式的分布。
对于性能,因为DFG仅在Mac OS 64位版本可用,所以可以使用AWFY on the mac进行测试。你一定要使用正确的工具进行性能评测。
看看结果就知道,JSC对V8基准(benchmark)的表现很不错。有趣的是,对于SunSpider测试,JSC打败了V8。不过V8一旦热身后,仍然是表现最好的. JSC做得很不错,而且正在逐渐改进中。
未来(future)
这就是JavaScriptCore. 当前团队的三个人(是的),正专注在DFG JIT上。其中最关键的是什么时候可以将DFG JIT运行到其它系统上。
JSC的另一个工作是新一代的垃圾回收器(new generational garbage collector)。它确实在做,但很慢。虽然有了Card-Marking write barriers(这是一垃圾回收领域的专业名词)的预留函数(stubs),但目前为止并没有相应的实现。不过,至少JSC提供了Handle API, 盖过了SpiderMonkey。
上一篇: JavaScriptCore, WebKit的JS实现(一)
转载请注明出处: http://blog.csdn.net/horkychen