BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース JRubyの新IRが将来のパフォーマンス改善を容易にする

JRubyの新IRが将来のパフォーマンス改善を容易にする

原文(投稿日:2009/11/24)へのリンク

JRubyがパフォーマンスの点で非常に改善した。1年前、JRubyは、 Ruby 1.8の実装では最速だった 。JRubyのパフォーマンスは、リリース毎に上がってきたが、Subbu Sastry氏が開発している新しい中間表現(IR)により、更なる改善が、見えてきた。

InfoQは、Subbu氏に、この新IRがどのようなもので、JRubyにどのような恩恵をもたらすのか、聞いた。

新しいIRについて話す前に、現在のコンパイラは、IR無しでどのように動いているのですか?

厳密に言えば、AST(抽象シンタックス・ツリー)もIR(中間表現)です。というのは、ソースというわけでもなく、ターゲット言語でもないわけですから。IRは、抽象的な表現(イン・メモリのデータ構造、"アセンブリ言語"の出力など)で、ソース言語のセマンティクスを捉えるのに選ばれました。しかしIRは、実装するにも最高です。

もしJRubyがインタプリタ(言語のリファレンス/プロトタイプの実装を行う最高でもっとも易しい方法)として、始めていたら、AST(そしておそらく他の理由)を操作するC言語によるMRI(まつもと氏によるRubyの実装)に完全に準拠したものでなければならなかったでしょう。ASTが、IRの選択肢としての意味はあります。

(JRuby AST は、パーサの出力で、単にソースコードのツリーによる表現です。異なった種類のノード、例えば、クラス、メソッド、変数を持ちます。それはまた、例えば、自動のリファクタリング提供するために、コードを分析するツールによって使われます。)

この数年にわたって、パフォーマンス上の懸念から、ASTをネイティブなバイトコードに翻訳するコンパイラの開発に至りました。そのため、解釈に必要なオーバーヘッドが減少し、またHotSpotは、最適化(インライン化など)やハードウェアにネイティブなコードにコンパイルすることができるようになりました。コンパイラは、なんでもやみくもにコンパイルするわけではありません。私は、実行前(AOT)コンパイラとして、配布できると考えてますが。メソッドの実行カウンタがある閾値を超えたら、コンパイラが作動するわけです。

Tom Enebo氏は、 "JITの閾値は、単なるメソッドのインボケ―ション・カウンタです。カウンタは、(デフォルトでは、今50です)次のように設定できます:

   -J-D jruby.jit.threshold=<インボケ―ション・カウンタ> 

いくつのメソッドをJITするのかを制御するように、JITに対して、まだいくつかの調整ができます。"

では、新しい中間表現は、ASTと比較してどのようなものでしょうか?

ASTは、インタープリテーションには、いいですが、"伝統的な"(そして他のRuby特有な)コンパイラの"最適化"を実装するとなると、おそらくIRとして最高の選択肢では、ありません。新IRは、ASTをややハイレベルなアセンブリ言語のような、連続した命令に翻訳します。1つの命令は、単なるオペレーション(ブランチ、コール、変数の受信、リターンなど)で、たくさんのオペランド(変数、固定長整数、浮動小数、配列、クロージャなど)を操作します。最初、たくさんのオペレーションは、メソッド呼び出しになりますが、これらのいくつかは、最適化され(インライン化される、など)ネイティブ(すなわちjvmネイティブ)なオペレーションになることが、期待されます。

(IRがどのようなものか理解するために、 JRubyのメーリングリスト に載っているこの記事を見てください)

IRは、また他のデータ構造の集まりを含みます、例えば制御フローグラフ(制御のフローを表す)、クラスやメソッドの階層、そして後ほど、おそらく更によい最適化のために 静的単一代入 (SSA) 形式などです。終始一貫した目標は、フロントエンドからの情報をできるだけ多く保持しながら、ソースを正規化形式で表現することです。コンパイラが進むと、ハイレベルのIR命令は、ローレベルのIRに分解されます(コールは、ルックアップ、メソッド・テーブルのロードそしてデスパッチに分解できます)あるいは、他の暗黙の情報が明示的になります(ヒープ・フレームの割り当て、ヒープ・フレームのロードとストア)そのため、更に最適化できます。

"アセンブラ言語"の命令の集まりとして、この新IRは、その表現においてもっと線形です。 – ツリーではありません – 先に述べたように、制御フローやしばしばデータフローを明示的に表現する他の補助的なデータ構造(グラフ)があります。

この新しい表現は、他にどのような恩恵をもたらしますか?

この新IRは、伝統的なコンパイラの最適化を実装するのに、もっと適しています。更に、我々は次のような技術を実験するつもりでいます。(a)クロージャをインライン化する (b) メソッド呼び出しをインライン化する(c) アンボクシングしながら固定長整数/浮動小数のコールをネイティブな整数/long(ロング)/float(浮動小数)/double(ダブル)のオペレーションに変換する。 (d) インラインされないクロージャに対して、ヒープからのロード/へのストアに伴うオーバーヘッドを減らすことなどの技術ですが、たぶん他の技術も実験します。このようなことは、ASTで扱うのは、非常に難しいでしょう。

更に、期待できるのは、このIRは、より少ないメモリで済みます(もちろん、適切に実装すればですが) – メモリが最も要るのは、最適化のフェーズだけです。オペレーションの正規表現として、単純なインタプリタ実装(IR命令を基に)が可能で、数十のオペレーションを扱うだけで済み、これがASTのノードタイプだったら数百も処理しなければなりません。

いつこのIRは、JRubyに使われるのか、計画が既にありますか?

この新IRが実現化するまでには、まだ時間がかかるでしょう。口を滑らさないほうがいいでしょう。いつだなんて言えません。起きる時には、起きます。私にとって、これは、楽しいプロジェクトです。 :-)

しかし、我々は、計画している第一段階は、ASTノードではなく、新しいIR命令を基にしたインタプリタを作ることです。こうすることで、捉える必要のあるすべての情報を捉えたか、ということに関して、我々の基盤を成し遂げた、という自信を持てるようになりますし、インタプリタのパフォーマンス改善(結局、我々は、本当に基盤からはずれてしまうこともあり得ます)についての我々の仮説をテストするのに役立ちます。そしてプロファイリングを実装する基盤を提供するのにも役立ちます(今度は、そのことが、コンパイル システムを注目すべき領域に導くのです)。

しかし、IRは部分的に盛り込まれています(取り組んでこなかった若干のASTノードもまだあります)、制御フローグラフ、いくつかのローカルな最適化、データフローのフレームワーク、いくつかのデータフロー解析やパス、クロージャのヒープ・ロード/ストアに関する最適化が盛り込まれていて、そしてこの数カ月にもっと多くのことが加えられます。

手短に言うと、JVMの上に階層的に実装する方法に、代わるものは実際ないと思います。それは、単にRubyとJVMのセマンティクスが合っていないからです。色んなそして長い議論のある、Rubyが持つすべての動的なセマンティクスを、JVMが最適化できるとは、期待できません。

IRのコードは、 on GitHubで見ることができる。

この記事に星をつける

おすすめ度
スタイル

BT