互換性
TruffleRubyは、C拡張機能を含む、Rubyの標準実装であるMRIのバージョン3.1.3との完全な互換性を目指しています。TruffleRubyは今なお開発中であるため、まだ100%の互換性があるわけではありません。
TruffleRubyはRailsを実行でき、C拡張機能を含め、多くのgemと互換性があります。TruffleRubyはruby/specの約97%に準拠しており、他の代替Ruby実装を上回っています。
MRIとの非互換性は、後で詳述するまれなケースを除き、バグとみなされます。MRIとの非互換性を見つけた場合は、報告してください。
TruffleRubyはMRIの動作にできるかぎりあわせるように努めています。いくつかの限られたケースでは、より優れた機能を提供するために、TruffleRubyは意図的にMRIとの互換性を持っていません。
識別
TruffleRubyでは、識別のために次の定数が定義されます:
RUBY_ENGINE
は'truffleruby'
です。RUBY_VERSION
は、互換性のあるMRIバージョンです。RUBY_REVISION
は、TruffleRubyのビルドに使用された完全なgit
コミット・ハッシュです(MRI 2.7以上と同様)。RUBY_RELEASE_DATE
はgit
コミット日です。RUBY_PATCHLEVEL
は常にゼロです。RUBY_ENGINE_VERSION
はGraalVMバージョンであるか、または0.0-
およびGitコミット・ハッシュです(ビルドがGraalVMリリースの一部でない場合)。
C APIでは、プリプロセッサ・マクロTRUFFLERUBY
が定義されており、#ifdef TRUFFLERUBY
で確認できます。
提供されない機能
継続およびcallcc
MRIでは継続が廃止され、かわりにファイバが推奨されています。継続およびcallcc
のセマンティクスは根本的にJVMアーキテクチャに適合しないため、これらがTruffleRubyに実装される可能性はほとんどありません。
フォーク
TruffleRubyインタプリタをfork
することはできません。JVMで実行する場合、この機能がサポートされる可能性はほとんどありませんが、ネイティブ構成では将来サポートされる可能性があります。fork
を使用できるかどうかをテストするための移植可能かつ正しい方法は次のとおりです:
Process.respond_to?(:fork)
標準ライブラリ
次の標準ライブラリはサポートされていません:
continuation
: MRIで廃止されましたdebug
:RubyVM::InstructionSequence
に依存し、かわりにVSCode拡張または--inspect
を使用しますio/console
: 部分的に実装されていますio/wait
: 部分的に実装されていますpty
: 将来実装される可能性があります
TruffleRubyには、JRubyと同様に、ffi
gemの独自のバックエンド実装が用意されています。これは完全に透過的であり、MRIの場合と同様に動作します。この実装はほぼ完了し、ほとんど使用されないまれなケースを除き、ffi
gemの仕様すべてに準拠しています。
MRIの内部機能
RubyVM
はユーザー向けではなく、実装されていません。
大きい違いのある機能
スレッドがパラレルに実行される
MRIでは、スレッドは、パラレルではなく同時にスケジュールされます。TruffleRubyでは、スレッドがパラレルにスケジュールされます。JRubyおよびRubiniusの場合と同様に、独自の共有可変データ構造へのアクセスを正しく同期することについてはユーザーが責任を負い、インタプリタの状態を正しく同期することについてはTruffleRubyが責任を負います。
ファイバのパフォーマンス特性がMRIと同じでない
ファイバのほとんどのユースケースは、起動が容易かつ低コストであることと、メモリーのオーバーヘッドが低いことを前提としています。TruffleRubyでは、ファイバは現在、オペレーティング・システム・スレッドを使用して実装されるため、パフォーマンス特性はRubyスレッドと同じです。Loomプロジェクトが安定し、JVMリリースで使用可能になったら、このことに対処する予定です。
内部としてマークされるクラスが異なる
MRIでは、MRI (CRuby)でのみ使用可能であるとしてドキュメントに記載されているクラスがいくつか提供されています。これらのクラスは、実装することが実用的であれば実装されますが、必ずしもそうではありません。たとえば、RubyVM
は使用できません。
Regexp
Regexp
インスタンスは、TruffleRubyでは常に不変です。CRuby 3.1では、リテラルのRegexp
はすべて不変ですが、リテラルでないものは引き続き可変です。この制限は、Regexpインスタンスでシングルトン・メソッドを定義できず、TruffleRubyでRegexpのサブクラスのインスタンスを作成できないことを意味します。
わずかな違いがある機能
コマンドライン・スイッチ
-y
、--yydebug
、--dump=
および--debug-frozen-string-literal
スイッチはサポートされていない開発ツールであるため、無視されて警告が表示されます。
マジックコメントを伴う-e
引数が渡されるプログラムには、UTF-8またはUTF-8のサブセットであるエンコーディングが必要です。これは、引数を取得するまでにJVMによってそれらがすでにデコードされているためです。
--jit
オプションおよびjit
機能はTruffleRubyでは機能せず、警告が表示されます。使用可能な場合は常にGraalVMコンパイラが使用されます。
Stringの最大バイトサイズが231-1である
RubyのStringは、Javaのbyte[]
として表されます。JVMでは、231-1という最大配列サイズが強制されるため(32ビットの符号付きint
にサイズを格納することによる)、RubyのStringを231-1バイトより長くすることはできません。つまり、Stringは2GBより小さくする必要があります。これは、JRubyと同じ制限事項です。回避策として、ネイティブに割り当てられた文字列を使用することが考えられますが、ネイティブ文字列に対するすべてのRuby String操作をサポートする労力が大きくなります。
スレッドが様々なポイントで中断を検出する
TruffleRubyスレッドは、プログラム内の中断されたポイントとして、MRIの場合とは異なる場所を検出する可能性があります。一般的に、TruffleRubyはMRIよりも早期に中断を検出する傾向があるようです。JRubyとRubiniusもMRIとは異なります。MRIでは動作が文書化されておらず、MRIのバージョン間で変更される可能性があるため、中断ポイントに依存することはお薦めしません。
ポリグロットの標準I/Oストリーム
ポリグロット・エンジンによって提供される標準I/Oストリームを、試験段階の--polyglot-stdio
オプションを介して使用すると、ファイル記述子0、1および2に対する読取りおよび書込みがこれらのストリームにリダイレクトされます。つまり、これらのファイル記述子に対する他のI/O操作(isatty
など)は、これらのストリームが実際に終了する場所に関連しなくなる場合があり、dup
などの操作がポリグロット・ストリームへの接続を失う可能性があります。たとえば、一部のロギング・フレームワークで行われるように、$stdout.reopen
を実行した場合、ポリグロット出力ではなく、ネイティブの標準出力が使用されます。
また、I/Oバッファ・ドレイン、sync
が設定されたI/Oオブジェクトに対する書込み、およびwrite_nonblock
では、EAGAIN
およびEWOULDBLOCK
が発生しても、それを検出する方法がストリームによって提供されないため、書込みが再試行されません。
エラー・メッセージ
エラー・メッセージ文字列は、通常はRuby Spec Suiteまたはテストの対象にならないため、MRIとは異なる場合があります。
シグナル
TruffleRubyが処理できる一連のシグナルはMRIとは異なります。ネイティブ構成を使用する場合、TruffleRubyでは、MRIでトラップされるものと同じシグナルをすべてトラップできるとともに、MRIではトラップされないものもいくつかトラップできます。トラップできないシグナルは、KILL
、STOP
およびVTALRM
のみです。したがって、ネイティブ構成では変更を加えることなく、MRI上で実行されるすべてのシグナル処理コードをTruffleRuby上で実行できます。
ただし、JVMで実行する場合、TruffleRubyはUSR1
またはQUIT
をトラップできません。これらのシグナルはJVMによって予約されているためです。このような場合、trap(:USR1) {}
によってArgumentError
が発生します。これらのシグナルをトラップできることに依存するコードは、使用可能な別のシグナルにフォールバックする必要があります。また、FPE
、ILL
、KILL
、SEGV
、STOP
およびVTALRM
をトラップすることはできませんが、これらのシグナルはMRIでも使用できません。
TruffleRubyをポリグロット・アプリケーションの一部として実行する場合、別の言語によって処理されたシグナルはTruffleRubyでトラップできなくなります。
GC統計
TruffleRubyではMRIと同様のGC.stat
統計が提供されますが、すべての統計を使用できるわけではなく、一部の統計は近似値である場合があります。実際値または近似値で提供される値を確認するには、GC.stat.keys
を使用してください。値がない場合、0
が返されます。
パフォーマンスが非常に低い機能
ObjectSpace
ObjectSpace#each_object
は実装されていますが、ヒープ全体を反復する必要があり、基本的にGCマーキング・フェーズと同等の処理を行うため、かなり低速です。ObjectSpace#trace_object_allocations_start
は、CRubyの動作と同様に、すべての割当てを遅くします。ObjectSpace
のほとんどのメソッドを使用すると、プログラムのパフォーマンスが一時的に低下します。それらをテスト・ケースや他の同様のオフライン操作で使用することは問題ありませんが、本番アプリケーションの内部ループでは使用しないことをお薦めします。
set_trace_func
set_trace_func
を使用すると、プログラムのパフォーマンスが一時的に低下します。ObjectSpace
と同様に、本番アプリケーションの内部ループではこれを使用しないことをお薦めします。
バックトレース
例外のスローやバックトレースを作成する必要があるその他の操作は、一般的にMRIよりも遅くなります。これは、バックトレース・エントリを再作成するには、Rubyコードを高速に実行するために適用された最適化をTruffleRubyで元に戻す必要があるためです。いずれにしてもRubyの実装で制御フローに例外を使用することはお薦めしません。
この問題を軽減するために、バックトレースが使用されないことを検出できる場合、それらは自動的に無効になります。
C拡張機能との互換性
識別子がマクロである場合もあれば、関数である場合もある
通常はマクロである識別子が関数である場合や、関数がマクロである場合があり、また、グローバル変数がマクロである場合もあります。これにより、特定の実装に依存するコンテキストでそれらが使用された場合に、問題が発生することがあります(そのアドレスの取得、関数ポインタ変数への割当て、defined()
を使用した、マクロが存在するかどうかの確認など)。これらの問題はすべてバグとみなされ、修正の対象となります。このようなケースがあれば報告してください。
rb_scan_args
rb_scan_args
では、最大10個のポインタのみがサポートされます。
rb_funcall
rb_funcall
では、最大15個の引数のみがサポートされます。
RDATA
およびRTYPEDDATA
のmark
関数
RDATA
およびRTYPEDDATA
のmark
関数は、ガベージ・コレクション中にコールされるのではなく、定期的にコールされます。オブジェクトに関する情報は、それらが構造体に割り当てられるとキャッシュされ、キャッシュが一杯になると、TruffleRubyはすべてのmark
関数を定期的に実行して、ガベージ・コレクタが理解する方法でそれらのオブジェクト関係を表します。このプロセスはMRIと同じように動作します。
JRubyとの互換性
RubyからJavaへの相互運用性
TruffleRubyは、JRubyがサポートするものと同じJavaへの相互運用性インタフェースをサポートしていません。TruffleRubyには、Javaを含む複数の言語と相互運用するための代替ポリグロットAPIがかわりに用意されています。
JavaからRubyへの相互運用性
JavaからのRubyコードのコールは、GraalVMポリグロットAPIによってサポートされています。
Java拡張機能
JRuby用に記述されたJava拡張機能の使用はサポートされていません。
ネイティブ構成でまだサポートされていない機能
ネイティブ構成でのTruffleRubyの実行は、JVMで実行する場合とほとんど同じです。両方のVMで使用されるガベージ・コレクタが異なるため、リソース管理には違いがありますが、機能的には、これらは基本的に互いに同等です。
ネイティブ構成とのJavaの相互運用性
Javaの相互運用性はネイティブ構成で機能しますが、より多くの設定が必要です。デフォルトでは、Javaの相互運用性のためにイメージで使用できるのは一部の配列クラスのみです。TruffleRubyを含むネイティブ・イメージをコンパイルすることにより、さらにクラスを追加できます。詳細は、ここを参照してください。
仕様の完全性
仕様はいくつあるかという問いに、簡単かつ正確に答えることはできません。仕様の数は、Ruby言語のバージョン、プラットフォーム、および仕様のバージョンによって異なります。また、標準ライブラリおよびC拡張機能APIの仕様は非常に不均一であり、誤解を招く結果が生じる可能性があります。
このブログ投稿には、TruffleRubyが準拠している仕様の数がまとめられています。