JavaScriptCore, WebKit的JS实现(一)

简介: 什么是method JIT? 什么是DFG JIT? JSC的DFG JIT与V8 Crankshaft相比有何优劣? JIT:一个方法式的JIT(a method jit) *关于method JIT,可以参考另一份档案。

什么是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

目录
相关文章
|
JavaScript 前端开发
javascript深拷贝和浅拷贝以及实现方法(推荐)
javascript深拷贝和浅拷贝以及实现方法(推荐)
532 0
javascript深拷贝和浅拷贝以及实现方法(推荐)
|
JavaScript 前端开发
利用JavaScript实现二级联动
利用JavaScript实现二级联动 要实现JavaScript二级联动效果,首先要确定需要哪些技术: 二维数组 for in循环 new Option(text,value,true,true) add(option,null) onchange() 表单事件 HTML代码: <!-- <input type="text" id="text"> --> 请选择省份: <select name="" id="provinces"> <!-- <option value="江苏省">江苏省</option>
|
JavaScript 前端开发
JavaScript函数柯里化的实现原理,进来教你完成一个自己的自动实现柯里化方法
JavaScript函数柯里化的实现原理,进来教你完成一个自己的自动实现柯里化方法
167 0
|
移动开发 JavaScript weex
weex-自定义module,实现weex在iOS的本地化,js之间互相跳转,交互,传值(iOS接入weex的最佳方式)
weex-自定义module,实现weex在iOS的本地化,js之间互相跳转,交互,传值(iOS接入weex的最佳方式)
219 0
|
JavaScript
JS中实现或退出全屏
JS中实现或退出全屏
153 0
|
前端开发 JavaScript
前端:JS实现双击table单元格变为可编辑状态
前端:JS实现双击table单元格变为可编辑状态
365 0
|
JavaScript 算法 前端开发
【前端算法】JS实现数字千分位格式化
JS实现数字千分位格式化的几种思路,以及它们之间的性能比较
276 1
|
算法 前端开发 JavaScript
【前端算法】用JS实现快速排序
理解数组方法里面运用到的算法,splice 和 slice的区别
113 0
|
JavaScript 前端开发 算法
【前端算法】javaScript实现二分查找
如何使用JS实现一个合格的二分查找
191 0