`
RednaxelaFX
  • 浏览: 3048142 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

YARV和JIT,还有JRuby……

阅读更多
昨天承night_stalker老兄的提醒,去google了一下YARV,看看我是不是把事情记错了。记得Ruby还没1.9的时候我就稍微关注过YARV的信息,但印象中Ruby 1.9/YARV是没有JIT的……

Hmm,我貌似是没记错。目前的Ruby 1.9.1里并没有JIT。

首先需要定义我这里所指的JIT是什么。JIT,Just-In-Time Compiler,也就是所谓的即时编译器,其过程是JIT compilation,即时编译。
广义上说,只要有一个环境E直接支持某种语言B的运行,另外有一个程序是以语言A所编写的,在E上运行前没有单独的编译阶段,而是直接在运行前“即时”将A编译为B,这个即时编译的过程就可以称为JIT,无论A是高级语言也好字节码也好,B是别的字节码也好机器指令也好。
但实践中JIT一般是指将某种中间代码形式转换为机器指令的过程,及执行这个过程的编译器。例如说在x86上运行的Sun Hotspot JVM,其中的JIT会在一定条件下将JVM字节码编译为x86指令;或者说在x86上运行的微软CLR,其中的JIT会在执行某个托管方法之前先检查其是否已经被编译为x86指令,如果还没有的话就将其中的MSIL(CLR的字节码,也叫CIL)给JIT为x86指令,然后再执行那个方法。

Ruby(以下直接提到Ruby实现如无特别说明皆指CRuby,也就是官方版)在1.8系列及之前的版本采用的运行方式是:
Ruby源代码 => 解析为AST(抽象语法树) => 直接在抽象语法树上解释执行

由Sasada Koichi先生所写的YARV进化为了Ruby 1.9.x的虚拟机,它的运行方式是:
Ruby源代码 => 解析为AST => 从AST生成YARV字节码 => 直接在YARV字节码上解释执行

YARV后端的字节码解释器采用的是direct threaded code的解释方式,其特征是在每执行完一条指令之后直接对下一条指令解码,并直接跳转到下一条指令所对应的函数去;这样避免了使在一个大的中央循环内通过switch来做指令解码和分发,减少了CPU的分支预判的失败。在指令流水线较长的CPU上,这种技巧对提高执行速度很有好处。最经典的threaded code应该是各种Forth的实现,其中包括directed threaded code与indirected threaded code等不同的实现方式。

至少在Ruby 1.9.1上,YARV并没有将YARV字节码先JIT为本地机器指令后再执行。我认为这就可以称为“没有使用JIT”。
像是CPython的运行过程:
Python源代码 => 解析为AST => 从AST生成Python字节码 => 直接在Python字节码上解释执行
跟YARV的看起来很像对吧?如果YARV算是使用了JIT,那CPython自然也算是使用了JIT了。照这个推广,用JIT的解释器可就多了。老的SpiderMonkey(Mozilla FireFox的JavaScript引擎)也是先将源码编译为它自己的字节码然后在字节码上解释执行的,KJS(KDE Konqueror的JavaScript引擎)也是类似,……嘛

对YARV的实现方式有兴趣的话可以留意Ruby 1.9的源码里vm开头的源文件。其中字节码解释器的主循环在vm_exec.c的vm_exec_core()函数里;每条指令对应的函数则在vm.inc里。让我们看看其中加号对应的YARV字节码opt_plus的实现函数:
vm.inc 写道
INSN_ENTRY(opt_plus){
{
  VALUE val;

  VALUE recv = TOPN(1);
  VALUE obj = TOPN(0);
  DEBUG_ENTER_INSN("opt_plus");
  ADD_PC(1+0);
  PREFETCH(GET_PC());
  POPN(2);
  #define CURRENT_INSN_opt_plus 1
  #define INSN_IS_SC()     0
  #define INSN_LABEL(lab)  LABEL_opt_plus_##lab
  #define LABEL_IS_SC(lab) LABEL_##lab##_##t
  USAGE_ANALYSIS_INSN(BIN(opt_plus));
{
#line 1287 "insns.def"
    if (0) {

    }
#if 1
    else if (FIXNUM_2_P(recv, obj) &&
	     BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
	/* fixnum + fixnum */
#ifndef LONG_LONG_VALUE
	val = (recv + (obj & (~1)));
	if ((~(recv ^ obj) & (recv ^ val)) &
	    ((VALUE)0x01 << ((sizeof(VALUE) * CHAR_BIT) - 1))) {
	    val = rb_big_plus(rb_int2big(FIX2LONG(recv)),
			      rb_int2big(FIX2LONG(obj)));
	}
#else
	long a, b, c;
	a = FIX2LONG(recv);
	b = FIX2LONG(obj);
	c = a + b;
	if (FIXABLE(c)) {
	    val = LONG2FIX(c);
	}
	else {
	    val = rb_big_plus(rb_int2big(a), rb_int2big(b));
	}
#endif
    }
#endif

    else if (!SPECIAL_CONST_P(recv) && !SPECIAL_CONST_P(obj)) {
	if (0) {
	}
#if 1
	else if (HEAP_CLASS_OF(recv) == rb_cFloat &&
		 HEAP_CLASS_OF(obj) == rb_cFloat &&
		 BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
	    val = DBL2NUM(RFLOAT_VALUE(recv) + RFLOAT_VALUE(obj));
	}
#endif

#if 1
	else if (HEAP_CLASS_OF(recv) == rb_cString &&
		 HEAP_CLASS_OF(obj) == rb_cString &&
		 BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
	    val = rb_str_plus(recv, obj);
	}
#endif
#if 1
	else if (HEAP_CLASS_OF(recv) == rb_cArray &&
		 BASIC_OP_UNREDEFINED_P(BOP_PLUS)) {
	    val = rb_ary_plus(recv, obj);
	}
#endif
	else {
	    goto INSN_LABEL(normal_dispatch);
	}
    }
    else {
      INSN_LABEL(normal_dispatch):
	PUSH(recv);
	PUSH(obj);
	CALL_SIMPLE_METHOD(1, idPLUS, recv);
    }

#line 1901 "vm.inc"
  PUSH(val);
#undef CURRENT_INSN_opt_plus
#undef INSN_IS_SC
#undef INSN_LABEL
#undef LABEL_IS_SC
  END_INSN(opt_plus);}}}

离JIT还是有一段距离的,嗯。

原本的YARV规划里是有JIT的,但,现实是在Ruby 1.9.1里它还没到位(*)。引用RubyConf 2005上Koichi先生的话:
Sasada Koichi 写道
JIT Compilation
• I made easy one for x86, but…
• Too hard to do alone.  I retired.

和当时的图:

到RubyConf 2006的时候,AOT有进展了而JIT仍然没到位。

RubyConf 2008,Koichi先生第四次参加RubyConf……这次他没提到JIT的问题。还是得读代码去了解现状 =w=

(*):或者它到位了而我没发现。求知道详情的讲解一下~~

=========================================================================

YARV自己是还没有JIT,不过这不能阻止其他人为YARV编写JIT后端。下面是相关的两个项目,yajit和yarv2llvm的一些链接:

Shinichiro Hamaji: yajit

Miura: yarv2llvm
Inside yarv2llvm(その1)
Inside yarv2llvm(その2)
Inside yarv2llvm(その3)
Inside yarv2llvm(その4)
Inside yarv2llvm(その5)
Inside yarv2llvm(その6)

=========================================================================

再看看JRuby。它的执行方式可以在启动JRuby虚拟机之前配置,例如说强制使用或禁用某些编译模式之类。
默认情况下,JRuby的运行方式是:
Ruby源代码 => 解析为AST => 在AST上解释执行(JRuby自己的解释器)
       ==> 某个CallSite的成功调用次数超过一定限制后(现在默认为是50次),将对应方法的AST即时编译为JVM字节码
       (由JVM执行字节码,有没有进一步的JIT取决于JVM)


JRuby也有预先编译的模式:
运行之前做预先编译(AOT,Ahead-of-Time compilation):
Ruby源代码 => 解析为AST => 将AST编译为JVM字节码
运行时:
JVM字节码由JVM执行(有没有JIT取决于JVM)


那JRuby算不算有JIT呢?
值得注意的是,JRuby自身是运行在JVM之上的。如果它底下的JVM是有JIT的,那么它也就算有了半个JIT。在JRuby的JIT模式被激活了之后,Ruby方法就会被JRuby编译为JVM字节码,进而有可能被JVM的JIT编译为本地机器指令。挺微妙的,呵呵。

至于JRuby的性能嘛,
Charles O. Nutter如是说:"Noise Cancelling" (2008-11-23)
Charles O. Nutter 写道
A year ago, we were generally a bit slower than Ruby 1.8.6; this year, we're faster in most cases than Ruby 1.9.

而根据Antonio Cangiano在2008-12-09的测试结果:The Great Ruby Shootout (December 2008)
Antonio Cangiano 09 Dec 2008 at 9:51 am 写道
JRuby was the second best, Ruby 1.9.1 was fastest.

当然,那是去年年底的测试结果,使用的还是JRuby 1.1.6RC1 vs Ruby 1.9.1;它们其实算是不相上下,各有侧重。现在JRuby已经出到1.2.0RC1了,性能比起1.1.6又有了一定的提升;官方Ruby方面则暂时还没发布1.9.2系列的preview。谁更快其实不好说。

其实要说“谁更快”很关键的一点在于自己的应用的类型。如果是I/O-bound的类型,那无论VM速度如何可能对应用的性能都不会带来多少影响;如果是需要真的并行计算,那么在当前的Ruby 1.9.1里的GIL限制显然会让它慢于支持真正多线程的JRuby;如果是需要高的交互性能,那么解释执行反而可能比JIT更合适,等等。有很多外在因素使得各种microbenchmark与实际自己的应用侧重的性能需求有所不同,所以基本上大家在提到benchmarks的时候都会说“Benchmarks are just lies damn lies”(引用自John Lam

JRuby比较占优势的应该是长时间运行、多线程的应用场景。运行时间不够长的话JRuby主要是在解释模式运行的;只有当某个CallSite被调用了超过50次并且都是指向同一个目标时,JRuby才会考虑把目标方法编译为JVM字节码。在长时间、高强度的循环里这会带来一定的好处。
不过,不出意外的,JRuby的JIT对底下的Sun JVM的Hotspot JIT也会造成影响:Hotspot会根据它所看到的JVM字节码来决定是否做内联、peephole之类的优化;当执行路径发生改变时,Hotspot会发现它做的一些假设不成立了,于是要退回到解释JVM字节码的模式,重新对执行状况做分析。Charles提到过他们在对JRuby做benchmark的时候,发现有些benchmark在执行到50次之前都还好好的,反而在50次之后JRuby做了JIT使Hotspot退到了慢速路径上而且久久没能恢复过来。以后JRuby还有很多潜力可以挖掘,让它与Hotspot能更好的匹配。(然而专门对Hotspot做的优化对其它JVM或许又是个毒药……要留心)

=========================================================================

IronRuby是.NET上的Ruby实现,它运行在CLI之上;在微软平台上的话,CLI的实现就是CLR;在*-nix平台上则有Novell支持的Mono作为CLI的实现。Anyway,为了讨论的方便,就以CLR为具体例子来说明。
目前的IronRuby也有两种运行模式,一种是预先编译的模式,另一种是解释模式;目前前者是默认模式。
预先编译模式:
Ruby源代码 => 解析为AST => 将AST转换为Expression Tree => 从Expression Tree生成MSIL => 由CLR执行(CLR会JIT)
解释模式:
Ruby源代码 => 解析为AST => 将AST转换为Expression Tree => 由DLR解释执行Expression Tree

所以IronRuby算不算有JIT呢?跟JRuby有相似之处。也是很微妙。不过目前IronRuby的解释模式不会触发Expression Tree => MSIL的编译过程,比JRuby的自适应性要弱一些。

更新:新的DLR解释器也开始有自适应编译了:先解释执行ETv2,一个“方法”被调用三次才触发ETv2 => MSIL的编译。请见这帖:http://www.iteye.com/topic/353790
分享到:
评论
5 楼 seraphim871211 2009-03-06  
引用

引用

看到这个,心里就想不会这么巧吧,在看看个人信息,果然是你。汗~~,太专业了,你看看大家看不太懂就没人拍你了,哈哈。

呃……我之前做什么很糟糕的事情了么? =v=

没呢,我只是随便说说,你还知道我是谁啊。

引用

对了,关于threaded code,以前读过一本书在第二章就讲了许多,感觉挺有趣的。可惜某些必要的取址操作只能靠GCC扩展才行,标准C做不了。书名是《虚拟机:系统与进程的通用平台》。买了影印版来读。


最近我要看RPC/RMI原理方面的东西,要自己实现个简单的“玩具”RPC,有好书推荐吗?最好语言是用java,C#的也行。
4 楼 RednaxelaFX 2009-03-06  
night_stalker 写道
赞,学习到了
果然多吐槽是可以抛砖引玉的……

见笑了 ^ ^
昨晚我真的是困惑了,因为最近没在读YARV的代码了,一下不是太肯定到底状况是怎么样的了。今天找了时间Google了一下"yarv jit"关键字,效果不是太好。还是直接读代码了,呵呵。
到底哪个Ruby实现比较快我自己还真是没第一手资料的。主要是benchmark对我的意义不太大,我又不用RoR搭网站也不用它来写计算密集型应用,就是平时当计算器用用而已……但多线程的问题我想关注一下,还有Ruby 1.9的兼容性问题。天啊,在Ruby 1.9.1上用不了Mechanize/Nokogiri真是太要命了。

seraphim871211 写道
RednaxelaFX 写道

Hmm,我貌似是没记错。目前的Ruby 1.9.1里并没有JIT。

看到这个,心里就想不会这么巧吧,在看看个人信息,果然是你。汗~~,太专业了,你看看大家看不太懂就没人拍你了,哈哈。

呃……我之前做什么很糟糕的事情了么? =v=

对了,关于threaded code,以前读过一本书在第二章就讲了许多,感觉挺有趣的。可惜某些必要的取址操作只能靠GCC扩展才行,标准C做不了。书名是《虚拟机:系统与进程的通用平台》。买了影印版来读。
3 楼 seraphim871211 2009-03-06  
RednaxelaFX 写道

Hmm,我貌似是没记错。目前的Ruby 1.9.1里并没有JIT。

看到这个,心里就想不会这么巧吧,在看看个人信息,果然是你。汗~~,太专业了,你看看大家看不太懂就没人拍你了,哈哈。
2 楼 night_stalker 2009-03-06  
赞,学习到了

果然多吐槽是可以抛砖引玉的……
1 楼 robbin 2009-03-06  
这篇文章分析的好专业!

我来补充两点:

1、如同楼主所说,benchmark这个东西是有应用场景的。事实上现在rails2.3在ruby1.9.1上面的性能还不如ruby1.8.7,真让人失望~

2、JRuby评测速度虽然很不错,但实际运行rails的速度很缓慢,基本上还不具有实用价值。

其实以目前ruby 1.8.7的性能运行rails2.2也相当不错,也许只是我要求的太多,希望在很便宜的机器上面可以跑几百万PV这个目标太过分而已。

相关推荐

    如何监控JRuby脚本的执行

    由于JRuby是基于JVM(Java Virtual Machine)的,因此可以利用Java生态中的各种工具来对其进行监控和优化。本文将详细讲解如何监控JRuby脚本的执行,以及如何使用jprofiler这一强大的性能分析工具。 首先,理解...

    Ruby-JRuby一个Ruby语言的Java实现

    JRuby的核心目标是提供与原生Ruby解释器相当的性能,同时利用JVM的跨平台兼容性和企业级特性,如垃圾回收、线程支持和丰富的库。通过在JVM上运行,JRuby可以无缝地与Java代码交互,使得开发人员能够利用Ruby的生产力...

    jruby_windows_1_6_4安装文件

    3. **性能提升**:由于JRuby运行在JVM上,它可以利用JVM的优化技术,如Just-In-Time(JIT)编译,从而提高运行效率。 4. **Rails支持**:对于Web开发,JRuby支持Ruby on Rails框架,可以在Windows环境下搭建高效...

    java和JIT编译器版本.pdf

    总之,理解Java和JIT编译器版本之间的关系以及如何根据操作系统和硬件配置来选择合适的JIT编译器是优化Java应用程序性能的关键步骤。正确设置这些参数可以显著提升Java程序的运行效率,降低资源消耗。

    JRuby 实战入门

    5. **性能调优**:了解JRuby的性能特性,如JIT编译,如何监控和优化JRuby应用。 6. **部署与持续集成**:学习如何在生产环境中部署JRuby应用,以及如何将其整合到持续集成/持续部署(CI/CD)流程中。 通过阅读...

    Trace-based JIT简介(对Method JIT的改进)

    实验结果表明,对于具有深层嵌套方法调用和扁平执行路径的大型应用,Trace JIT能够提供显著的性能提升。 #### 未来工作与总结 尽管Trace-Based JIT展现出了巨大的潜力,但它也面临着一系列挑战,如如何更准确地...

    cpp-ZetaVM一个用于动态编程语言的虚拟机和JIT编译器

    ZetaVM是一款专为动态编程语言设计的高效虚拟机,同时集成了Just-In-Time(JIT)编译器,以提供更快的执行速度和优化的性能。在本文中,我们将深入探讨ZetaVM的核心特性、工作原理以及它在C/C++开发中的应用。 ### ...

    jit spray source code

    **JIT Spray技术详解** JIT(Just-In-Time)喷射,也称为JIT...通过分析`simple_sploit`和`advanced_shellcode`的源代码,我们可以进一步了解JIT Spray的具体实现和可能的变种,这对于安全研究和防御有着重要的意义。

    精益生产之JIT管理实战

    总的来说,JIT管理实战课程旨在通过深入理解JIT的理论基础和实践应用,帮助企业提升生产效率,减少浪费,提高产品质量,以适应不断变化的市场环境。学习JIT不仅可以优化制造过程,还能促进企业的竞争力和盈利能力。

    jit JavaScript

    JavaScript引擎如V8(Chrome和Node.js使用)、SpiderMonkey(Firefox使用)以及JSC(Safari使用)都实现了JIT技术。这些引擎在执行JavaScript时,会根据不同的策略选择何时进行编译。例如,V8采用了分层的JIT编译...

    JIT实现拓扑展现

    在IT行业中,"JIT实现拓扑展现"这个主题涉及到的是动态编译技术和网络拓扑图的可视化。这里,我们主要探讨JIT(Just-In-Time)编译器以及如何利用它来优化程序性能,同时也会关注如何通过编程手段将网络拓扑结构以...

    Writing JIT-Spray Shellcode for fun and profit

    由于原始材料中未提供具体链接,建议读者搜索相关安全研究机构(如Digital Security Research Group)的出版物和报告,以获取更多关于JIT-Spray攻击技术和实践的信息。此外,各大安全会议(如Black Hat)的演讲和...

    浅析dalvik虚拟机JIT技术的实现.doc

    这些文件中的函数如`dvmCompilerStartup()`、`dvmCompilerShutdown()`和`compilerThreadStart()`等,共同构成了JIT技术的基础框架。 #### 编译流程与代码缓存 JIT技术的编译过程分为多个阶段,包括方法编译(`...

    JIT Spray技术.pdf

    本文档探讨了两种新型技术来绕过这些缓解措施:指针推断(Pointer Inference)和即时编译喷射(JIT Spraying)。这两种技术充分利用了浏览器内广泛使用的高级脚本解释器或虚拟机的攻击面。 #### 关键技术介绍 ####...

    精益生产之JIT实务

    JIT实务涉及到企业管理的多个方面,旨在通过最小化库存、优化流程和提高质量来降低成本并提升竞争力。 在JIT实务中,首先要理解八大浪费,这是精益生产的基础。八大浪费包括: 1. 不良、修理的浪费:由于质量问题...

    cpp-Minijit用C和Python中从头开始编写的教育目的x8664JIT编译器

    5. **调试辅助**:Python脚本可以方便地生成和查看编译后的机器码,这对于理解和调试JIT编译器非常有用。 在压缩包"minijit-master"中,你可能会找到以下内容: - 源代码文件:C语言和Python的实现。 - 示例代码:...

    jvm初识及JIT优化

    jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识及JIT优化jvm初识...

    比照MRP和JIT谈3C理论在采购补给中的应用

    然而,传统MRP(Material Requirements Planning)和JIT(Just-In-Time)在材料需求规划上存在一些问题。在重复性生产的情况下,传统MRP进行销售预测时未考虑到企业产能的限制,以及预测数量的不确定性。这将导致...

    JIT(Just In Time)采购知识-准时化采购

    JIT(Just In Time)采购,也称准时化采购,是一种源自日本丰田公司的管理理念,旨在通过精确控制供应流程,确保在正确的时间、正确的地点、提供正确数量和质量的物料,以满足生产需求,同时最大限度地减少库存和...

Global site tag (gtag.js) - Google Analytics