Archive

Posts Tagged ‘IonMonkey’

Open Projects in IonMonkey

April 21st, 2013 No comments

昨天 Mozilla 的 Nicolas B. Pierron 在邮件列表中给出了几个 IonMonkey 的开放项目,有兴趣的同学可以去 SpiderMonkey 的邮件列表看看。这里列出项目的简介:

  • Clarifying our heuristics, to be able to make guesses while we are compiling in IonMonkey, and recompile without the guarded assumptions if the bailout paths are too costly. Our current view is mostly black & white and only one bad use case can destroy the performances. 
  • Adding resume operations, such as an instruction can be moved to a later point in the control flow. This optimization is a blocker for any optimization which can be done with the escape analysis.
  • Adding profile guided optimization, the idea would be to profile which branches are used and to prune branches which are unused, either while generating the MIR Graph, or as a second optimization phase working on the graph. 
  • Improving our Alias Analysis to take advantage of the type set (this might help a lot kraken benchmarks, by factoring out array accesses).
  • Improve dummy functions used for asm.js boundaries. Asm.js needs to communicate with the DOM, and to do so it need some trampoline functions which are used as an interface with the DOM API. Such trampolines might transform typed arrays into strings or objects and serialize the result back into typed arrays.

[译] Baseline Compiler in SpiderMonkey

April 19th, 2013 1 comment

这篇博客是对Mozilla这篇文章的简短编译,有兴趣的读者可以看原文。(PS:现在SpiderMonkey的架构越来越像是V8了。)

本周(2013-4-5)我们终于将 baseline 编译器加入了主分支,进入了 Firefox Nightly 版本。从开始开发到今天差不多半年的时间。

Baseline Compiler 是什么?

可以将 Baseline 编译器看成是 IonMonkey 的一个预备编译器(warm-up)。它的诞生使得我们在未来丢弃 JaegerMonkey 成为可能,也因此使得我们有机会大幅度的减少内存占用(在移除了JaegerMonkey之后)。这还使得我们实现新的语言特性、实现新的优化变得更加容易和直观。

引入了 Baseline 编译器之后我们的 SpiderMonkey 在各个 benchmark 上的性能提高了 5%~10%。你们可以在我们的自动性能评测网站上看到结果

为什么又要做一个JIT?

目前 Firefox 有 JaegerMonkey 和 IonMonkey 两个 JIT(这还不算已经移除的 TraceMonkey)。JaegerMonkey能够生成“很快的”代码,而IonMonkey能够生成“真正快的”代码。如果一段代码运行次数频繁,就使用JaegerMonkey编译它;如果之后运行还是很频繁,那就用IonMonkey再去编译一次。这样能够让重量级的优化用在真正需要的地方。但是这种策略的成功,需要仔细的权衡不同优化界别中编译优化的选择,因为编译本身也是需要耗费大量时间的,重量级的优化尤其如此。

简单来说,JaegerMonkey目前被我们当作简单快速的JIT来使用,但是它的设计初衷并不是这个。而 IonMonkey 跟 JaegerMonkey 之间的协作也存在一些问题。最好的解决方式是设计一个跟IonMonkey设计很接近的快速JIT,而baseline就是为了这个目的设计的。

SpiderMonkey的现状

JavaScript代码在SpiderMonkey中的解析过程大致如下:

  1. JavaScript代码首先被解释器解释执行。解释器很慢,但是它可以保证较快的启动速度,并且可以为JIT收集类型信息。
  2. 当一个函数变得“hot”(执行频率达到一定阈值),则调用JaegerMonkey进行JIT,JaegerMonkey使用解释器收集的类型信息来进行优化。
  3. 当函数已经被JaegerMonkey编译,并且执行次数达到IonMonkey的编译阈值(“really hot”),IonMonkey启动,花费比JaegerMonkey多得多的时间生成比JaegerMonkey好得多的代码。
  4. 如果函数中有变量(或其它东西)的类型发生了变化,那么之前JaegerMonkey和IonMonkey生成的jit代码都会被丢弃;以上的三个过程从头开始。

SpiderMonkey 这么设计是有原因的:解释器虽然很慢,但是能够保证执行的结果总是正确的;而IonMonkey虽然能够让编译之后的代码执行速度变快,但是需要花费很多的时间来编译;如果IonMonkey编译的代码执行次数很少,那么反而会导致总体的运行时间变长;而JaegerMonkey处于两者之间,能够用比IonMonkey少的时间生成比解释器速度快的代码,因此它被作为一个折中。

这样的设计很完美,不是么?

不,有几个重要的问题。

问题

  • JaegerMonkey和IonMonkey都没有能力收集类型信息,但是它们依赖类型信息来生成代码。
  • JaegerMonkey和IonMonkey的调用规范是不一样的,这导致两个JIT生成的代码之间的相互调用成本很高。
  • 解释器收集类型信息的功能具有局限性,并不能满足IonMonkey的需求。
  • 类型推断机制需要大量的内存,而类型推断的作者 Brian Hackett 表示把 JaegerMonkey 抛开之后事情会简单很多。
  • 很多 JavaScript 代码执行的次数很少,连 JaegerMonkey 不会被调用。SpiderMonkey 在 SunSpider 测试集得分上落后很大程度上就是因为这个原因。
  • JaegerMonkey 写得太复杂了,我们真的不想继续维护了 🙂

解决方案

我们使用 Baseline JIT 解决这些问题。它的优点:

  • 代码比JaegerMonkey和IonMonkey都简单;
  • 不会失效(invalidate);
  • 可以收集类型信息,inline cache机制可以帮助收集更多类型信息;
  • 比解释器快10~100倍

致谢

Baseline was developed by Jan De Mooij and myself(注:Kannan Vijayan), with significant contributions by Tom Schuster and Brian Hackett. Development was greatly helped by our awesome fuzz testers Christian Holler and Gary Kwong.

And of course it must be noted Baseline by itself would not serve a purpose. The fantastic work done by the IonMonkey team, and the rest of the JS team provides a reason for Baseline’s existence.

Hello Firefox 18: Benchmark Results, Comparing with Firefox 17.0.1 and Chromium 18

January 10th, 2013 No comments

Firefox 18 发布了,新闻说启用了IonMonkey JIT的SpiderMonkey性能提升了25%(Kraken)。今天测试了一下,发现在 Linux上,Firefox 18 比 Firefox 17 的提升,甚至可以达到 47%,跟 Chromium 的速度旗鼓相当。以下是用我的台式机跑出来的 Kraken、V8、SunSpider、Octane 四个Benchmark的结果。

Kraken Benchmark

Firefox 18

详细结果

Firefox 17

详细结果

Firefox 18 vs. Firefox 17

(为了防止折行,将“FROM”和“TO”列隐去了,可以参看上面的结果):

从结果来看引入了IonMonkey之后加速了将近47%。但是同时,个别程序性能反而下降了。

Chromium

作为对比,一并测试了一下 Chromium 的得分,版本号 18.0.1025.168(详细结果):

 

总体上而言还是 Chromium 快一点,但是得分很接近,在一些分项上 Firefox 18.0 速度比Chromium更快。

Firefox 18.0 vs Chromium

 

Sunspider 0.91 Benchmark

SunSpider 0.9.1 的测试结果,有点意外的是 Firefox 17.0.1 最快,其次是Firefox 18.0,Chromium最慢。嗯,一定是我打开的方式不对。

测试地址:http://www.webkit.org/perf/sunspider-0.9.1/sunspider-0.9.1/driver.html

Firefox 18.0

详细结果

 

Chromium

详细结果

Firefox 17.0.1

详细结果

V8-v7 Benchmark

V8的测试结果,Firefox 18.0 > Chromium > Firefox 17.0.1

测试地址:http://v8.googlecode.com/svn/data/benchmarks/v7/run.html

Firefox 18.0

Score: 7671

Richards: 8811
DeltaBlue: 10991
Crypto: 11226
RayTrace: 6761
EarleyBoyer: 11968
RegExp: 904
Splay: 9461
NavierStokes: 15932

Chromium

Score: 7298

Richards: 10584
DeltaBlue: 14030
Crypto: 13852
RayTrace: 8836
EarleyBoyer: 19236
RegExp: 2368
Splay: 3268
NavierStokes: 2974

Firefox 17.0.1

Score: 5695

Richards: 6499
DeltaBlue: 7061
Crypto: 9931
RayTrace: 3211
EarleyBoyer: 7682
RegExp: 1447
Splay: 7603
NavierStokes: 8945

Octane v1 Benchmark

最后是 Octane 的测试结果。Octane Benchmark 的结果是一个分数,得分越高越好。总体得分来看,Chromium > Firefox 18.0 > Firefox 17.0.1。

关于这个Benchmark,Mozilla的开发人员Nicholas Nethercote并不认可,发表了一篇博客,认为测试程序的选取不具有代表性,过度的考虑了Chrome Apps。

测试地址:http://octane-benchmark.googlecode.com/svn/latest/index.html

Firefox 18:

Octane Score: 6754
Richards 8977
Deltablue 9984
Crypto 9910
Raytrace 6326
EarleyBoyer 11472
Regexp 634
Splay 9192
NavierStokes 15768
pdf.js 3452
Mandreel 6184
GB Emulator 8755
CodeLoad 7924
Box2DWeb 6940

Firefox 17:

Octane Score: 5618

Richards 6365
Deltablue 7425
Crypto 9962
Raytrace 3217
EarleyBoyer 7217
Regexp 1410
Splay 7896
NavierStokes 8877
pdf.js 4050
Mandreel 5447
GB Emulator 5096
CodeLoad 8622
Box2DWeb 5306

Chromium 18.0

Octane Score: 7735

Richards 8804
Deltablue 14116
Crypto 13961
Raytrace 9028
EarleyBoyer 19073
Regexp 2419
Splay 3726
NavierStokes 3112
pdf.js 10526
Mandreel 7992
GB Emulator 11014
CodeLoad 8310
Box2DWeb 5498

说明

  • 测试的机器的配置是 Intel(R) Core(TM)2 Quad CPU Q9400 @ 2.66GHz, 8G DDR2, Ubuntu 10.04 x86_64。
  • 我机器上的 Chromium 的版本号似乎比 Chrome 的小,不确定是不是最新的版本(用的 stable release channel),对于测试结果有影响。
  • 发现默认情况下Firefox 17和Firefox 18不会同时运行。Firefox在启动的时候会检查一下是否已经有了Firefox进程,如果有的话就不开新的进程了。这使得在测试的时候必须先关闭所有的Firefox浏览器窗口才能够换一个版本。为了避免乌龙我在每次测试之前,通过“About Firefox”菜单确认了版本。
  • 这个测试结果仅限于Linux,在Windows环境下或许是完全另外的一个结果。相比而言各个浏览器对于Linux环境下的性能优化都不是很上心,小小失落。

IonMonkey 中可能的研究点

November 16th, 2012 No comments

一周前,一位巴西的大四学生给 Mozilla JS-Internals 邮件列表发了一封邮件[1],说自己下半年就开始计算机科学的研究生学业了,希望能够在 IonMonkey 上做些研究,但是刚接触 IonMonkey 没有什么感觉,希望能够得到一些指点。今天 Mozilla JS Engine 的负责人 David Anderson 回复了他,指出了几个他们感兴趣的研究项目[2],有兴趣的读者可以关注一下:

  1. Escape Analysis(逃逸分析):目前还没有任何的工作,所以即使不是完整的算法实现,能够得到一些测试数据也是很好的。逃逸分析能够帮助减少冗余的堆内存占用(当一个线程中的堆内存对象不确定是否被其它线程引用的时候是不能轻易的删除的)。
  2. Better Alias Analysis(别名分析):目前 IonMonkey 中有一个别名分析(位于 js/src/ion/AliasAnalysis.{h,cpp}),但是比较的粗糙,例如在遇到类似“v.x + v.y + v.z”这样的表达式时,现在的别名分析会将 v.x 和 v.z 都看成是 v.y 的别名。这阻碍了后续的优化工作。
  3. RA Improvements(寄存器分配算法的改进):要重写一个 RA 是非常难的,工作量也非常的大。如果能够在现在 RA 实现的基础上做一些改进,也是很有意义的。
  4. Control-flow Elimination(不常用控制流消除):目前 IonMonkey 能够消除(eliminate)单个指令,但是无法消除 CFG 中的 Block 。如果这个功能实现了,我们(开发人员)就可以做进一步的实验,尝试更加激进的优化,消除掉不常用的分支,或许还可以促进 RA 的效果。

目前 IonMonkey 还在开发中,支持的分析和优化还不是很多,实现上也是比较简单的实现,应该还有不少的机会。

Reference:

[1]: http://www.mail-archive.com/dev-tech-js-engine-internals@lists.mozilla.org/msg00120.html

[2]: http://www.mail-archive.com/dev-tech-js-engine-internals@lists.mozilla.org/msg00122.html

IonMonkey分析:目录结构

September 16th, 2012 No comments

本文的内容整理自 https://wiki.mozilla.org/IonMonkey/Overview

IonMonkey的代码位于“Mozilla-central/js/src/ion”目录下,目前有149个文件,4个子目录,代码43679行。从编译器的角度来说这个代码的规模还不算大,结构相对简单。根据Mozilla Wiki的这张结构图可以看到IonMonkey使用了MIR和LIR两层IR结构:

Ionmonkey_overview

IonMonkey结构

IonMonkey的代码可以按照这张结构图分成以下几个部分:

  • 前端,将JavaScript字节码翻译到MIR:
    • IonBuilder.* 将字节码翻译到MIR,比较大,6801行代码;
    • MIR.* 组成MIR的类,共6979行代码;
    • MIRGenerator.h :不到一百行代码,不知道是什么东西;
    • MIRGraph.* :1407行代码,基本块和控制流图构建与维护;
    • MOpcodes.h:MIR操作码,定义了117个opcode。SpiderMonkey的其它模块都将操作码等数据放在*.tbl文件中,或许该文件也会在后续被分拆出来。
  • 在MIR上进行的分析和优化;
    • IonAnalysis.{cpp,h} :一些小的分析算法,例如计算DominatorTree,计算Phi节点等。1052行代码;
    • LICM.{h,cpp}:循环不变量外提代码,总共661行代码;
    • TypeOracle.h:Type Oracle的接口文件,379行代码;
    • TypePolicy.{h,cpp}:定义类型具体化的规则,共739行代码;
    • ValueNumbering.{h,cpp} :全局值编号算法;
  • 将MIR转译到LIR的Lowering过程;
    • Lowering.* 生成LIR结构。
    • LIR-Common.h:硬件平台通用的LIR指令,三千多行;
    • LOpcodes.* :平台通用的LIR opcode。
      • shared/Lowering-x86-shared.* :x86和x64共用的LIR代码;
      • <arch>/Lowering-<arch>.* :平台特定的LIR代码;
      • <arch>/LIR-<arch>.h : 平台特定的LIR指令;
      • <arch>/LOpcodes-<arch>.h :平台特定的LIR opcode;
  • 寄存器分配、指令调度、代码生成;
    • GreedyAllocator.* – 贪心寄存器分配算法.目前还没有出现在主分支中;
    • LinearScan.* – 线性扫描分配器算法;
    • CodeGenerator.* :代码生成功能入口,包含了很多共用代码;
      • shared/CodeGenerator-shared.* :可以被所有平台使用的代码生成逻辑;
      • shared/CodeGenerator-x86-shared.* :x64和x86平台共用的代码;
      • <arch>/CodeGenerator-<arch>.* :特定平台的代码;
    • MoveResolver.* :目前不清楚是干什么的,Mozilla Wiki的原文是“Performs cycle detection and topological sorting of parallel moves.”;
    • shared/MoveEmitter-x86-shared.*: 目前不清楚是干什么的,Mozilla Wiki的原文是“Emits a sequence of parallel moves that have been cycle-detected and topologically sorted.”
    • <arch>/StackAssignment-<arch>.h:RA算法用到的辅助类,用于在栈上分配空间。
    • <arch>/Architecture-<arch>.h:硬件平台信息,例如寄存器数量和名字,调用规范等。
    • IonRegisters.h:寄存器交互相关的C++辅助类。
    • IonMacroAssembler.h:平台无关的汇编辅助类。
    • shared/MacroAssembler-x86-shared.h:x86和x64共享的抽象汇编器类。
    • <arch>/MacroAssembler-<arch>.h:x86的汇编类。
    • Assembler-shared.h:用于进行代码汇编的数据结构。
    • shared/Assembler-x86-shared.h:x86和x64共享的指令集辅助类。
    • <arch>/Assembler-<arch>.*:平台相关的指令集辅助类。
    • IonLinker.h*:将生成好的代码移动到可执行内存页中。
  • 运行时支持:
    • Bailouts.* :当遇到类型推测失败等情况时使用。David在邮件列表中说“A guard failure, type-inference invalidation, or GC can cause a “bailout”. A bailout is when an Ion frame on the stack must be converted back into an interpreter frame. When this happens, interpreter frames are created for each JS frame in the Ion frame (there can be multiple because of inlining), and we resume running the function in the interpreter instead.”
      • <arch>/Bailouts-<arch>.* – 平台相关的bailout辅助类。
    • <arch>/Trampolines-<arch>.cpp – 平台相关的trampolines类.
  • 数据结构及其它辅助代码:
    • IonCode.h :IonCode、IonScript的声明;
    • IonCompartment.h :IonCompartment的声明;
    • IonFrames.h :Ion frames布局声明;
    • BitSet.* :一个任意大小的比特集合实现。
    • C1Spewer.*:调试用的一个工具;
    • FixedArityList.h :模板,生成固定大小的向量类;
    • InlineList.h :来对象之间内建链表结构;
    • IonAllocPolicy.h :基于内存池的分配实现;
    • IonSpewer.* :调试用工具;
    • JSONSpewer.* :JSON调试用工具。