譯者 | 朱先忠
策劃 | 云昭
WebAssembly已經(jīng)存在了一段時間,但到目前為止,它對高級語言的實用性有限,尤其是使用垃圾收集的語言。然而,隨著web瀏覽器即將推出對托管內(nèi)存的支持,情況即將發(fā)生變化,這使得WebAssembly成為Scheme、OCaml以及所有非C++或Rust使用者的可行目標。
本文將回顧為什么1.0版本的WebAssembly不是Scheme的好目標,變通方法是什么,新設(shè)施是什么,實現(xiàn)將如何利用它們,以及仍然存在哪些限制。在2-3年內(nèi),WebAssembly將成為我們許多最熟悉的語言的優(yōu)秀編譯目標和語言運行時基礎(chǔ)。
WebAssembly,終于要來了。
1、現(xiàn)實中的WebAssembly
WebAssembly可以看作是C編譯器的一個奇怪的后端。目前,只有少數(shù)幾種源語言能夠成功運行在WebAssembly上。
Haskell、Ocaml、Scheme、F#等語言呢?我們呢?我們只算是一些懶蟲嗎?
那我們?yōu)槭裁床辉谀抢锬??WebAssembly上的Clojure在哪里?F#、Elixir和Haskell編譯器在哪里?人們早期的確作出一些努力,但并沒有真正成功。為什么?我們只是沒有付出努力嗎?為什么Rust語言可以飛速發(fā)展,而Scheme語言卻沒有呢?
WebAssembly的1.0和2.0版本還不能算是良好的支持垃圾收集的語言,讓我們仔細看看其原因。
事實證明,在WebAssembly上尚不存在好的Scheme語言實現(xiàn)是有原因的:如果您的語言依賴于存在垃圾收集器的話,那么WebAssembly的初始版本就是一個可怕的目標。盡管已經(jīng)取得了一些進展,但對于當前標準化和部署的WebAssembly版本仍然有待觀察。為了更好地理解這個問題,讓我們深入研究這個系統(tǒng)的內(nèi)部,看看它的局限性是什么。
2、GC和WebAssembly 1.0
基于垃圾收集(GC:Gabage Collecting)的值類型存儲在什么地方呢?
對于WebAssembly 1.0而言,唯一可能的答案是:線性內(nèi)存。
WebAssembly 1.0為您提供的表示數(shù)據(jù)的原型是所謂的線性內(nèi)存:其實,也就是一個可以讀取和寫入的字節(jié)緩沖區(qū)。除了內(nèi)存布局更簡單之外,這與本機編譯時得到的非常相似。您可以以64 KB的頁面為單位獲取此內(nèi)存。在上面的例子中,我們將請求10個頁面,640kB。應(yīng)該足夠了,對吧?我們將把它全部用于垃圾收集器,并使用一個凹凸指針分配器。堆指針/分配指針保存在可變?nèi)肿兞?hp中。
這里給出了分配函數(shù)的樣子。分配函數(shù)$alloc類似于C語言中的malloc函數(shù):它使用一些字節(jié)作參數(shù)并返回一個指針。在WebAssembly中,指向內(nèi)存的指針只是一個偏移量,它是一個32位整數(shù)(i32)。(使用64位地址空間的選項已經(jīng)納入計劃中,但還不是標準的實現(xiàn)。)
如果這是您第一次看到WebAssembly函數(shù)的文本表示,那么您可以盡情地學習一下,但這卻不是我上面演示內(nèi)容的重點;我想強調(diào)的是call $gc——當分配指針到達區(qū)域的末尾時所發(fā)生的事情。
3、GC和WebAssembly 1.0(1)
調(diào)用$gc背后隱藏著什么?答案是:通過線性內(nèi)存運送GC?;凇癝top-The-World”技術(shù),采取非并行、非并發(fā)機制,而是……根節(jié)點。
首先要注意的是,您必須自己提供$gc。當然,這是可行的——這就是我們在編譯到本地目標時所要做的工作。
不幸的是,盡管WebAssembly中的多線程支持有些不足;不過,它允許您共享內(nèi)存并使用原子操作,只是您必須在WebAssembly之外創(chuàng)建線程。在開發(fā)實踐中,您交付的GC可能沒有利用多線程優(yōu)勢,因此它可能是相當原生態(tài)的,以致于將所有垃圾收集工作推遲到“Stop-The-World”階段。
4、GC和WebAssembly 1.0(2)
活動對象包括:
- 根(roots)節(jié)點
- 活動對象引用的任何對象
其中,根(roots)節(jié)點是活動堆棧幀中的全局值與局部值。
注意:你無法通過編程來訪問活動堆棧幀。
更糟糕的是,您無法訪問堆棧上的根(roots)節(jié)點。一個GC必須保留活動對象——它們是被循環(huán)定義為根引用的任何對象或活動對象引用的任何物體。從根節(jié)點開始,作為全局變量或者是被活動堆??蚣芤玫娜魏蜧C托管對象。
但是,這種情況下我們遇到了問題,因為在WebAssembly(任何版本,而不僅僅是1.0版本)中,你不能遍歷堆棧,所以你根本找不到活動的堆棧幀;當然,這樣一來你也找不到堆棧根節(jié)點。(有時,人們希望將其作為一種低級別的能力來支持;但一般來說,最后形成的共識似乎是,如果由引擎來負責實現(xiàn)GC機制,那么系統(tǒng)的整體性能會更好一些;但目前這僅是一個預兆而已?。?/p>
5、GC和WebAssembly 1.0(3)
針對上述問題,一種變通的解決方案是:
- 精確控制堆棧的根節(jié)點
- 將所有可能的指針值溢出到線性內(nèi)存并保守地收集
考慮到堆棧的不可迭代性,基本上有兩種變通的解決方案。一種是讓編譯器和運行時維護對象根節(jié)點的顯式堆棧;這樣一來,垃圾收集器可以確定這些根節(jié)點是指針。這種辦法很好,因為它可以讓你移動物體。但是,維護堆棧也是一筆開銷;基于現(xiàn)有技術(shù)的解決方案是創(chuàng)建一個輔助表(“堆棧映射”),將可以調(diào)用GC的每個潛在點與如何找到根節(jié)點的指令相關(guān)聯(lián)。
另一種解決方法是將整個堆棧溢出到內(nèi)存中。或者,可能只是類似指針的值;無論如何,需要保守地掃描所有關(guān)鍵詞,以便搜索到可能是根節(jié)點的東西。但是,采用這種方案的話需要我們必須自己訪問內(nèi)存,而不是訪問WebAssembly實現(xiàn)會溢出堆棧到達的內(nèi)存區(qū)。這種方案可能還湊合;但它算是次優(yōu)的。有興趣的讀者可以參閱我最近關(guān)于Whippet垃圾收集器的帖子(https://wingolog.org/archives/2023/02/07/whippet-towards-a-new-local-maximum),以深入了解基于保守性根節(jié)點搜索的含義。
6、GC和WebAssembly 1.0(4)
· 無法收集外部對象(如JavaScript)的循環(huán)。
· 指向GC托管對象的指針是對線性內(nèi)存的偏移,需要在線性內(nèi)存上具有從外部·讀取/寫入對象的能力。
· 無法將內(nèi)存回饋給操作系統(tǒng)。
· 想進行詳細的“腸道檢查”:它的回答是“NO”。
如果僅此而已,情況就不那么好了,而且情況會變得更糟!線性內(nèi)存GC的另一個問題是,它限制了將多個模塊和主機組合在一起的可能性,因為在Web瀏覽器中管理JavaScript對象的垃圾收集器對線性內(nèi)存上的垃圾收集器一無所知。在這樣的系統(tǒng)中,您可以很容易地創(chuàng)建內(nèi)存泄漏。
此外,為了讀取或?qū)懭雽ο蟮淖侄?,對線性內(nèi)存中對象的引用需要對所有線性內(nèi)存進行任意讀寫訪問,這一點非常令人討厭。那么,如何在適當修改的情況下構(gòu)建一個可靠的系統(tǒng)呢?
最后,一旦你收集了垃圾,也許你設(shè)法壓縮了內(nèi)存,你就不能返回給操作系統(tǒng)任何東西了。對于這點,已經(jīng)有一些建議正在醞釀中,但還沒有實現(xiàn)。
如果BOB的觀眾必須在“更糟糕的是更好的”和“正確的事情”之間做出選擇的話,我認為BOB的觀眾更接近于選擇“正確的事情”。像這樣的觀眾本能地會對丑陋的系統(tǒng)感到厭惡;我認為GC相對于線性存儲差不多也描述了一個丑陋的系統(tǒng)。
7、GC和WebAssembly 1.0(5)
瀏覽器中已經(jīng)存在一個高性能并發(fā)并行壓縮的GC了。
關(guān)鍵是,WebAssembly 1.0要求您編寫和交付一個糟糕的GC,而主機中可能已經(jīng)有一個很棒的GC——一個投入了數(shù)百人年努力的GC,一個肯定會做得比你做得更好的GC。web瀏覽器中托管的WebAssembly應(yīng)該可以訪問瀏覽器的垃圾收集器!
我有一種感覺,當我們這些對垃圾收集語言情有獨鐘的人,一直站旁觀席上時,Rust和C++程序員們卻一直忙于“在球場上進球”。是的,他們被“球”絆倒了,但最終他們還是設(shè)法在擊“球”距離內(nèi)成功了。
8、變革即將到來!
對內(nèi)置GC的支持將于2023年第四季度推出。
有了GC,基本條件已經(jīng)到位。
讓我們將語言編譯到WebAssembly。
為了繼續(xù)使用體育環(huán)境下“足球”的比喻,我認為在下半場,我們的球員將最終能夠上場,并達到眾所周知的110%。WebAssembly用戶開始支持垃圾收集,我認為即使到今年年底,它也將在主要瀏覽器中推出。這將是一個大事件!我們有機會,我們需要好好把握。
當然,正如我前面所提到的,WebAssembly仍然是一臺奇怪的機器:作為編譯目標有些奇怪,在運行時也是如此。對于調(diào)試支持方面,簡直是一場恰到好處的混亂;也許過段時間會有其他關(guān)于這方面的文章。
對于如何表示字符串,這是一個令人驚訝的棘手問題;在WebAssembly標準社區(qū)中,有人認為JavaScript和WebAssembly可以共享底層字符串表示,也有人認為這是一件愚蠢的事,而認為復制是唯一的出路。我不知道哪一方會獲勝;也許稍后也會有更多關(guān)于這方面的內(nèi)容。
類似地,與JavaScript的整個互操作問題在很大程度上處于早期階段,目前的情況是選擇什么都不做而不是做錯事。您可以將WebAssembly(ref-eq)傳遞給JavaScript,但JavaScript對它無能為力:它沒有原型?,F(xiàn)有技術(shù)還提供了一個JS運行時,它封裝了每個wasm對象,將wasm模塊中導出的函數(shù)代理為對象方法。
最后,一些語言實現(xiàn)確實需要JIT支持,比如PyPy。
9、總結(jié)
有了GC支持,WebAssembly現(xiàn)在已經(jīng)為我們準備好了。
把我們熟悉的語言放在WebAssembly上現(xiàn)在已經(jīng)是一件很容易的事情了。
那么,讓我們在下半場進球吧!
請訪問以下有關(guān)鏈接:
- "gitlab.com/spritely/guile-hoot-updates"
- "wingolog.org"
- "wingo@igalia.com"
- "igalia.com"
- "mastodon.social/@wingo"
事實證明,WebAssembly在C、C++、Rust等方面取得了一些巨大的勝利,但現(xiàn)在輪到我們參與到游戲中了。GC即將到來,作為一個社區(qū),我們需要準備好編譯器和語言運行時。讓我們沖好咖啡,把一些字節(jié)放在一起;現(xiàn)在還為時過早,不過,對于擁有最佳WebAssembly體驗的語言社區(qū)來說,這是一個值得贏得的世界。
10、WebAssembly是一個令人興奮的新型通用計算平臺
WebAssembly到底是什么?它不是一種你用于編寫軟件的編程語言,而是一種編譯目標:如果你愿意學習的話,它基本算是一種匯編語言。
對于此平臺,它擁有可預測的便攜性能:
- 低層級上運行
- 本地代碼約占不到10%
通過隔離實現(xiàn)可靠的組合:
- 默認情況下,模塊不共享任何內(nèi)容
- 沒有夢魘般錯誤
- 提供內(nèi)存沙盒支持
你需要將代碼編譯到WebAssembly,以便更輕松地進行部署和創(chuàng)作。
如果你把WebAssembly的特點看作一臺抽象機器;那么,對我來說,它在以下兩個主要領(lǐng)域比其他機器有所進步。
首先,它接近本質(zhì)——例如,如果您將圖像處理庫編譯到WebAssembly并運行它,與將其編譯到x86-64或ARMv8或其他版本相比,您將獲得類似的性能。(特別是對于圖像處理,本地運行通常仍然會獲勝,因為WebAssembly中的SIMD(單指令多數(shù)據(jù)流)原語更窄,而且將圖像放入和取出WebAssembly可能意味著一個要創(chuàng)建一個副本。)WebAssembly的指令集涵蓋了廣泛的低級別操作,這使編譯器能夠生成高效的代碼。
這里的新穎之處在于WebAssembly既可移植,又很成功。我們這些程序員“怪人”知道,僅僅在技術(shù)上做得更好是不夠的:你還必須成功地為你的替代方案爭取吸引力。
第二個有趣的特征是,WebAssembly(一般來說)遵循最小權(quán)限體系架構(gòu):WebAssembly模塊從一開始就只能夠訪問它自己。模塊實例所具有的任何功能都必須在實例化時由主機顯式地與其共享。這不同于可以訪問所有主內(nèi)存的DLL,也不同于可以改變?nèi)謱ο蟮腏avaScript庫。這一特性使WebAssembly模塊能夠可靠地組成更大的系統(tǒng)。
11、大肆宣傳WebAssembly吧
所有瀏覽器都支持WebAssembly!因此,您的代碼能夠提供給世界上的任何人使用!
它就運行在你的身邊!從靠近用戶的網(wǎng)站運行代碼!
把一個庫(例如Expat)組合到你的瀏覽器程序(例如Firefox)中,不需要冒任何風險!
這是一種新的輕量級虛擬化:Wasm正相當于容器對虛擬機的作用!因此,不再需要耗費花在Kubernetes上的現(xiàn)金!
同樣,WebAssembly正在取得成功!它在你所有的手機、所有的桌面web瀏覽器、所有的內(nèi)容分發(fā)網(wǎng)絡(luò)上;在某些情況下,它似乎會取代云中的容器。
原文鏈接:??https://www.wingolog.org/archives/2023/03/20/a-world-to-win-webassembly-for-the-rest-of-us??
譯者介紹:
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。