Archive

Posts Tagged ‘JIT’

[译] 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.

[摘要] The V8 Myth: Why JavaScript is not a Worthy Competitor

December 25th, 2012 No comments

本文是内容摘要。原文见这里。作者的主页在这里

以下是摘要:

  • JavaScript是无类型的(untyped)语言,代码规模相对较小,以源代码形式发布,在运行动态编译;ActionScript是有类型的(typed),代码规模较大,在编译后以二进制字节码的形式发布。
  • JavaScript在语言本质上的一些特性导致了它不适合用来开发重量级的应用。清醒一些吧。
  • 动态分析是静态分析的很好的补充,但是无法完全替代静态分析。ActionScript代码经过一个AOT(Ahead-of-Time)编译器充分的分析和优化,而JavaScript的即时编译器无法承受这种优化的开销。
  • 你大概听说V8(一个新的JavaScript VM)使用了“类型推断”技术,但是你可能不知道一个JIT是无法承受AOT使用的 modular analysis 所带来的开销的。
  • 我粗略的测试了一下ActionScript工具链和JavaScript工具链。前者得到的代码速度是后者的三倍以上。当然这个比较不够严谨。
  • 所以,不要操心JavaScript了,来关注ActionScript吧,这个才是21世纪最值得拥有的脚本语言。

摘要结束

原文作者原先是美国马里兰大学程序语言研究组的研究人员,后来加入了Adobe公司程序语言研究小组。这篇博客写于2012年1月,距今差不多快1年——对于互联网技术而言是很长的一段时间。文章很短,评论很多,语言之间的对比和吐槽总是很有人气 🙂

今天看来,文中提到的JavaScript的一些问题依然存在,相信其中还有一些研究的机会,博主已经看到了一些相关的研究成果发表了。有兴趣研究的同学要抓紧时间了 🙂

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

TraceMonkey (not) in Firefox

October 28th, 2012 No comments

这个世界总是变化太快。浏览器的世界变化更快。

2008年左右加入到Firefox/SpiderMonkey中的Trace-based JIT引擎TraceMonkey,2011年10月份的时候被默认禁用(bug#697666),11月份的时候已经被David Anderson从Mozilla-Central中移除了(bug#698201)。

Nicholas Nethercote在他的博客中解释说,当时Firefox中有TraceMonkey(TM)和JaegerMonkey(JM)两个不同的JIT同时存在,而引入的Type Inference技术(TI)使得JM+TI的执行性能超过了JM+TM的执行速度。TM针对特定的代码优化的效果还是不错的,例如没有分支跳转的循环。但是在其它的代码情形下,不如JM(它是Method-based)。除此之外TM的代码的复杂性和内存消耗也是它被移除的原因之一。在移除前,TM的代码行数已经达到了67000+的规模,对于一个JS编译器来说已经是很大了。

TraceMonkey诞生的时候出了好几篇高质量的论文(PLDI’09, VEE’09),我也曾经想在上面做一些工作。想不到我还没有克服自己的拖延症,TraceMonkey就已经告别了。只能叹“相逢恨晚,造物弄人” :-D。

Multicore JIT in .NET 4.5

October 28th, 2012 No comments

前不久微软推出了 .NET 4.5,其中包含了一个 Multicore JIT。

这个Multicore JIT的目标是减少应用程序的启动时间,提高启动速度。根据相关的介绍,.NET程序在运行时需要先从平台无关的字节码翻译到本地码,这个过程就依赖JIT。如此JIT的运行时间就包含在了应用程序的启动时间内了。为了提高应用程序的启动时间,微软的做法是将JIT从主线程中拆出来,放在一个单独的线程中。在目前的多核平台上,这一个单独的线程可以和主线程并行的执行,减少用户的等待时间。

但是这样会遇到一个问题:一般情况下JIT是在程序的函数即将被执行的时候进行翻译的,这样就会导致主线程执行一个函数时,需要等待JIT进行即时编译,效果上跟把JIT放在主线程中没有什么区别。为了使JIT线程在翻译的时候主线程不至于停顿,就需要JIT能够提前判断接下来要执行的函数并进行编译。JIT不是神仙,能够未卜先知,于是微软给出来一个Profile-based的方案:启用Multicore JIT之前,首先要对应用程序进行Profiling,记录下程序在启动时调用的函数(代码)序列,并保存在文件中。下次应用程序启动的时候,Multicore JIT从Profile中读取函数调用序列并提前进行翻译。

理论上Multicore JIT最大能够缩短50%的启动时间,如果Profile信息和当前程序运行的情况是相同的话。最坏情况是Profile跟应用程序的当前执行情况完全不懂,那么可能就反而有降速了。

为什么只有一个JIT线程?用两个JIT线程会不会将启动速度缩短到原来的三分之一?我想,大概是因为以下的几个原因:

  • .NET环境中还有别的线程执行,应用程序本身也可能是多线程的,JIT能够使用的空闲核心不会太多;
  • 有不少的运行环境还是双核平台的,两个JIT线程不会增加速度;
  • 多个JIT线程会导致更多的并发数据访问和线程上下文切换开销,性能反而可能会下降;
  • 多个JIT线程开发难度更大,非确定性的bug会让调试的人发疯。

另外,看相关的文档,似乎微软的.NET环境中,应用程序只会被JIT一次,而不是像JVM(如Oracle HotSpot)一样有多个JIT编译器可以对一个函数生成多种不同的本地码。

以下是微软开发人员关于Multicore JIT的访谈视频,中间的是Multicore JIT程序经理Dan Taylor,右边是.NET性能架构师(Performance Architect)Vance Morrison,左边是Windows软件工程师Rick Brewster。Dan和Vance介绍Multicore JIT原理和初心,Rick现身说法用了Multicore JIT之后他开发的大型.NET桌面程序启动时间上的改进。