Ruby 3 JITの最新情報: 現状と今後(翻訳)

概要 原著者の許諾を得て翻訳・公開いたします。 英語記事: Ruby 3 and JIT: Where, When and How Fast? 原文公開日: 2017/12/26 著者: Noah Gibbs サイト: Appfolio Engineering 画像は英語記事からの引用です。 Ruby 3 JITの最新情報: 現状と今後(翻訳) Ruby 3にJITが導入されるというニュースは既にお聞きおよびかと思います。JITはどこからやってくるのでしょうか?いつ導入されるのでしょうか?それでどれだけ高速化されるのでしょうか?JITがあるとデバッグが心配な場合にオフにできるのでしょうか? そもそもJITって何? JITは「just-in-time」の略ですが、日常会話の「間に合ったー!」「滑り込みセーフ」の意味でなければ、特に実行時コンパイラ(Just-In-Time Compiler)を指します。現在のRubyはコードを逐次解釈して実行するインタプリタですが、JITを用いると、Rubyプログラムの一部をマシン語に変換して、Unixコマンドやexeファイルのように実行します。特に、JITは私たちが日頃読んでいるRubyコードを、プロセッサに合わせて最も自然かつ高速なコード(しばしば機械語やマシン語(”machine code” or “machine language”)などと呼ばれます)に変換します。 JITは「普通の」コンパイラといくつかの点で異なっています。最大の違いは、プログラム全体をコンパイルするわけではない点です。その代わり、最も頻繁に実行される部分だけをコンパイルし、そのプログラムでの使われ方にうまく合わせて最速で実行できるようにコンパイルします。JITは、プログラムでメソッドがどのように呼ばれているかについて推測する必要はありません。プログラムをしばらく監視して情報をメモし、それからコンパイルを開始します。 「プログラマーが合法的におさぼりする言い訳No.1:『コンパイル中です』」 「おらっ!仕事中だぞ」「コンパイル中でーす」「さよか」 うう、JITのせいでこの言い訳が使えなくなってしまいました。 今後の言い訳には「AoT設定をデバッグ中」とか「ETLスクリプトを実行中」が当分おすすめです。 JITでどれだけ速くなるか この世には「嘘」、「ひどい嘘」、そして「ベンチマーク」があります(訳注: 元ネタ)。JITによる高速化を正確な値で表すなど無理な相談です。正確な値など存在しないからです。しかし、特定のプログラムについては、完全に合理的な負荷をかけた状態で50%、150%、あるいは250%までJITで高速化できるケースが多々あります。現実的な負荷の元で、500%以上もの高速化を達成するケースすらいくつかあります。ただし、JITよりインタプリタの方が速いケースも若干あることは言うまでもありません。なぜなら、現実世界には常に最適化されているものなど存在しないからです。 現時点での無難かつシンプルなCRuby向けJIT実装では、およそ30%〜50%のパフォーマンス向上が見られ、測定方法次第では最大150%に達することもあります。30%〜50%はJITにしてはかなり控えめな値ですが、これらのブランチはまだまだシンプルですし、30%〜50%という値は過小評価のしようがありません。これは3年分から10年分の「通常」リリースに相当するスピードアップであり、わずか1〜2年のJITへの取り組みでこれほどの成果を達成したのです。そして通常のスピードアップに加えて、こうした大きなスピードアップは現在も起き続けています。さらにJITは長期に渡って改良を重ねることができます。JITは、昔ながらの「純粋なインタプリタ」のRubyでは到底不可能だった最適化の世界へと大きく扉を開いているのです。それこそが、JITを搭載したRuby実装が既に劇的な高速化の可能性を秘めている理由です。TruffleRubyなどの実装は著しいメモリオーバーヘッドを伴いますが、コードを900%あるいはそれ以上スピードアップできます。この戦略はCRubyには合いませんが、それでも高速化は確かに可能なのです。 私は、「どんだけ速くなる?」という質問にはたいていRails Ruby Benchの結果を添えて回答しています(結局私の作ったgemではありますが)。しかし現時点のMJITは、大規模な並列実行を行うRailsアプリを実行できるほどには安定していません。ご心配なく、そのときが来れば結果を公表いたします。 これは直近の値ではありません。かつMJITやYARV-MJITの値は今も激しく意味深な変動を繰り返しています。しばらくお待ちください。 CRubyのJITはどこから来たのか RubyのJITは、しばらく前からある形を取って導入されてきました。JRubyには何年も前からJITがありますし、RubiniusにもしばらくJITがありましたがその後取り除かれました。しかし「純粋な」CRubyにJITが組み込まれたことはかつてありませんでした。その代わりさまざまな実験用ブランチとしてJITが姿を表しましたが、Rubyリリースに取り入れられたことはなかったのです。 Shyouhei Urabeによる”deoptimization”ブランチはかなりよかったのですが、大きな成功には至りませんでした。このJITは非常に純粋かつ非常にシンプルであり、可能な最適化はごくわずかにとどまりましたが、その代わり余分なメモリをほとんど必要としないことが保証されていました。Rubyコアチームはメモリ使用量にとても気を遣っています。 その後、最近になってVladimir Makarov(Ruby 2.4のハッシュテーブルを再構築したあのMakarovです)が、メモリを食わない強力なJIT実装「MJIT」を作りました。MJITは既存のCコンパイラ(GCCやCLang)をRuby JIT向けに強化します。MakarovがMJITの動作を解説するキーノートスピーチのためにRubyKaigiに招かれたのも、MJITへの期待の大きさゆえです。Makarovは最初にRubyを改造して、スタックベースのVMではなくレジスタベースのVMを用いるようにし、その基礎の上にJITを構築しています。しかしMJITはまだ歴史が浅く、一般的にリリースできるほどには安定していません。どんなRubyプログラムでも動かせる、クラッシュしないRubyを作るのは困難であり、MJITはまだその段階にありません。しかし最近の結果によるとMJITのCPUベンチマークはRuby … Continue reading Ruby 3 JITの最新情報: 現状と今後(翻訳)