Ruby 1.9はRubyの世界を1.8.xのユーザ空間スレッドシステムからネイティブスレッドのシステムに変えた。1.9のネイティブスレッドはGVL (Global VM Lock:大域的な仮想マシンのロック)に苦しんでいて、それによって一度にたった1つのRubyスレッドしか実行することができないが、ネイティブスレッドへの移行は別の恩恵をもたらした。
Joe Damato氏はRuby 1.8.xのスレッド実装の問題があり、その問題が1.9のネイティブスレッドではなくなっていることを明らかにした。手短に言うと: 1.8.xではコンテキストスイッチの効率がかなり悪いのだ。それはスレッドの完全なスタックの中身を(中断するスレッドに対しては)スタックからヒープへ、スケジュールされたスレッドに対してはその反対方向にコピーすることに原因がある。大きなスタックを持っていたり、大きなスタックフレームを持っているアプリケーションはこの実装の詳細に苦しめられているのだ。
ネイティブスレッド実装は複数のスタックを管理してそれらを取り替えることでこの非効率なことが起こらないようにしている。Joe氏の投稿はこのアプローチをRuby 1.8.xにもたらす彼の"heap stacks"についての非常に詳細な説明になっている。
パフォーマンスの改善は著しく、2倍から10倍も増加しており、そのベンチマーク結果は1.9.1のベンチマーク結果に近いところまできている。
コードのパッチがあたったバージョンはGitHubで利用可能である: 1.8.6向け、1.8.7向け。
Heap Stackソリューションは継続とGCのいくつかの長く存在していた問題を修正するMBARIパッチと並んで、Ruby1.8.xの最大の非効率性を根絶するもう一つの試みである。
Rubyのパフォーマンスをよりよくする別の道がMacRubyプロジェクトから始まっていて、そこでは最近LLVMをベースとしたVMに関する作業が始まっている。その作業のいくつかはすでにRubyの事前 (Ahead Of Time, AOT) コンパイラをつくるのに使われている。 ここで、AOTとはジャストインタイム (Just In Time)コンパイラと対比的なもの、すなわち、実行時にコンパイルする代わりに、AOTコンパイラを実行するとソースコードから実行形式を生成する:
表現(ソースコード)はまずLLVM中間表現に、そのあとでビットコードに、さらにアセンブリに、そして機械語にコンパイルされるんだ。本当のコンパイルだよ:-)
このことが有用なシナリオがたくさんある:
1) コードの難読化、2) 動的コード生成が許されない環境においてRubyを利用する、などにとって有用となるだろうね:)
最後に、プロファイラはアプリケーションのボトルネックを明らかにする1つの方法である。Ryan Davis氏は彼のZenprofileプロファイラを更新し、メソッド呼び出しを追跡する効率的な方法としてRubyランタイムにおけるイベントフックを利用するようにした。Zenprofileはここしばらく使われているが、更新されたバージョンでは、フックを設定するためのネイティブコードの必要性を取り除いたevent_hook gem に依存するようになっている。event_hookを使うことで、今やRubyインタープリタにフックをかけるためにネイティブコードを書く必要はなく、純粋なRubyのイベントフックを書くだけでよい。Zenprofileはそれを利用して、そのプロファイリングロジックの純粋な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