スパインル:Ruby AOT ネイティブコンパイラ

2026/04/24 17:28

スパインル:Ruby AOT ネイティブコンパイラ

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

Spinel は、標準的な Ruby コードを外部のランタイムライブラリ(標準の C/C 数学ライブラリ以外を除く)を必要とせずに、スタンドアロンの高パフォーマンスなネイティブバイナリに変換する画期的な新しいコンパイラです。全プログラムの型推論と積極的な最適化(小規模クラスへのスタック割り当て、文字列結合チェーンの平坦化による割付削減、静的シンボルインターニング等)を活用することで、劇的な高速化を実現します。ベンチマークの結果では、miniruby 대비 幾何平均で 11.6 倍の実行速度向上を示し、「コンウェイのゲーム・オブ・ライフ」などの特定タスクでは最大 86.7 倍の改善が確認されています。重要な点は、このコンパイラは自己ホスト化されており、メタプログラミングや eval を使用せずに特別な Ruby サブセットを利用して自身をコンパイルできることであり、並行処理や任意精度算術を含む 74 のコア機能テストで堅牢性を保っています。この技術により、開発者は Ruby アプリケーションを単一バイナリとして配信し、システムに標準 C ライブラリがあれば効率的に実行でき、企業_grade のソリューションを展開する企業が重いランタイム環境のインストールや複数の依存関係の管理といった複雑さを排除できます。

本文

Spinel -- Ruby AOT コンパイラ

Spinel は、Ruby ソースコードを独自の実行ファイル(ネイティブバイナリ)へとコンパイルするツールです。プログラム全体を対象とした型推論を実装し、最適化された C コードを生成することで、CRuby に比べて大幅な速度向上を実現しています。Spinel は自身もコンパイル対象となる自己完結型の設計を採用しており、コンパイラのバックエンドは Ruby で記述され、それ自身がネイティブバイナリとしてコンパイルされます。

仕組みについて

Ruby (.rb)
    |
    v
spinel_parse           Prism (libprism) を用いたパーサーで解析し、AST をシリアライズする
    |                  (C バイナリを使用するか、CRuby + Prism ジェムをフォールバックとして使用する)
    v
AST テキストファイル
    |
    v
spinel_codegen         型推論と C コードの生成を行う(自己完結型のネイティブバイナリ)
    v
C ソース (.c)
    |
    v
cc -O2 -Ilib -lm      標準的な C コンパイラー + ランタイムヘッダーを使用
    |
    v
ネイティブバイナリ     インストール不要(ランタイム依存関係なし)の自立型実行ファイル

クイックスタート

libprism のソースを取得する (rubygems.org の prism ジェムから):

make deps

全ビルトを実行:

make

Ruby プログラムを作成:

cat > hello.rb <<'RUBY' def fib(n) if n < 2 n else fib(n - 1) + fib(n - 2) end end

puts fib(34) RUBY

コンパイルして実行:

./spinel hello.rb ./hello # 値として「5702887」を出力(瞬時に完了)

オプション

  • ./spinel app.rb
    # ./app という名前でコンパイル
  • ./spinel app.rb -o myapp
    # ./myapp という名前でコンパイル
  • ./spinel app.rb -c
    # 生成処理のみを行い、app.c を出力
  • ./spinel app.rb -S
    # C コードを標準出力 (stdout) に表示

自己完結化 (Self-Hosting)

Spinel は自身のバックエンドをコンパイルします。ブートストラップチェーンの工程は以下の通りです:

  • CRuby + spinel_parse.rb → AST
  • CRuby + spinel_codegen.rb → gen1.c → bin1
  • bin1 + AST → gen2.c → bin2
  • bin2 + AST → gen3.c
  • gen2.c == gen3.c
    (ブートストラップループが閉じる)

ベンチマーク結果

74 の機能テストが合格し、55 のベンチマークが合格しました。 幾何平均では、以下の 28 つのベンチマークにおいて miniruby (Ruby 4.1.0dev) よりも約 11.6 倍高速です。基線となるのはバンドルされたジェムなしの最新 CRuby miniruby ビルドで、これはシステム上の Ruby (3.2.3) を大幅に上回ります。Spinel の優位性は相対的に小さくなるものの、計算量が多い負荷においては依然として実用的なパフォーマンスです。

計算密集型

ベンチマークSpinelminirubyスピードアップ
life (コンウェイのライフゲーム)20 ms1_733 ms86.7 倍
ackermann5 ms374 ms74.8 倍
mandelbrot25 ms1_453 ms58.1 倍
fib (再帰的)17 ms581 ms34.2 倍
nqueens10 ms304 ms30.4 倍
tarai16 ms461 ms28.8 倍
tak22 ms532 ms24.2 倍
matmul13 ms313 ms24.1 倍
sudoku6 ms102 ms17.0 倍
partial_sums93 ms1_498 ms16.1 倍
fannkuch2 ms19 ms9.5 倍
sieve39 ms332 ms8.5 倍
fasta (DNA シーケンス生成)3 ms21 ms7.0 倍

データ構造と GC

ベンチマークSpinelminirubyスピードアップ
rbtree (Red-Black ツリー)24 ms543 ms22.6 倍
splay tree14 ms195 ms13.9 倍
huffman (符号化)6 ms59 ms9.8 倍
so_lists76 ms410 ms5.4 倍
binary_trees11 ms40 ms3.6 倍
linked_list136 ms388 ms2.9 倍
gcbench1_845 ms3_641 ms2.0 倍

リアルワールドアプリ

ベンチマークSpinelminirubyスピードアップ
json_parse39 ms394 ms10.1 倍
bigint_fib (桁数 1,000)2 ms16 ms8.0 倍
ao_render (レイトレーサー)417 ms3_334 ms8.0 倍
pidigits (大整数計算)2 ms13 ms6.5 倍
str_concat2 ms13 ms6.5 倍
template engine152 ms936 ms6.2 倍
csv_process234 ms860 ms3.7 倍
io_wordcount33 ms97 ms2.9 倍

サポートされている Ruby 機能

  • コア: クラス、継承、super, include (ミクシン), attr_accessor, Struct.new, alias, モジュール定数、組み込み型のオープンクラス。
  • 制御フロー: if/elsif/else, unless, case/when, case/in (パターンマッチング), while, until, loop, for..in (レンジと配列), break, next, return, catch/throw, &. (セーフナビゲーション)。
  • ブロック: yield, block_given?, &block, proc {}, Proc.new, lambda -> x { }, method(:name)。ブロックメソッド: each, each_with_index, map, select, reject, reduce, sort_by, any?, all?, none?, times, upto, downto。
  • 例外処理: begin/rescue/ensure/retry, raise, 独自に定義した例外クラス。
  • 型: Integer, Float, String (不変+可変), Array, Hash, Range, Time, StringIO, File, Regexp, Bigint (自動昇格), Fiber。タグ付きユニオンによる多型値。自己参照的なデータ構造向けに nullable object types (T?) を提供。
  • グローバル変数:
    $name
    は静的な C 変数へとコンパイルされ、コンパイル時に型不一致を検出します。
  • 文字列:
    <<
    オペレーターは自動的に可変文字列 (sp_String) に昇格し、O(n) インプレイスアプレンドを実現。
    +
    , インポーレーション, tr, ljust/rjust/center および標準的なメソッドは両方の型で動作します。文字比較
    s[i] == "c"
    はゼロ割り当てによる直接の文字配列アクセスへと最適化されます。連鎖結合 (
    a + b + c + d
    ) は単一の malloc 呼び出し (sp_str_concat4 / sp_str_concat_arr) に集約され、N-1 つの中間文字列を削減します。ループ内の
    str.split(sep)
    は既存の sp_StrArray を再利用するため(csv_process: 400 万回の割り当てを削除)、配分のオーバーヘッドが軽減されます。
  • Regexp: 組み込みの NFA 型正則表現式エンジン(外部依存関係なし)。
    =~
    ,
    $1-$9
    ,
    match?
    ,
    gsub(/re/, str)
    ,
    sub(/re/, str)
    ,
    scan(/re/)
    ,
    split(/re/)
    がサポートされています。
  • Bigint: mruby-bigint を用いた任意精度整数。ループ内の乗算パターン(例:
    q = q * k
    )やフィボナッチ数列スタイルの自己参照的な加算(
    c = a + b
    )から自動的に昇格されます。静的ライブラリとしてリンクされ、使用時にのみ読み込まれます。
  • Fiber: ucontext_t を用いた協調型並行性の実装。Fiber.new, Fiber#resume, 値渡り付きの Fiber.yield がサポートされています。ヒープ上で昇格されたセルを介して自由変数をキャプチャします。
  • メモリ管理: サイズセグメント化されたフリーリスト、非再帰的なマーキング、スティッキーマークビットを採用したマーク&スウィープ GC。小さいクラス(8 個以下のスカラーフィールド、継承なし、パラメータ経由での変異なし)は自動的にスタック配分されたバリュータイプとして処理され、5 フィールドのクラスの 100 万回の割り当てが 85 ms から 2 ms に短縮されます。バリュータイプのみのプログラムでは GC ランタイムを一切発症させません。
  • シンボル: 独自の sp_sym タイプであり、文字列とは異なる(
    :a != "a"
    )。シンボルリテラルはコンパイル時に SP S_name 定数へインターン化され、文字列#to_sym は動的プールが実際に必要な場合にのみ使用されます。シンボルキー付きのハッシュ
    {a: 1}
    は、sp_SymIntHash を用いて sp_sym (整数) キーを直接格納するため、strcmp や動的な文字列割り当てを行いません。
  • I/O: puts, print, printf, p, gets, ARGV, ENV[], File.read/write/open (ブロック付き), system(), 反引字表現。

最適化技術

プログラム全体を対象とした型推論に基づいて、コンパイル時において以下のような最適化が施されます:

  • バリュータイプの昇格: 小さな不変クラス(8 個以下のスカラーフィールド)は C struct としてスタック上での処理となり、GC オーバーヘッドを完全に排除します。
  • 定数伝播: 単純なリテラル定数(N = 100 など)は使用場所でインライン展開され、cst_N ランタイムルックアップを回避します。
  • ループ不変長の引き上げ:
    while i < arr.length
    では、ループ開始前に arr.length が一度評価されます。「
    while i < str.length
    」の場合は strlen が引き上げられます。本体内でリシーバーの修飾(例:
    arr.push
    )がある場合は、引き上げが無効化されます。
  • メソッドのインライン展開: 短いメソッド(3 語句以下かつ非再帰)は static inline にされ、gcc がコールサイトでインライン展開できます。
  • 文字列結合チェーンのフラッティング:
    a + b + c + d
    は単一の sp_str_concat4 / sp_str_concat_arr 呼び出しにコンパイルされ、1 つの malloc で N-1 つの中間文字列を削減します。
  • Bigint の自動昇格:
    x = x * y
    のようなループやフィボナッチ数列スタイルの自己参照的な加算
    c = a + b
    は自動的に Bigint として処理されます。
  • Bigint に変換する to_s: mruby-bigint の mpz_get_str を用いた分割統治法 O(n log²n) で、 naive な O(n²) より高速です。
  • 静的シンボルインターン化:
    "literal".to_sym
    はコンパイル時間の SPS_ 定数に解決され、動的なプールは実際の動的インターンが使用された場合にのみ出力されます。
  • sub_range 内の strlen キャッシャーリング: 文字列の長さが引き上げられた場合、
    str[i]
    アクセスでは内部の strlen 呼び出しをスキップするために sp_str_sub_range_len を使用します。
  • split の再利用: ループ内の
    fields = line.split(",")
    は新しい配列を割り当てるのではなく、既存の sp_StrArray を再利用します。
  • デッドコードエリミネーション:
    -ffunction-sections -fdata-sections
    でコンパイルされ、
    --gc-sections
    でリンカー処理されるため、使用されていないランタイム関数が最終バイナリから除去されます。
  • 早期終了を促す推論ループ: param/return/ivar 固定点ループは、3 つの配列の署名が変更しない時点で即座に停止します。大半のプログラムは全工程である 4 回ではなく、1〜2 回の反復で収束し、ブートストラップ時間を約 14% 短縮します。
  • parse_id_list のバイトウォーク: AST フィールドリストパーサー(自己コンパイル時に約 12 万回呼び出される)は、
    s.split(",")
    ではなく
    s.bytes[i]
    を用いて手動でバイトをウォークすることで、N+1 つの割り当てを毎回 2 個に削減します。
  • 警告のないビルト: テストおよびベンチマークの全てでデフォルトの警告レベルにおいてきれいにコンパイルされるように生成された C コードは設計されています。ハネスでは
    -Werror
    を使用するため、回退が即座に表面化します。

アーキテクチャ

  • spinel 1 つのコマンドで動作するラッパースクリプト (POSIX シェル)
  • spinel_parse.c C フロントエンド: libprism → テキスト AST(約 1,061 行)
  • spinel_codegen.rb コンパイラバックエンド: AST → C コード(約 21,109 行)
  • lib/sp_runtime.h ランタイムライブラリヘッダー(581 行)
  • lib/sp_bigint.c 任意精度整数(5,394 行)
  • lib/regexp/ 組み込みの正則表現式エンジン(1,759 行)
  • test/ 機能テスト 74 テスト分
  • benchmark/ ベンチマーク 55 件
  • Makefile ビルド自動化

コンパイラバックエンド (spinel_codegen.rb) は、Spinel 自身がコンパイル可能な Ruby のサブセットで記述されています。サポートされる構文にはクラス定義、def, attr_accessor, if/case/while, each/map/select, yield, begin/rescue、文字列/配列/ハッシュの操作、および File I/O が含まれます。メタプログラミング、eval、バックエンドでの require は使用されません。

ランタイム (lib/sp_runtime.h) には GC、配列/ハッシュ/文字列の実装、およびすべてのランタイムサポート機能が単一のヘッダーファイルに集約されています。生成された C コードはこのヘッダーを含め、リンカーは libspinel_rt.a(bigint と正則表現式エンジン)から必要な部分だけを参照します。

パーサーには 2 つの実装があります:

  • spinel_parse.c libprism を直接リンク(CRuby は不要)
  • spinel_parse.rb Prism ジェムを使用(CRuby のフォールバック)

両方の実装は同等の AST 出力を生成します。Spinel のラッパーでは、C バイナリが利用可能である場合はそれを優先的に使用します。require_relative はパーサ実行時に参照ファイルをインライン展開することで解決されます。

ビルド方法

  • make deps
    # vendor/prism に libprism を取得する(初回のみ)
  • make
    # パーサー、regexp ライブラリ、ブートストラップコンパイラをビルド
  • make test
    # 74 の機能テストを実行(ブートストラップが必要)
  • make bench
    # 55 のベンチマークを実行(ブートストラップが必要)
  • make bootstrap
    # ソースからコンパイラを再構築
  • sudo make install
    # /usr/local にインストール (PATH に spinel を追加)
  • make clean
    # ビルドアーティファクトを削除

インストール先ディレクトリを上書きするには:

make install PREFIX=$HOME/.local

Prism は Spinel のパーサーとして使用されます。

make deps
コマンドは rubygems.org から prism ジェムの tarball をダウンロードし、C ソースを vendor/prism に展開します。すでに Prism ジェムがインストールされている場合はビルトが自動的に検出され、必要であれば
PRISM_DIR=/path/to/prism
によってカスタムディレクトリを指定することもできます。

CRuby は初期のブートストラップ用だけに必要です。

make
コマンドを実行後には、その後のパイプライン全体は Ruby を使用せずに動作します。

制限事項

  • eval のサポートなし: eval, instance_eval, class_eval
  • メタプログラミングのサポートなし: send, method_missing, define_method (動的)
  • スレッドのサポートなし: Thread, Mutex(Fiber はサポート)
  • エンコーディングのサポートなし: UTF-8/ASCII を仮定
  • 一般化されたラムダ演算子のサポートなし: [] 呼び出し付きの深いネスト
    -> x { }

依存関係

  • ビルド時: libprism (C ライブラリ)、CRuby(ブートストラップ用のみ)
  • 実行時: なし。生成されたバイナリは libc と libm のみが必要です。
  • Regexp: 組み込みエンジンを使用するため、外部ライブラリは不要です。
  • Bigint: mruby-bigint から導出された組み込み機能であり、使用時にのみリンクされます。

歴史

Spinel は当初 C (約 1.8 万行目の c-version ブランチ) で実装され、その後 Ruby (ruby-v1 ブランチ) に書き換えられ、最後に自己完結型の Ruby サブセット(現在の master ブランチ)に書き換えられました。

ライセンス

MIT ライセンス。LICENSE ファイルを参照してください。

同じ日のほかのニュース

一覧に戻る →

2026/04/25 4:30

私のオーディオインターフェースは、SSH がデフォルトで有効になっています。

## Japanese Translation: Rodecaster Duo は、ファームウェア署名検証の欠如に起因する深刻なセキュリティ脆弱性を有しています。この欠陥は、研究者がコンテナを通じて SSH パスワード認証を有効にする自製ファームウェアの開発および展開に成功した事例から明らかとなりやすく、カスタムソフトウェアへのフラッシングを可能にしています。同装置は USB 書き込みを無効化することでブリック状態を防ぐように設計されていますが、この制限は回避可能であり、初期試みが失敗した場合(例:macOS)や予期せずトリガーされた場合でもファームウェア更新が進められる可能性があります。USB HID プロトコルの解析により、ファームウェア更新はレポート 1 を介して送信される単純な ASCII コマンド('M」と 'U」)によって開始されることが明らかとなり、実際のファイル操作にはディスクのマウント、`archive.tar.gz`および`archive.md5`の複製、許可付きパーミッションの設定(777)、そして再起動が含まれます。同装置はデフォルトで公開鍵認証とハードコードされた鍵を使用した SSH が有効になっており、これらの鍵が変更されない場合、セキュリティリスクを引き起こします。これらの欠陥についてサポートチケットへの回答はなく、システムのオープンな性質が、この専門的なオーディオツールを有益な改変のプラットフォームであると同時に、潜在的なセキュリティ侵害のプラットフォームにも変えつつあります。

2026/04/25 4:01

クラシック・アメリカン・ダイナー

## Japanese Translation: 米国のダイナーは、輸送史と料理文化の独自の融合を表しており、20 世紀に鉄道輸送用に製造された列車車から、象徴的な路辺のレストランへと進化してきました。歴史記録によれば、主要な高速道路沿いには多様なメニューが提供されており、1940 年夏のマーランド州バーウィン近郊のダイナーではホットドッグが 5 セント、プレートが 25 セント、ニューヨーク市の施設では 1959 年に朝食プレートが 75 セントで提供されていました。長い労働時間に対応するため、多くの場所はアバディーン(マリーランド州)などで見られるように 24 時間営業のモデルを採用し、トラックドライバーだけでなく一般住民もアクセスできるようにしました。国会図書館はこれらの施設の建築的多様性を捉えた広範な写真アーカイブを保管しており、ジョージア州カラムスにあるルート 27 ダイナー(韓国料理も販売している)の波打つ金属製の外観から、バーモント州チェスターにある「ストリームライン」アルミニウム仕上げのクラウニーガールダイナーまで多岐にわたります。これらの画像は単なる食料供給を超え、ダイナーを米国人の好意と国家の記憶を象徴する強力なシンボルとして示しており、20 世紀初頭からの文書からキャロル・M・ハイズミスの作品のような現代アーカイブに至るまで、ピジョンフォージにあるサンライナーダイナーやフェニックスにある5&ダイナーなどの現場を捉えています。

2026/04/24 23:28

過剰な思考、スコープクリープ、そして構造的相違によって引き起こされるプロジェクトへの sabotaging です。

## Japanese Translation: 著者は「やってみるか」という哲学を提唱し、広範な調査よりも即時の作成を優先することで楽しみを保ち、スコープの蔓延を防ぐことを重視する。この転換は、ハードウェアのプロトタイピングインターフェース、Clojure+Rust 融合言語、CAD プログラミングといった長年にわたる技術的関心を扱うが、焦点のない成功基準により数百時間の投入にも関わらず合成された解決策をもたらさず、こうしたサイクルを打破するために著者は迅速なプロトタイピングに注力する。友人の Marcin と一緒に週末プロジェクトとして製作した合板の棚は、機能的成果に絞って完璧な仕様ではなくてはしごを作ったものであり、逆にリソースが不要な機能や過剰な調査に浪費されるときには失敗する。具体的には、LLM エージェントプロジェクト(Finda スタイルのファイルシステム検索)でアンカー機能を見捨てること、difftastic、semanticdiff.com、diffsitter などのツールを数時間レビューして高レベル構造を正しく処理できないことが判明した例などが挙げられる。こうした限界に失望した著者は、Tyvek/ライトディフューザー材料の EU ベンダーを探したり、Coinbase クリプト破産分析から酵母ワクチンや Loon Lisp まで幅広い話題に触れたりする雑多な更新事項も記録している。