- 適用於 JDK 23 的 GraalVM (最新版)
- 適用於 JDK 24 的 GraalVM (搶先體驗版)
- 適用於 JDK 21 的 GraalVM
- 適用於 JDK 17 的 GraalVM
- 封存
- 開發版本
相容性
TruffleRuby 的目標是與 Ruby 的標準實作 MRI 版本 3.2.2 完全相容,包含 C 擴充功能。TruffleRuby 仍在開發中,因此尚未達到 100% 相容。
TruffleRuby 可以執行 Rails,並與許多 gem 相容,包括 C 擴充功能。TruffleRuby 通過了大約 97% 的 ruby/spec,比任何其他替代的 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
是 TruffleRuby 版本,或者,如果您的建置不是 TruffleRuby 發行版的一部分,則為0.0-
和 Git 提交雜湊值。
在 C API 中,定義了預處理器巨集 TRUFFLERUBY
,可以使用 #ifdef TRUFFLERUBY
檢查。
Ruby 3.x 功能 #
TruffleRuby 支援 Ruby 3.2 及更早版本的大部分功能。但是,某些功能尚未實作。請參閱以下問題以了解詳細資訊
完全缺失的功能 #
Continuations 和 callcc
#
Continuations 在 MRI 中已過時,建議改用 Fibers。Continuations 和 callcc
不太可能在 TruffleRuby 中實作,因為它們的語義根本不符合 JVM 架構。
Fork #
您不能 fork
TruffleRuby 解譯器。當在 JVM 上執行時,此功能不太可能被支援,但未來可能會在原生組態中支援。測試 fork
是否可用的正確且可移植的方法是
Process.respond_to?(:fork)
標準函式庫 #
以下標準函式庫不受支援
continuation
:在 MRI 中已過時debug
:它依賴於RubyVM::InstructionSequence
,請改用 VSCode 擴充功能 或--inspect
io/console
:部分實作io/wait
:部分實作pty
:未來可能會實作
TruffleRuby 為 ffi
gem 提供自己的後端實作,類似於 JRuby。這應該是完全透明的,並且行為與在 MRI 上相同。此實作應該相當完整,並且通過了 ffi
gem 的所有規格,除了某些很少使用的邊緣情況。
內部 MRI 功能 #
RubyVM
不適用於使用者,並且未實作。
具有主要差異的功能 #
執行緒平行執行 #
在 MRI 中,執行緒是並行排程,但不是平行執行。在 TruffleRuby 中,執行緒是平行排程的。如同 JRuby 和 Rubinius,您有責任正確地同步對您自己的共享可變資料結構的存取,而 TruffleRuby 將負責正確地同步解譯器的狀態。
Fibers 不具有與 MRI 中相同的效能特性 #
Fibers 的大多數使用案例都依賴於它們易於且廉價地啟動,並且具有較低的記憶體開銷。在 TruffleRuby 中,Fibers 目前使用作業系統執行緒實作,因此它們具有與 Ruby 執行緒相同的效能特性。一旦 Loom 專案在 JVM 版本中變得穩定且可用,將會解決這個問題。
某些標示為內部的類別將會有所不同 #
MRI 提供一些在文件中描述為僅在 MRI (CRuby) 上可用的類別。如果實際可行,這些類別將會被實作,但情況並非總是如此。例如,RubyVM
不可用。
Regexp
#
在 TruffleRuby 中,Regexp
實例永遠是不可變的。在 CRuby 3.1 中,所有字面 Regexp
都是不可變的,但非字面仍然是可變的。此限制表示無法在 Regexp 實例上定義單例方法,並且無法在 TruffleRuby 上建立 Regexp 子類的實例。
具有細微差異的功能 #
命令列開關 #
-y
、--yydebug
、--dump=
和 --debug-frozen-string-literal
開關將被忽略並發出警告,因為它們是不受支援的開發工具。
以魔術註解傳遞在 -e
引數中的程式必須具有 UTF-8 或 UTF-8 子集的編碼,因為 JVM 在我們取得它們時已經解碼了引數。
--jit
選項和 jit
功能對 TruffleRuby 沒有影響,並會發出警告。當可用時,始終會使用 GraalVM 編譯器。
字串的最大位元組大小為 231-1 #
Ruby 字串表示為 Java byte[]
。JVM 強制執行 231-1 的最大陣列大小(透過將大小儲存在 32 位元帶正負號的 int
中),因此 Ruby 字串不能超過 231-1 個位元組。也就是說,字串必須小於 2GB。這與 JRuby 的限制相同。一個可能的解決方法可能是使用原生配置的字串,但要支援原生字串上的每個 Ruby 字串操作將需要很大的努力。
UTF-16 和 UTF-32 編碼的字串 #
TruffleRuby 不支援具有奇數位元組數(以原生位元組序)的 UTF-16 字串。同樣地,對於 UTF-32,它必須是 4 的倍數。這對於最佳化、壓縮、不變性等是必要的。
執行緒在不同點偵測中斷 #
與在 MRI 上相比,TruffleRuby 執行緒可能會在程式中的不同點偵測到它們已中斷。一般來說,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
上重試寫入,因為串流沒有提供偵測此情況的方法。
錯誤訊息 #
錯誤訊息字串有時會與 MRI 不同,因為這些通常不在 Ruby Spec Suite 或測試的範圍內。
訊號 #
首先,根據 POSIX (man 2 signal
),永遠無法捕獲 KILL
和 STOP
。某些訊號在 CRuby 上是保留的,它們在 TruffleRuby 上也是保留的,因為捕獲這些訊號會導致各種問題:SEGV
、BUS
、ILL
、FPE
和 VTALRM
。
當使用原生配置時,TruffleRuby 允許捕獲所有 MRI 能捕獲的相同訊號。因此,任何在 MRI 上執行的訊號處理程式碼,在原生配置下都能在 TruffleRuby 上無需修改地執行。
然而,當在 JVM 上執行時,TruffleRuby 無法捕獲 QUIT
訊號,因為此訊號已由 JVM 保留。在這種情況下,trap(:QUIT) {}
將會引發 ArgumentError
。任何依賴捕獲此訊號的程式碼,都需要回退到另一個可用的訊號。
當 TruffleRuby 作為多語言應用程式的一部分執行時,任何由其他語言處理的訊號,TruffleRuby 都無法捕獲。
GC 統計資訊 #
TruffleRuby 提供類似於 MRI 的 GC.stat
統計資訊,但並非所有統計資訊都可用,且某些統計資訊可能是近似值。使用 GC.stat.keys
來查看哪些提供了實際或近似值。遺失的值將會回傳 0
。
呼叫者位置 #
使用 Kernel#caller_locations
或 Thread.each_caller_location
可能會包含引擎特定的位置物件和/或路徑。這是預期的行為,必要時應在應用程式碼中進行過濾。
由 Thread.to_enum(:each_caller_location)
返回的列舉器不支援使用 .next
進行迭代。在 CRuby 中,這會引發 StopIteration
,而在 TruffleRuby 中,它會在未確定的 (與 .next
被呼叫的位置和方式有關) 呼叫堆疊上迭代。不建議在任何情況下使用此功能(無論是 CRuby 還是 TruffleRuby)。
效能極低的特性 #
ObjectSpace
#
ObjectSpace#each_object
已實作,但速度相當慢,因為需要迭代整個堆積,且本質上執行與 GC 標記階段等效的操作。 ObjectSpace#trace_object_allocations_start
會降低所有配置的速度,與 CRuby 上的行為類似。使用 ObjectSpace
上的大多數方法會暫時降低您程式的效能。在測試案例和其他類似的「離線」操作中使用它們是可以的,但您可能不希望在生產應用程式的內部迴圈中使用它們。
set_trace_func
#
使用 set_trace_func
將會暫時降低您程式的效能。如同 ObjectSpace
,建議您不要在生產應用程式的內部迴圈中使用此功能。
回溯追蹤 #
拋出例外和其他需要建立回溯追蹤的操作通常比在 MRI 上慢。這是因為 TruffleRuby 需要撤銷已應用於快速執行 Ruby 程式碼的優化,以便重新建立回溯追蹤條目。無論如何,不建議在任何 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 提供一個替代的多語言 API,用於與包括 Java 在內的多種語言進行互通。
Java 與 Ruby 的互通性 #
支援從 Java 呼叫 Ruby 程式碼,請使用 GraalVM 多語言 API。
Java 擴充 #
不支援使用為 JRuby 撰寫的 Java 擴充。
原生配置中尚未支援的功能 #
在原生配置中執行 TruffleRuby 與在 JVM 上執行大致相同。在資源管理方面存在差異,因為這兩種 VM 使用不同的垃圾收集器,但在功能上,它們基本上是相同的。
原生配置中的 Java 互通性 #
Java 互通性在原生配置中有效,但需要更多設定。預設情況下,只有一些陣列類別可用於映像中的 Java 互通性。您可以透過編譯包含 TruffleRuby 的原生映像來新增更多類別。詳情請參閱此處。
規格完整性 #
「有多少規格?」並不是一個容易且準確回答的問題。規格的數量因 Ruby 語言的不同版本、不同的平台和規格的不同版本而異。標準程式庫和 C 擴充 API 的規格也非常不均勻,可能會產生誤導性的結果。
這篇部落格文章總結了 TruffleRuby 通過了多少規格。