12月11日~13日、ベルサール汐留にて「RubyKaigi 2015」が開催されました。今年も基調講演が毎日一つずつ行われました。その模様をレポートします。
RubyKaigi 2015 最終日3日目の基調講演はEvan Phoenixさんです。Ruby 3x3を受けて、Rubyを速くするためにできる最適化の方法を提案しました。発表タイトルは「Ruby: 2020 - How do we get to Ruby3x3」です。
Evanさんがやってきたこと
Evanさんは、Rubyの実装の一つであるRubiniusや、Rubyで書かれたアプリケーションサーバPumaの作者であり、RubyGemsとCRubyのコミッタでもあります。また、RubyConfやRailsConfの主催などをしているRubyCentralのディレクタでもあります。
そんなEvanさんは過去に自身が行った、RubyとRubyniusの速度改善を紹介しました。
- SydneyというRuby1.8.2に行なった並列化パッチ
- RubiniusにおけるRubyの並列化実装
- RubiniusにおけるLLVM向け動的最適化JITの実装
そして今年のRubyConfでの、まつもとさんの発言「Ruby 3はRuby 2より3倍速くする」というRuby 3x3を取り上げ、我々はどのように実現できるかを考えたそうです。その結果、Evanさんは3倍速くするためCRubyにJITを導入する必要があると語り始めました。
パフォーマンスを向上させるには
まず、プログラムのパフォーマンスを上げる作戦として、次の2つの方法があると述べました。
- 同じ量のコードを並列に処理できるようにする
- 同じ処理をするのに実行されるコードの量を減らす
並列化
コアライブラリが並列化に対応していないCRubyでの並列化については、修正は大変だと言われています。過去Evanさんが実装したSydneyでは500kのパッチが必要になったそうです。
コアライブラリの並列化以外で並列化での高速化を実現するために、ActorモデルやStreamを使えるようにする方法がありますが、この方法ではプログラムを実装する人が並列化手法を意識して使う必要があります。
実行されるコードの量を減らす
そのため、実行されるコードの量を減らす方法として、JITを活用するのがよいと述べました。Rubyは動的な言語のため、JITによる最適化が困難と思ってしまいますが、Evanさんによると90%以上のメソッド呼び出しにおいては単一のクラスを返すだけのようです。
そしてRubyのコードを用いたJITによる最適化の例を挙げ、JITによる高速化のメリットを述べました。
JITによる過去の高速化調査
次にEvanさんは、SELFやStrongtalk、V8で行なわれた最適化の実例を挙げ、そこから学んだことを話しました。
これらの実例より、他の言語での最適化としてよく取り入れられたJITを導入することが、パフォーマンス改善の鍵になると述べました。
JITの種類
JITコンパイラで用いられる方式として次の3つを紹介し、特徴を述べました。
- Tracing JIT
- Method JIT
- Partial Evaluation
Tracing JITは、プログラムの中で同じ処理が何度も実行されたときに行う最適化です。この方法ではコードがどの経路を辿るかの管理が非常に重要になり大変なのですが、現在のプロセッサにおいて分岐は軽い処理とされるのであまり有効にならないと言います。
Method JITは、メソッドが何度も呼び出されるときに行なう最適化です。この方法だと、一度コンパイルされたメソッドは再利用できますが、コンパイルには時間がかかり、インライン化によってメソッドのサイズが大きくなります。
Partial Evaluationは、Truffle + Graalとして知られる古典的な手法です。高いパフォーマンスが得られる一方、起動やプリコンパイルが遅かったり、既存の研究もあまり存在しなかったりするものだそうです。
最近の動向としては、多くのVMはMethod JITの方式に向かっているとのことです。
Ruby 3x3の目標として、次のコードをカナリアコードと名付け、これが最適化された結果1を返すようにならないといけないとしました。
Rubyがやるべき最適化の提案
Evanさんはここまでの調査を踏まえ、RubyのコアライブラリをYARVのバイトコードからLLVMのバイトコードに変換する方式を提案しました。これにより、LLVMが持つJITの最適化の恩恵を受けられるようになると言います。この提案が実現すると、先程見せたカナリアコードをどのように最適化できるかを変換過程を交えながら示しました。
さらに、ブロックを呼び出すときのCの関数であるrb_block_callやrb_funcallを別のメソッドや関数に変換したり、コードにキャッシュを加えてキャッシュを使ったりすることも改善に繋ると言います。
また、この方法は開発者に易しい手法だとも述べます。それは、最適化のためのヘルパーコードを自動的に追加するツールを用意できたり、既存のコアライブラリをそのまま流用できるためです。
そして、「MIPASWAP(Matz is Pragmatic So We Are Pragmatic)」という標語をあらたに掲げ、研究だけでなくよりプラグマティックになるべきだと話しました。
まとめ
最後にRubyを3倍速くするの難しいことだが、実行時に動的な箇所を減らし、JITを行なうことで実現が可能になると今回の提案をまとめました。 そしてEvanさんは「現在CRubyは多くの人や会社が使っており、我々の力でCRubyを次のレベルにすることができる。みんなで今すぐ行動しよう」と会場にアピールをして、発表を締めくくりました。