はじめに
はじめまして、2025年に新卒で入社し、クラウドハウスでバックエンドエンジニアをしているショーンです。
RubyKaigi2025の2日目で行われた、Maxime Chevalier-Boisvert(@maximecb) さんによるセッション、ZJIT: Building a Next Generation Ruby JITを紹介いたします。
MaximeさんはShopifyのRuby開発チームのリーダーで、RubyKaigiでも2021年から毎回 JITコンパイラの進化について発表している、いわば常連です。
毎回YJIT に関連する心躍る発表を用意してくださるMaximeさんでしたが、今回はなんと新しいJITコンパイラ「ZJIT」について発表されました。
過去の発表
rubykaigi.org
rubykaigi.org
rubykaigi.org
スケジュール一覧の中でも「ZJIT」のキーワードはひときわ目を引き、開催前からとても楽しみにしていました。
この記事ではセッションの内容に沿って、RubyにおけるJITコンパイラのこれまでについての振り返りから始め、ZJITの開発背景や技術的詳細、そして今後の期待される展開などを紹介します。
RubyKaigi最後の Matz (まつもとゆきひろ氏) のKeynoteで、エイプリルフールの伏線回収として衝撃のRuby 4.0が今年リリースされるという超絶に胸アツで激アツな発表がありましたが、その中の目玉のひとつとして、ZJITの導入が開始されることも発表されました。
Rubyの未来を大きく変える可能性を秘めたZJIT。RubyKaigi2025に始まったそのビッグウェーブに乗るべく、ぜひ最後までお付き合いいただければ幸いです。
YJITについて振り返る
ZJITを理解するためには、まずYJITについて振り返る必要があります。
YJITはShopifyが開発したCRuby向けのJIT(Just-In-Time)コンパイラで、Ruby 3.1から実験的に導入され、Ruby 3.2で正式に組み込まれました。
昨年RubyKaigi2024でYJITの発表について書いている弊社のこちらの記事も是非ご覧ください。 developers.techouse.com
YJITの開発背景と歴史
YJITは2020年にShopifyで開発が始まりました。最初は「MicroJIT」というプロトタイプとして開発され、Rubyのスピードアップを目指していました。しかし、初期のバージョンではRailsBenchなどの実用的なベンチマークでは性能が十分ではありませんでした。
その後、YJITは改良が重ねられ、RailsBenchなどで大幅な性能向上を見せるようになりました。そして最終的に、Ruby 3.1でCRubyに統合されるという大きな成果を上げました。これにより、Ruby 2.7と比較して最大3倍の速度向上が達成されたとのことです。
Ruby3.2では、Rustへの書き換えや、中間表現導入によるマルチプラットフォーム対応など、進化を遂げていきました。
YJITの特徴と技術
YJITの最大の特徴は、Lazy Basic Block Versioning(LBBV)という技術を採用していることです。
これは、コードの実行パスに沿って、必要になった時点で基本ブロック の単位でJITコンパイルを行う手法です。
すなわち、従来のJITコンパイラがメソッド全体を一度にJITコンパイルするのに対し、YJITは、分岐のない一本道のコード(= 基本ブロック) の単位で小さく区切って必要なところから順にコンパイルしていきます。 これによって、実行時によく通るパスだけを素早く最適化でき、無駄のない効率的な実行ができるようになるのです。
詳しい技術的な詳細については理解できていない部分が多いですが、一般に以下のようなメリットがあると理解しています。
- 必要な部分だけをコンパイルするため、起動時間が短縮できる
- コンパイルされたネイティブコードの量が少なく済む傾向があるため、メモリ使用量が少なくなる
- 基本ブロックの実行パス上であれば型が安定しやすい (頻繁に型の変更は起きないため) 、それによって実行時の型に特化したコードを生成でき、処理が早くなる
- 部分的にネイティブコードに変換するため、インタプリタとJITの両方を柔軟に使い分けることができる
LLVB についてより深く理解したい方は、Maximeさんの博士論文を読んでみることをお勧めします。博士論文の研究で扱っていた技術が、今やそのままCRubyのmain stream に入っているのは本当に凄まじいですね。
YJITのこれまでの実績
YJITは多くの企業やサービスで採用され、大きな成果を上げています。数々の企業から Real World Web Application (つまり、Rails アプリケーション) が高速化したという報告がありました。
発表ではZendeskの事例が紹介されました。Zendeskでは、YJITを有効化しただけで20%のパフォーマンス改善が見られ、その効果が実際の運用に大きな影響を与えたそうです。
ベンチマーク結果も非常に良好で、ShopifyのLiquidRenderベンチマークでは2.8倍のスピードアップ、他のベンチマークでも平均2倍のスピードアップが記録されています。
このように、YJITは「理論上の高速化」ではなく、現実のWebアプリケーション (Rails アプリケーション) を意識した最適化を、Shopifyの強力なベンチマークリソースをもとに継続的に行っている点が特徴です。また、Shopifyのような大規模トラフィックを抱える現場で鍛え抜かれたJITであるからこそ、実サービスでも効果が出やすいのです。
YJITの限界と課題
しかし、YJITにも限界があります。発表でMaximeさんは以下のような課題を挙げていました。
性能の頭打ち: 最初のバージョンでは大きなスピードアップがありましたが、次第に性能向上の幅が小さくなってきたこと。当然と言えば、当然ですが YJITを導入したバージョン程のスピードアップが、その後のYJITのアップデートではなかなか達成できなくなってきたということです。
Webアプリケーション全体のパフォーマンス限界: データベースリクエストなどはJITでは改善できないため、Webアプリケーション全体のパフォーマンスには限界があります。
計算集約的な作業での性能差: マイクロベンチマーク(例えば、10億回の入れ子ループ)では、RubyはJavaScriptや他の言語に対して大きな差をつけられており、計算集中的な作業では依然として性能差があります。
アーキテクチャの拡張性: YJITのアーキテクチャは、これ以上の拡張が難しくなってきているという課題もあります。
これらの限界を克服するために、新しいJITコンパイラ「ZJIT」の開発が始まったのです。
ZJITとは何か?
ZJITも、ShopifyのYJIT開発チームが開発している新しいRubyのJIT(Just-In-Time)コンパイラです。ZJITという名前は、YJITの次のアルファベットであるZを取ったものと思われますが、その設計思想は単なる進化形ではなく、「原点回帰」 とも言えるアプローチを取っています。
ZJITの開発背景
発表によると、ZJITは単なるYJITの改善ではなく、過去の数年の開発経験を活かした次世代のJITコンパイラとして位置づけられています。YJITの開発過程で行った数百の実験と実運用データをもとに、より維持・拡張が容易なJITを目指して開発が始まりました。
発表では、「ZJITの目標は、少なくとも20年以上にわたり、Rubyが生き残る言語であること。その世界に対応できるようなJITを提供すること 」だそうです。非常に野心的で、Rubyの将来に対する長期的な想いを感じることができました。言語別のマイクロベンチマークのグラフの変化を見せて、Maximeさんが "Take that! Python…!!" (ベンチマークで差をつけつつあるPython に対して、追撃パンチを喰らわせて、これでもくらえ!! 的なニュアンス) と言ったシーンは笑ってしまいました。
「原点回帰」のコンセプト
ZJITの最も特徴的な点は、YJITが採用していたLazy Basic Block Versioning(LBBV)という革新的な手法を捨て、より伝統的なメソッドベースのアーキテクチャに回帰したことです。
Maximeさんは、まさに自分の博士論文の手法 を手放し、「より伝統的で標準的な方法に基づいたJITを構築することに決めた」と説明していました。具体的には、メソッドベースのコンパイラアーキテクチャを採用し、LBBVを避けることでリスクを最小限に抑えるという選択をしたのです。
Rubyにおいて、この「原点回帰」的なメソッドベースのJITコンパイラのアプローチがとられるのは実は初めてではありません。現在はShopifyのJIT開発チームの超強力なメンバーであるk0kubunさん が開発し、Ruby2.6で導入された MJIT(Method-based JIT)など、何度か挑戦が試みられた歴史があります。
Ruby2.6 においてMJITは Optcarrotなどのマイクロベンチマークでの高速化をもって導入されましたが、実際のRailsアプリケーションで動かすと、十分な性能向上を得ることができなかったのです。そこから、アプローチを変えたYJITの開発へと繋がっていきます。
しかし、「ZJIT」と当時でアプローチは似ているが、置かれている状況が違うということは重要です。
ShopifyがYJIT開発で、RubyでもJITコンパイラで高速化ができるということを証明しました。そしてそこで得た知見と強力な開発チーム、大量のベンチマークとリソースを持って改めて原点回帰的アプローチが採用される。これは、これまでのRubyの歴史で初めてのことです。
幸運なことに、オフィシャルパーティでk0kubunさんから少しお話を伺う機会がありましたが、これを 「強くてニューゲーム」 と表現されていました。
ZJITの技術的な特徴
次に、ZJITの技術的な特徴についてMaximeさんは発表の中から読み取れた部分について紹介いたします。
メソッドベースのコンパイルアーキテクチャ
ZJITの最も基本的な特徴は、メソッド単位でコンパイルを行うアーキテクチャを採用していることです。これは、YJITの基本ブロック単位のアプローチ(LBBV)とは対照的です。
メソッドベースのコンパイルには以下のような利点があります:
- 広範囲の最適化: メソッド全体を一度に見ることで、より広範囲の最適化が可能になる。
- 関数呼び出しのオーバーヘッド削減: メソッド内の関数呼び出しを最適化しやすくなる。
- 標準的な手法: 多くの成熟したJITコンパイラで採用されているアプローチのため、他言語での成功事例や、既存の研究や知見をより活用しやすい。
- モジュール化された設計: コンパイラの各部分が明確に分離され、拡張や改良が容易になります。それによってYJITに比べて、広くRubyコミュニティ全体での開発参加を促進し、より多くのコントリビュータが関与できる。
中間表現(IR)とSSA形式
ZJITでは、コンパイル過程で中間表現(IR: Intermediate Representation)を使用します。
YJITでも、Ruby3.2以降に中間表現は採用されてきましたが、
特に、ZJITではSSA(Static Single Assignment: 静的単一代入) 形式を採用していると言及されました。これはIBMが1980年代に開発した形式で、LLVMやGCCなどの主要なコンパイラでも使用されている業界標準の手法だそうです。
SSA形式の特徴はこんな感じのようです。
- 各変数は一度だけ代入される: これにより、変数の依存関係が明確になり、最適化が容易になります。
- プログラムの流れが明示的: 制御フローとデータフローが明確になり、解析が容易になります。
- 最適化が適用しやすい: 不要なコードの削除、定数畳み込み、ループ最適化などが適用しやすくなります。
ZJITのIRには、型情報やパッチポイント(最適化を解除してインタプリタに戻るためのポイント)、ガード(型が合わなかった場合などに脱出するための仕組み)なども含まれているとのことです。これらの情報を活用することで、効率的なコード生成と実行時の安全性を両立させています。
メソッド呼び出しの最適化
Rubyにおいて、メソッド呼び出し(CALL, メソッドディスパッチ)は非常にコストの高い操作です。多くの処理と複雑なスタック管理が必要となります。
YJITでもメソッド呼び出しの高速化は行われていましたが、Maximeさんによれば、まだ理論的な最大性能の半分程度しか出せておらず、生成されるコード量も大きすぎてキャッシュ効率が悪いという問題がありました。
ZJITでは、この問題に対処するために「JITからJITへの高速呼び出し(fast JIT-to-JIT calls)」を実現しています。JITコンパイルされたメソッドから別のメソッドを呼び出す際に、従来はVM (インタプリタ) が処理を介していましたが、呼び出し対象のメソッドもJITコンパイルされている場合は、VM (インタプリタ) を介さず、直接JITコードを呼び出すことができる最適化です。
これにより、メソッド呼び出しのオーバヘッドが削減され、軽量な呼び出しが可能になります。
こちらの機能はすでに開発2ヶ月半時点のZJITで実装され、動いているとのことです!
コンパイル結果の再利用(永続化)
JITコンパイルは実行時に行われるため、プログラムの実行リソースを消費します。特に大規模なアプリケーションでは、JITの初回コンパイル処理が性能のボトルネックとなるケースも少なくありません。
Maximeさんは、この問題に対処するための興味深いアプローチとして、「JIT結果の永続化と再利用」を検討していると述べました。これが実現できれば、一度生成した機械語や最適化情報を別のデプロイや起動時に再利用できるようになります。
Shopifyのような大規模サービスでは、頻繁にコードがデプロイされますが、変更内容はわずかであることが多いです。したがって、毎回全サーバーでJITコンパイルを最初からやり直すのは非効率であり、JIT結果の再利用ができれば大きなメリットがあります。
聞いただけで、かなり挑戦的なのが伝わってきますが、Maximeさんは、以下のような具体的な課題を挙げていました。
- コードだけでなく、最適化メタデータをどのように保存するか
- 型情報や分岐予測の情報などの、「実行時の知見」をどのように保存・再利用するか
- これらは動的に変化する可能性があるため、整合性のある設計が求められる
- JITコードが持つインラインキャッシュや、Rubyオブジェクトへのポインタをどう扱うか
- プロセスを再起動した時点で、これらのプロセス固有のアドレス空間に依存した情報は無効になってしまう
- 再起動後の環境に合わせて情報を再構築するために、軽量なリンカの実装を検討している
しかし、これらの課題は技術的に解決可能であり、すでに他言語のJITでは成功例があるため、Rubyでも実現可能だとMaximeさんは述べています。
ZJITの現在のステータス
Maximeさんの発表によると、ZJITはまだ開発の初期段階にあります。発表時点で、開発開始2ヶ月半ほどとのことです。
現在の開発状況
現時点でのZJITは、まだ実装段階ですから、YJITと比較すると、対応している機能はまだ少なく、本格的な実用には至っていません。しかし、基本的なアーキテクチャの設計と実装は順調に進んでいるとのことです。
Maximeさんによれば、以下のような基本機能はすでに動作しているとのことです:
- 再帰呼び出し
- 基本的な条件分岐
- 型チェック
- 単純なループ処理
また、コアアーキテクチャとして、以下のようなZJITの機能の基盤は固まりつつあるとのことです:
- SSAベースのIR(中間表現)
- 機械語レベルのスタックを使った高速JIT間呼び出し
- 基本的なガードと最適化解除 (Deoptimization) の処理
Maximeさんは、使用されるRubyの機能の幅広さを考慮すると、大規模なRubyプログラムでYJITを凌駕するのには時間がかかると思うものの、2025年の年末までにパフォーマンスの面でZJITをYJITとほぼ同等にすることを目標として掲げていました。
YJITとの共存と移行計画
発表では、YJITとZJITの開発/移行計画についても言及がありました。
ZJITがYJITよりも高速かつ安定していると確信できるまでは、YJITは残り続けるが、長期的にRubyを支えていくZJITをしばらくは実装していく計画とのことです。
したがって、YJITは現時点から「メンテナンス最小モード」に入り、Shopifyの開発リソースをZJITに集中する方針だそうです。
Ruby 3.5 or 4.0 のリリース時には、--enable-zjit
のようなオプションで実験的なZJITが有効にできるようになるとのことでした。
また、ZJITが採用しているアーキテクチャでは、綺麗にモジュール化された方法でYARVバイトコードをZJITの中間表現に最適化できるので、将来的にRubyが複数のJITコンパイル層を持てるようになる可能性もあるとのことです。 ZJIT の Issue 記載あり
最後に
ZJITは、YJITの成功体験を持ったShopifyのYJIT開発チームが「強くてニューゲーム」状態で開発を始めた、原点回帰型のメソッドベースのJITコンパイラです。
RubyKaigi2025の最後に、Matz から重要な発表がありました。今年末にRuby 4.0をリリースし、そこで実験的にZJITを導入することが宣言されたのです!
そして、ZJITはその直後に Rubyのupstreamにマージされました。これ以上ないくらい激アツな展開でした。 Issue #21221 PR #13131
私は新卒で今年初めてRubyKaigiに参加しました。普段の業務ではRubyの内部について深く意識する機会はありませんでしたが、今回の参加がきっかけとなり、その奥深さを感じることができました。
特に印象的だったのは、Shopifyが 「この先20年、Rubyが選ばれ続ける言語であるために」、これまで成功を収めてきたYJITのやり方をあえて手放し、ZJITという挑戦的な道を選んだことです。
そして会場の雰囲気を通じて、Rubyコミュニティには、同じくRubyを愛し、Rubyの未来を本気で願っていること人がこんなにたくさんいるのだということを肌で感じることができました。
Matz のKeynoteでも言及がありましたが、Rubyの実行速度が向上することで、より多くの場面でRubyが選択肢となるはずです。そして ZJITがその一翼を担うのは間違いなさそうです。
今後もZJITの開発に注目していきたいと思います!!
おまけ:k0kubunさんとの写真
1日目の夜のアフターパーティで、k0kubunさんと写真を撮ることができました (紹介したセッションの発表者ではないですが、嬉しかったので貼らせていただきます)
Techouseでは、社会課題の解決に一緒に取り組むエンジニアを募集しております。 ご応募お待ちしております。