1 ポイント 投稿者 GN⁺ 2025-11-19 | 1件のコメント | WhatsAppで共有
  • YJITとZJITは、Ruby 3.xでRubyコードを機械語に変換して実行速度を高めるJITコンパイラの仕組み
  • YJITは各関数やブロックの呼び出し回数をカウントし、一定のしきい値に達するとそのコードを機械語に変換
  • 変換されたコードはYJITブロックに保存され、各ブロックは複数のYARV命令に対応するARM64機械語命令へ変換される
  • Branch Stubを使ってランタイム時に実際のデータ型を観察し、それに応じた機械語命令を選択的に生成
  • この仕組みは、Rubyの実行性能向上と動的型処理の効率性を同時に実現するための中核メカニズム

Chapter 4: Rubyを機械語にコンパイルする

Interpreting vs. Compiling Ruby Code

  • 原文に詳細な内容はなし

Counting Method and Block Calls

  • YJITはプログラムの関数およびブロックの呼び出し回数を追跡してホットスポットコードを識別
    • 各関数やブロックのYARV命令シーケンスの横にjit_entryjit_entry_callsの値を保存
    • jit_entryは初期状態ではnullで、後からYJITが生成した機械語コードへのポインタを保存
    • jit_entry_callsは呼び出されるたびに1ずつ増加
  • 呼び出し回数がしきい値に達すると、YJITがそのコードを機械語へコンパイル
    • Ruby 3.5のデフォルトしきい値は小規模プログラムで30回大規模アプリケーションで120回
    • 実行時に--yjit-call-thresholdオプションで変更可能
  • この方式により、YJITは頻繁に実行されるコードだけを機械語に変換し、効率的な実行経路を確保

YJIT Blocks

  • YJITは生成した機械語命令をYJITブロックに保存
    • YJITブロックはRubyのブロックとは異なり、YARV命令の一部分の区間に対応
    • 各Ruby関数やブロックは複数のYJITブロックで構成される
  • 例のプログラムでは、ブロックが30回目に実行されたときにYJITがコンパイルを開始
    • 最初のYARV命令getlocal_WC_1を機械語へ変換して新しいYJITブロックを生成
    • その後getlocal_WC_0命令を追加でコンパイルし、同じブロックに含める
  • Figure 4-8によると、YJITはARM64命令を生成し、M1プロセッサのx1、x9レジスタに値をロード
    • getlocal_WC_1は1つ前のスタックフレームのローカル変数を、getlocal_WC_0は現在のスタックの変数をスタックへ保存
    • 生成された機械語命令は同じ動作を実行する

YJIT Branch Stubs

  • YJITがopt_plus命令をコンパイルするとき、オペランドの型が分からない問題が発生
    • 整数、文字列、浮動小数点数など、型によって必要な機械語命令が異なる
    • 例: 整数加算ではadds命令を使い、浮動小数点加算では別の命令が必要
  • これを解決するため、YJITは事前分析ではなくランタイム観察方式を採用
    • プログラム実行中に実際に渡された値の型を確認し、それに合った機械語を生成
  • この動作のためにBranch Stubを使用
    • 新しい分岐(branch)にまだ接続されたブロックがない場合、一時的にstubへ接続
    • その後、実際の型が確認されると、そのstubを適切なブロックに置き換える

ZJIT(言及のみ)

  • 目次にはZJIT関連のセクションが含まれているが、本文には具体的な説明はない

要約

  • YJITはRuby 3.5における動的型付け言語の実行効率を高めるためのJITコンパイラ
  • 呼び出し回数ベースのコンパイルトリガーYJITブロック構造Branch Stubによるランタイム型確認が中核
  • ARM64アーキテクチャ上で実際の機械語命令へ変換し、Rubyコードの実行速度を向上
  • ZJITは次世代JITとして言及されているが、詳細は本文にない

1件のコメント

 
GN⁺ 2025-11-19
Hacker Newsの意見
  • 以前、MacRuby が LLVM を使って macOS 上でネイティブコードにコンパイルされ、Objective‑C フレームワークと統合されていた時代があった
    かなり素晴らしいアイデアだったが、結局 Apple は Swift に舵を切ったようだ
    新しい版が出たら Ruby Under a Microscope をぜひ買って読もうと思っている。Ruby は今でも好きだが、実際に使う機会はあまりなかった

    • MacRuby の作者は Apple を去った後、RubyMotion を作った
      今は別の人たちが引き継いでいるが、現在は DragonRuby(ゲーム中心の Ruby 実装)により力を入れている雰囲気だ
    • MacRuby は作者が去った後、RubyMotion へとつながっていった
      ちなみに ウィキ文書 もある
    • 今でも Objective‑C を使って macOS、iOS、iPadOS 向けのアプリを作ることはできる
      ただし、昔の API はもうサポートされていない可能性がある
    • いろいろな言語を触ってきた立場から見ると、Apple が Swift へ移ったのは、ちょうど Microsoft が VB6 から VB.Net へ移行したのに近い感覚だ
      VB6 は開発速度が本当に速く、Direct3D や ASP Classic まで扱えた
      Ruby の 優雅さと開発のしやすさ があの時代を思い出させる
      もし Ruby に VB6 級の GUI ツールがあったなら、人気もかなり違っていた気がする
  • Pat が引き続きプロジェクトを続けているのを見るのは本当にうれしい
    彼の最初の Ruby Under a Microscope の本とブログ記事は、私に大きな 刺激 を与えてくれた
    以前 Euruko カンファレンスで直接会ったこともあるが、本当に素晴らしい人だった

    • 温かいコメントに感謝します
  • 初めて Ruby Under a Microscope を読んだときは本当に面白かった
    そのおかげで、以前 CTF の問題解き にも活用した
    最近は Ruby の内部実装を追えていないが、新しい版が出たら必ず買うつもりだ

    • 2002年から2010年まで Ruby をたくさん使っていたが、その後はほとんど触らなくなった
      今回の記事を見て、新版をまた読みたくなった
  • Ruby コンパイルの話が出たので聞きたいのだが、Stripe の開発者たちが作った Sorbet compiler を使ってみたことはあるだろうか
    Sorbet Compiler 公開記事

    • 今はリポジトリから消えていて、もう開発されていないようなのが残念だ
      AOT コンパイル は Ruby では本当に難しい
      Sorbet のアプローチが興味深いのは、Ruby の 型検査 を基に高速な経路を作れるからだ
      私も個人プロジェクトとして Ruby コンパイラを作っていて、hokstad.com/compiler
      writing-a-compiler-in-ruby を参考にしている
      今は RubySpec の通過に集中していて、後では型ベースの最適化にも挑戦してみるつもりだ
  • Ruby コンパイルとは直接関係ないが、Enterprise Integration with Ruby という本は、Web 以外の領域で Ruby を活用するうえで大きな 洞察 を与えてくれた

  • MRuby を知って以来、自分のプロジェクトやスクリプトをスタンドアロンの実行ファイルに変える楽しさにハマっている

  • Ruby Under a Microscope が今でも更新されているのはうれしい
    Ruby の内部動作を理解したい人にとっては 必読書 だと思う

  • YJIT のブロックが何度も実行されるとき、入力型ごとにどのようにコンパイルを追跡しているのか気になっていた
    Ruby が int や float などのさまざまな型をどう処理するのか知りたい

    • それこそが YJIT の核心
      実際の型が与えられるまでコンパイルを遅らせる “wait‑and‑see” アプローチを使っている
      型ごとにブロックのバージョンを別々に管理し、状況に応じて呼び分ける
      このアルゴリズムは Basic Block Versioning と呼ばれる
      Shopify の Maxime Chevalier‑Boisvert が RubyConf 2021 の発表動画 でうまく説明している
      新しい JIT エンジンである ZJIT は別の方式を使っているようだ
  • 動的型付け言語を JIT で高速化するには、通常 メモリ使用量の増加 という代償を払うことになる
    Shopify のような大企業でなければ、こちらのほうが大きな問題かもしれない

    • ただ、小規模な企業では普通アプリケーションの規模も小さい
      最近のクラウドインスタンスはコアあたり 4GiB 程度のメモリを持っているので、数百 MB の JIT コード程度なら十分に耐えられる
  • YJIT が関数呼び出し回数だけを数えて ホットスポット を見つける方式は単純に見えた
    JavaScript JIT のように、ループ内部の重い演算を検知する機能はないのだろうかと気になった
    Ruby のブロック構造がこうした最適化に役立つかもしれないと思った

    • その通りで、Ruby はループ本体をブロックとして扱うため
      JIT はブロックを別個の関数のように扱いながら、反復処理を自然に 最適化 できる
      この点は次の章でさらに深く扱う予定だ