Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

YJIT: CRuby向けの新しいJITコンパイラを構築する(翻訳)

概要

Shopify Engineeringの許諾を得て翻訳・公開いたします。本記事は公開前にShopify Engineeringにレビューをいただいています。

画像は元記事のものです(追記2021/11/04: 冒頭イラストを削除いたしました)。

本記事はTwitterにて@shiroemonsshiromemonsさまからのリクエストを受けて翻訳いたしました。リクエストありがとうございます!

なお、以下のissue #18229で、著者のMaxime Chevalierさんをコミッターに迎えるプロポーザルが出され、その後コミッターとして登録されました。おめでとうございます🎉

また、週刊Railsウォッチ20211026後編で報じたように、YJITがRuby 3.1向けにmasterブランチにマージされました。おめでとうございます🎉

Shopify/yjit - GitHub

YJIT: CRuby向けの新しいJITコンパイラを構築する(翻訳)

1980年代〜1990年代にかけて、Perl、Ruby、Python、PHP、Javascriptなどのインタプリタ型かつ動的型付けのプログラミング言語が産声を上げました。これらの言語はパフォーマンスより使いやすさや柔軟性を重視していました。こうしたプログラミング言語はさまざまな意味で、当時を取り巻く状況を反映していました。90年代はドットコムが隆盛を極めていた時代で、CPUクロックスピードはおよそ18か月ごとに倍増していました。この成長ぶりは永遠に続くように思われ、ソフトウェアを高速に動かすための努力は不要でした。コンピュータはひたすら速くなっていたので、問題は自然に解決されたのです。しかし今日の状況はやや異なっています。現代のシリコン製造技術は限界に達していて、もはやシングルコアのパフォーマンス強化だけではパフォーマンス問題を解決できなくなっています。また、モバイルデバイスや環境への配慮という観点から、エネルギー効率の重要性も認識され始めています。

私は昨年のパンデミックの最中に、Ruby on Railsで動く巨大なサーバーインフラを運営するShopifyで職を得ました。大勢のソフトウェアエンジニアが所属するチームに参加して、CRubyインタプリタやCRubyのガベージコレクタの最適化から、Rubyの別実装であるTruffleRubyの実装まで、さまざまな方法でRubyコードのパフォーマンス向上に取り組みました。私はそのときから、ShopifyとGitHubのベテランエンジニアチームとともに、CRubyの新しい組み込みJIT(just-in-time)コンパイラであるYJITを手がけています。

スピードという機能は過小評価されているので、このプロジェクトはShopifyと世界中のRuby開発者にとって重要です。CRubyには、既にMJITと呼ばれるJITコンパイラがあり、3年前から開発が継続されています。MJITは小規模なベンチマークでは高速化を達成していますが、Ruby on Railsのように広く普及しているRubyアプリケーションを実際に高速化することについてはまだ成功していません。YJITは「データドリブン」アプローチを採用し、特にRailsやShopify Core(ShopifyのメインとなるRailsモノリス)のパフォーマンススポットにフォーカスしています。

YJITとは

YJITについてツイートするTobi Lütke

YJITは、CRuby内部にJITコンパイラを段階的に構築し、JITで実行されるコードを徐々に増やすことで、最終的に実行の大半でインタプリタを置き換えることを目指すプロジェクトです。このコンパイラは近日中にCRubyに正式に取り込まれる予定で、私が博士号取得中に開発を始めたJITコンパイラアーキテクチャである『Basic Block Versioning(BBV)』をベースにしています。YJITについては今年のMoreVMs 2021のワークショップで講演し(↓上の動画)、RubyKaigi Takeout 2021でも講演しました(↓下の動画)。私たちのアプローチについて詳しく知りたい方はぜひご覧ください。

現時点の結果

YJITプロジェクトは現時点で1周年を迎えようとしていますが、MoreVMsの発表から大幅に改善されたことに私たちは満足しています。 一連のベンチマーク結果では、CRubyインタプリタと比較してrailsbenchで20%、liquidテンプレートレンダリングで39%、Active Recordで37%のスピードアップを達成しています。また、YJITはウォームアップが非常に高速です。YJITはどのベンチマークでも1回のイテレーションでほぼパフォーマンスのピークに到達し、最初のイテレーションでもすべてのベンチマークでインタプリタと同等以上のパフォーマンスを達成しています。

A bar graph showing the performance differences between YJIT, MJIT, and No JIT.

インタプリタのパフォーマンスを1.0としたときのベンチマーク速度(イテレーション/秒)、高いほどよい

YJITをCRuby内部で構築するにはいくつかの制約があります。つまり、JITコンパイラをCで書かなければならず、高パフォーマンスのJITコンパイラ向けに作られていないCRubyコードベースの設計上の決定に対応する必要があります。しかしYJITは、既存のRubyコードやパッケージとの互換性をほぼ100%維持できるという重要なメリットがあります。約30,000件のテストで構成されているCRubyのテストスイートにパスし、300万行を超えるコードを含み500個以上のRuby gemに依存するコードベースのShopify Coreでも、GitHubバックエンドCIでも、すべてのテストにパスしました。しかも、Shopify社内にある一部のproductionサーバーにも実際に導入されています。

YJITのBBVアーキテクチャは、動的に型付けされたコードをコンパイルするうえでいくつかの重要なメリットがあると私たちは信じています。コード生成パイプライン全体をエンドツーエンドで制御することで、現在のGCCベースのMJITアーキテクチャではできないことが可能になります。特に、YJITは型情報を元にコードを素早く特殊化し、プログラム実行時の挙動に応じて実行時にコードにパッチを適用できます。コンパイル速度やウォームアップ時間についても優位を保っています。

次なるステップ

YJITチームは、このコンパイラをRuby 3.1にマージするようRubyのコア開発者たちから依頼を受けました。私たちの成果が正式にRubyに取り入れられることは、私にとっても同僚にとっても大変光栄です。数か月もすれば、すべてのRuby開発者がRubyバイナリにコマンドオプションを渡して実行するだけでYJITを試せるようになります。しかし私たちの旅はまだ終わっていません。YJITとCRubyをさらに高速化する計画が既に進行しています。

現時点では、railsbenchのCPUインストラクションのうちYJITで実行されるのは79%程度で、残りはRubyインタプリタで実行されています。つまり、現在の結果を改善する余地はまだ残されているということです。YJITが今後進む道筋は明確で、私たちはYJITが現在よりもずっと高いパフォーマンスを提供できると信じています。しかしYJITを構築するにはCRubyの実装を詳しく理解することが不可欠でした。そしてその結果、CRubyアーキテクチャの内部でさらに高いパフォーマンスを引き出すために改善可能と私たちが信じている、重要な要素をいくつか発見しました。発見された改善はYJITのみならずMJITにも有用で、中にはRubyインタプリタを高速化する改善もあります。そういったわけで、今後の改良作業の一部はYJITとは別に上流工程で行うことになるでしょう。

改良作業のいくつかについては、今後このブログ記事で紹介する可能性もありますが、ここでは私たちが取り組んでみたいと考えているCRubyの改善点を暫定的にリストアップします。

  • CRubyをオブジェクトシェイプベースのオブジェクトモデルに移行する
  • CRubyの型タグ付けスキームを変更して型チェックのコストを削減する
  • 定数のキャッシュ機構をより詳細な粒度で実装する
  • 呼び出しのコンベンションの高速化と軽量化
  • CのランタイムメソッドをRubyで書き換えて、JITコンパイラがインライン化できるようにする

Matz(まつもとゆきひろ氏)は最近のEuruko 2021で、Rubyは当面の間言語への新機能追加を控え目にすると述べています(動画↓)。言語を急激に変更すると、JIT実装を軌道に乗せて最新に保つのが難しくなる可能性があるので、これは賢明な判断だと思います。私たちの見解では、今後Rubyをより強固な言語とし、競争力の極めて高いパフォーマンスを提供するために内部の改修に集中するのは理にかなっていると思います。

皆さんが私たちと同様にYJITとRubyの未来に熱い期待を寄せていただければ幸いです。YJITを試してみたい方は、CRubyと同じオープンソースライセンスのもとでGitHubリポジトリから入手できます。バグが見つかったらissueをオープンして簡単な再現方法を添えていただけると助かります。YJITの試し方や、speed.yjit.org向けに構築したパフォーマンストラッキングシステムなどについて詳しくは、間もなくYJITの追加記事を2件公開しますので、どうぞご期待ください。

訳注

以下は元記事公開後にShopfy Engineeringブログで公開された、YJITの試し方の解説記事です。

著者について

Maxime Chevalier-Boisvert: 2016年にモントリオール大学でコンパイラ設計の博士号を取得し、動的型付けプログラミング言語に最適化したJITコンパイラアーキテクチャであるBasic Block Versioning(BBV)を開発。現在はShopifyでCRuby内部に構築される新しいJITコンパイラであるYJITプロジェクトを率いています。

お知らせ

あなたが世界のどこにいても、次の旅はここから始まります。現実世界の問題を解決するシステムをゼロから構築することに興味をお持ちでしたら、私たちがこれまで扱ってきたさまざまな課題を弊社のエンジニアリングブログでお読みいただけます。エンジニアリングキャリアページでは、現在弊社で募集中の職種やDigital by Defaultについて紹介しています。

関連記事

Rubyオブジェクトの未来をつくる「シェイプ」とは(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。