性能快报:Heap Stacks提升1.8.x线程性能 & MacRuby AOT & ZenProfile和EventHooks

Ruby 1.9把Ruby世界从1.8.x的用户空间线程(userspace thread)系统带入原生线程(native thread)。尽管全局VM锁(Global VM Lock,VM)仍然影响着1.9的原生线程,使得每次只有一个Ruby线程能被执行,但是向原生线程的转变也带来了其它的好处。

Joe Damato对Ruby 1.8.x的线程实现中的一个问题进行了研究,在1.9中由于原生线程的引入,该问题不复存在。简单地说:由于线程的上下文切换会导致线程栈内容的完全复制,如(针对挂起线程)从栈到堆的方向,还有针对被调度线程的另一方向,这样做开销非常巨大。大凡栈分配得大或者带有巨大栈帧(stack frame)的应用程序,都难拒其扰。

通过维持多个栈并在这些栈之间进行切换,原生的线程实现就不用担心出现这种低效率情况的尴尬了。Joe的文章详细描述了他的“heap stacks”方案,并把这套方案带到了Ruby 1.8.x上。

如此一来,结果非常明显地体现在了性能的提升上──2至10倍的性能改善,这使得基准测试的结果一举逼近1.9.1。

从GitHub上我们可以找到给1.8.6和1.8.7打过补丁的代码。

Heap Stacks方案是为了根除Ruby 1.8.x中最大的低效率问题的又一次尝试,除此之外,另一个就是解决了部分长期困扰Continuation和GC方面问题的MBARI补丁。

条条大路通罗马,要让Ruby实现更高的性能,别的路也行得通:就在不久前,MacRuby项目开始了实现基于LLVM的VM的工作。现在这项工作已经有一部分被用来为Ruby创建一个提前编译(Ahead Of Time,AOT)的编译器了。在这里,AOT是和JIT(Just In Time,实时)编译器相对应的──也就是说,AOT编译器的运行,并非在运行期编译,而是从源码生成出可执行文件:

表达式会被编译成LLVM IR,然后转换成位码(bitcode),再变成汇编语句,最后变成机器码。这是真正的编译 :-)

对于许多场景这样做确实是很有用的:

这样做有利于1)代码混淆,及2)在动态代码生成不允许的环境下使用Ruby。

最后,剖析器(profiler)可以用作来发现应用程序瓶颈的一种手段。Ryan Davis更新了他的zenprofile剖析器。该剖析器使用Ruby运行时的事件钩子作为跟踪方法调用的有效方式。说起来Zenprofile也颇有一段历史了,不过目前更新的版本依赖于event_hook──这个gem把建立钩子所须的原生代码单独抽取了出来。藉由event_hook,我们可以不必大费周章地通过编写原生代码给Ruby解释器挂上钩子,现在只需编写纯Ruby代码就可以实现事件钩子了。Zenprofile使用了event_hook,提供了一套剖析逻辑的纯Ruby版本,以及使用了RubyInline和C作为原生代码的一套更快的版本。

简单看一下zenprofile的代码,我们可以发现使用event_hook非常简单,只需要扩展EventHook类,然后覆盖某些方法,如def self.process event, obj, method, klass,就可以捕获事件了。

Zenprofile还提供了可用于关注个别方法性能的spy_on功能。该功能可通过Ruby代码进行配置,如要关注Integer#downto的性能,下面是一段来自misc/factorial.rb的例子:

require 'spy_on'
Integer.spy_on :downto

查看英文原文:Performance Roundup: Heap Stacks Boost Threads in 1.8.x, MacRuby AOT, ZenProfile and EventHooks

你可能感兴趣的:(性能快报:Heap Stacks提升1.8.x线程性能 & MacRuby AOT & ZenProfile和EventHooks)