RubyKaigi 2017 Keynoteレポート

Vladimir Makarovさん「3x3を達成するには,極めて大きな改善が必要だ」Towards Ruby 3x3 performance 〜RubyKaigi 2017 基調講演 3日目

この記事を読むのに必要な時間:およそ 2.5 分

9月18日~20日,広島国際会議場にてRubyKaigi 2017が開催されました。3日目の基調講演は,RedHatのトロントオフィスで働くGCCの開発者であるVladimir Makarovさんです。

Vladimirさんは,Ruby 2.4で導入された新しいハッシュテーブルの実装者であり,20年以上に及ぶGCCの開発経験を持つ,コンパイラ開発のスペシャリストです。今回,Vladimirさんは「Towards Ruby 3x3 performance」と題して,発表しました

画像

Stack machineをRTLへ

MRI(Matz' Ruby Interpreter,以下 Ruby)では,1.8.7まで作者のまつもとゆきひろさんが開発していたインタプリタが使われていました。そして,バージョン1.9でささだこういちさんが開発したYARV(Yet Another Ruby VM)に置き換えられました。YARVはstack machineを採用したVMですが,Vladimirさんはまず,このstack machineをRTL(Register Transfer Language)へ置き換える変更を実装しました。

Stack machineとRTLではそれぞれ優位な点が異なりますが,命令数が短くなること,JITコンパイラとコードを共有することが可能になること等,RTLには今後の実装上で好ましい点が多くありました。そこで,まずはRTLへの移行を実装しました。

画像

現在のRTLの実装の段階としては,make testによるテストはすべて通っているという段階です。make testは,Ruby本体のテストを実行するコマンドで,リポジトリにおけるtest/以下のテストを実行します。RubySpecは実行されませんが,最初のステップとしては非常に良いスタートであると言えるでしょう。また,マイクロベンチマークでは,既に最大110%のパフォーマンス改善を達成しています。しかしながら,RTLの難点であるコードサイズが大きくなってしまうこと,また,オペランドデコーディングのオーバーヘッドが依然として大きいことがこれからの課題として存在します。そのほかにも,Rubyプロセスを実際に長時間起動させた場合の安定性については検証されておらず,これについても今後の課題となっています。

画像

JITの実装比較

次に発表では,JITの実装方法をいくつか紹介し,比較検討しました。JITを使ってパフォーマンスを改善するには,大きく分けて3つの方法があります。

  1. フルスクラッチでJITコンパイラを実装
  2. 最適化コンパイラを利用
  3. 既に広く使われているJITコンパイラを利用

1のフルスクラッチJITコンパイラを採用しているのは,LuaJIT,JavaScript V8等があります。特定の言語に対する最適化を自由に実装できるため,最もパフォーマンスを改善しやすい手法です。しかしながら,JITコンパイラをフルスクラッチで開発することは非常にコストと時間がかかり,メンテナンス性もよくありません。

2のGCCやLLVMに代表される最適化コンパイラを利用する方法では,GCCでは300以上の最適化パターンが実装されているためパフォーマンスを改善しやすく,Rubyのコンパイルでは既にGCCを使っているため,新しい依存関係を追加する必要もありません。現在の実装を大きく変更する必要もないので,メンテナンス性にも優れています。しかし,すべての最適を施そうとすると,コンパイル時間が非常に長くなってしまうという問題があります。

3のJVM,GraalVMに代表される,既に広く使われているJITコンパイラを利用する方法では,安定していて,かつ,信頼のおける最適化を適用できます。しかし,主要な既存JITコンパイラは,既にJRubyやTruffleRuby等のRuby実装で使われています。

画像

開発リソースに乏しいRuby coreチームでは,フルスクラッチでJITコンパイラを実装することは現実的ではありません。また,JVMやGraalVMは,先述のような別の実装が開発されており,現在のMRIの実装を大きく変更する必要もあります。一方,GCCやLLVMにおけるコンパイル時間が長くなってしまう問題に対しては,パフォーマンスに貢献しない最適化を無効にすることで対処可能です。これらを総合して考えると,2の最適化コンパイラを使うのが,Rubyプロジェクトにとって最も適切な選択だと判断しました。

MRI JIT(MJIT)

では,MRI JITがどうやって最適化を施しているかをみていきましょう。MJITの実装を簡潔に言うと,Cコードを動的に生成し,それを実行,さらにそのCコードに対しても最適化を施す,という手法を採用しています。

Cコードを動的に生成して最適化を図る手法は,処理が重く,筋の悪いジャンキーな手法であると考えられていることもありますが,注意深く実装することで効率的な最適化を行うことができます。また,新しい依存関係の追加も必要なく,デバッグも容易に行うことができます。

画像

画像

著者プロフィール

西嶋悠貴(にしじまゆうき)

Ruby core コミッタ,did_you_mean gem 作者,kaminari gem メンテナ。Shutterfly,Pivotal NYオフィスに勤務後,現在はArtsy NYオフィスのシニアソフトウェアエンジニアとして開発に従事。

Twitter:@yuki24
GitHub:yuki24

コメント

コメントの記入