發布日期:2025 年 1 月 29 日
WebAssembly 垃圾收集 (WasmGC)
程式設計語言分為兩種:垃圾收集程式設計語言和需要手動記憶體管理的程式設計語言。前者包括 Kotlin、PHP 或 Java。後者的例子包括 C、C++ 或 Rust。一般來說,高階程式設計語言較有可能將垃圾收集設為標準功能。
簡單來說,垃圾收集是指嘗試回收程式已分配但不再參照的記憶體。這種記憶體稱為垃圾。實作垃圾回收機制的策略有很多種。最容易理解的其中一種方法是參照計數,其目的是計算記憶體中物件參照的數量。
這可能讓人覺得很難理解,但程式設計語言會在其他程式設計語言中實作。舉例來說,PHP 執行階段主要實作於 C 中。如果開發人員想要將 PHP 等語言編譯為 Wasm,通常需要編譯所有部分,例如語言的剖析器、程式庫支援、垃圾收集和其他重要元件。
Wasm 會在主機語言 JavaScript 的背景下,在瀏覽器中執行。在 Chrome 中,JavaScript 和 Wasm 會在 V8 中執行,V8 是 Google 的開放原始碼 JavaScript 引擎。而且 V8 已具備垃圾收集器。也就是說,如果開發人員使用已編譯為 Wasm 的 PHP,最後會將移植語言 (PHP) 的垃圾收集器實作項目傳送至已具備垃圾收集器的瀏覽器,這麼做會造成浪費,這時就需要 WasmGC 的協助。
如要進一步瞭解 WasmGC,請參閱「Chrome 現已預設啟用 WebAssembly 垃圾收集功能 (WasmGC)」一文,如想深入瞭解相關資訊,請參閱 V8 網誌文章「A new way to bring garbage collected programming languages efficiently to WebAssembly」。
Wasm 尾部呼叫最佳化
如果呼叫是從目前函式傳回之前執行的最後一個指令,就稱為位於尾端位置。編譯器可以捨棄呼叫者影格,並將呼叫替換為跳躍,藉此最佳化這類呼叫。這對於遞迴函式特別實用。舉例來說,請考慮這個 C 函式,它會將連結清單的元素加總:
int sum(List* list, int acc) {
if (list == nullptr) return acc;
return sum(list->next, acc + list->val);
}
在一般呼叫中,這會消耗 O(n) 堆疊空間:清單中的每個元素都會在呼叫堆疊上新增一個影格。如果清單過長,這可能會很快導致堆疊溢位。將呼叫替換為跳躍,尾部呼叫最佳化可有效地將這個遞迴函式轉換為使用 O(1) 堆疊空間的迴圈:
int sum(List* list, int acc) {
while (list != nullptr) {
acc = acc + list->val;
list = list->next;
}
return acc;
}
這項最佳化作業對於功能性語言而言特別重要。它們高度仰賴遞迴函式,而 Haskell 等純函式甚至不提供迴圈控制結構。任何類型的自訂疊代通常都會使用遞迴。如果沒有尾部呼叫最佳化,任何非簡單的程式都會很快遇到堆疊溢位,因為堆疊空間很快就會用盡。
最初,WebAssembly 不允許這類尾端呼叫最佳化,但這項規定在尾端呼叫擴充功能提案中有所改變。如要進一步瞭解,請參閱 V8 網誌上的「WebAssembly 尾端呼叫」一文。
結論
如今,WasmGC 和尾部呼叫最佳化功能已成為新版基準,因此更多應用程式可以使用這些功能來提升效能,例如 Google 試算表透過將 Google 試算表計算 worker 移植至 WasmGC,就達到了這項目的。