標準化進展非常緩慢。為此 Fermyon 加入了 字節碼聯盟 親自推動標準化進程,并通過構建 Spin 代碼實現來充分利用標準
語言支持不夠。Fermyon 認為前 20 種語言中至少有 15 種必須完全支持 WebAssembly 以及 WASI 和組件,才能正確地認為 WebAssembly 被很好地采用。Fermyon 采取的立場是將注意力集中在最受歡迎的語言上,這就是為什么使用 Rust 而不是 C 或Zig。
“
這方面也有一些好消息:
語言支持正在迅速增長,今年C#、Python和Ruby都增加了支持
wasi 支持現在是進入 wasm 游戲領域的籌碼
在主流實現語言未能發揮作用的地方,該語言的替代實現正在加緊發展,比如 tinygo 對于 wasm 的支持就超越了 go
生態系統不是可選的。WebAssembly 有望成為下一波計算浪潮,但除非 Fermyon 能圍繞它建立生態系統,所以 Fermyon 正在努力聯合相關其他企業合作共建社區。
社區實現碎片化。(比如 deno 和其他非標準化實現,文章沒有明說)。可悲的是,有時這僅僅是由于無知和不參與:“我們不知道有一個標準為此而出現”。所以目前發布的組件規范(正在進行中,但正在迅速成熟)旨在解決這類問題,這個標準使得在不同的主機實現之間共享 WebAssembly 二進制文件成為可能。
還有一個不幸的趨勢,即一些開發人員選擇與組件模型相反的工作,創建與他們自己的主機運行時的強鏈接。走這條路一方面會導致平臺鎖定,另一方面會毫無意義地重新編寫相同的代碼(針對略有不同的主機進行工具化)。幸運的是,那些準備最好的人(Fastly、Mozilla、Microsoft)反而選擇推動互操作性標準以造福所有人。這是正確的第一步。
為了阻止破壞性的碎片化“手榴彈”,我們必須增加社會壓力,不要我行我素,而要堅持互操作性標準。做到這一點的一個關鍵方法是彼此公開合作(通過字節碼聯盟、W3 和 CNCF 等組織),不僅要創建和實施標準,還要創建對話論壇。
“
Fermyon 的愿景是,在五年內,WebAssembly 將成為常態,而不是小眾市場。新一波應用程序將能夠利用 WebAssembly 的速度、安全性和組件模型。為了實現這一目標,我們每個人都可以發揮作用。
Wasmtime 1.0 性能概覽
近日字節碼聯盟發布了 wasmtime 1.0 性能概覽[2] 的一篇文章,為將在 9.20號發布的 wasmtime 1.0 穩定版做前期鋪墊,介紹了 wasmtime 團隊近期在編譯器和運行時中所做的工作。這里只做重點摘要,并非全文翻譯,對細節感興趣的可進一步參閱原文。
什么是性能
讓 Wasmtime 和 Cranelift 變得更快意味著什么?所謂的“快”是什么意思?
“
Cranelift 也被用于 Rust Debug 模式編譯后端
當 Wasmtime 執行 Wasm 程序時,CPU既執行從Wasm字節碼編譯的本地指令,也執行 "Wasmtime Runtime "的一部分,Wasmtime Runtime 用于維護數據結構以幫助實現Wasm語義。這兩部分的執行有兩個階段:啟動初始化(Wasm代碼的編譯,和運行時的初始化)和 穩態(steady-state)執行。這兩個層面的四個組合都對性能有一定的影響,可以分別進行優化。
Compiler (Cranelift) | Runtime (Wasmtime) | |
---|---|---|
啟動階段 | 代碼編譯時間 | Wasm 模塊實例化時間 |
穩態階段 | 生成代碼的速度 | 運行時的基本速度 |
wasmtime 對于改善這四個象限中的每一項都做了大量工作。
Wasm 模塊實例化
WebAssembly 之所以安全是因為wasm 模塊每個實例與生俱來的隔離性。為了有效地利用這種隔離性,Wasm的一些應用將每一個工作單元實例化為一個新的實例,例如服務器上每個傳入的請求。因此,極快的模塊實例化是像Wasmtime這樣的Wasm VM的一個關鍵要求。
現在 wasmtime 的模塊實例化速度已經被優化到了微秒級別。這是如何做到的呢?
虛擬內存技術
在過去,wasmtime 是通過為 wasm 應用初始化一大塊內存(通過malloc或mmap或一些其他分配器),然后將數據復制到正確的位置。
現在,是從現代計算機使用的虛擬內存技術獲得靈感,實現了一個 實例分配器[3]使用了mmap 、madvise 和寫時復制(copy-on-write)的技術將實例化的成本大大的降低了。
延遲初始化
Wasmtime運行時在開始執行已編譯的Wasm代碼之前,要花費大量時間來初始化數據結構。所以,團隊為函數引用表和它們所指向的函數閉包對象實現了延遲初始化[4]。
優化結果
SpiderMonkey.wasm 的實例化時間從大約2毫秒到5微秒,快了400倍。
運行時性能
Wasm 執行過程中的大部分 CPU 時間通常花在Wasm程序本身,或它調用的 "hostcalls"(這是Wasmtime用戶插入Wasmtime的代碼,無法直接控制),除此之外,Wasmtime本身有一些部分在某些情況下必須運行,這部分代碼就是 Wasmtime Runtime 的性能優化之處。
加速棧走查(Stack-Walking)
之前,為了讓Wasmtime列舉所有的棧幀(stackframes),Cranelift編譯器產生了所謂的 "unwind info"。這是一種元數據,描述了編譯后的代碼將在任意給定點上把值放在棧中。利用這些元數據,Wasmtime的 "unwinder"能夠逆向程序狀態:它理解一個活動函數每次調用的棧幀,最終找出誰調用了它,并在棧上迭代,直到它到達Wasm的初始入口。整個過程非常慢。
團隊對此進行了改進,確保始終保持一個幀指針的鏈表,從而達到棧走查像遍歷鏈表那么簡單。這種性能改進是一個巨大的質量改進:它允許啟用棧跟蹤,并大幅提高Wasmtime的健壯性。
加速多任務協作 與 代際(Epoch) 中斷
Wasmtime的一個常見用例是同時并發運行許多不同的 WebAssembly guests,并在它們之間設置時間片。Wasmtime內置支持在一個異步事件循環上運行對Wasm的調用。
Wasmtime 用戶在這種情況下可能遇到的一個問題是如何限制 Wasm 程序的執行時間。通常,當與事件循環異步運行時,計算密集型任務應拆分為多個段,以便事件循環不會停止超過最大“時間片”。
通過將 Wasm 字節碼標準編譯為本地機器代碼,Wasm 中的循環成為編譯代碼中的循環,并運行盡可能多的迭代,沒有限制。如果用戶從事件循環中調用此函數,則該事件循環可能會無限期停止。
因此,特別是在運行不受信任的代碼時,Wasmtime 用戶必須建立一種在一定時間限制后重新獲得控制權的方法。所以 Wasmtime 必須提供一種在某個時間點中斷 Wasm 執行的方法。
之前,實現這一行為的主要方式是通過“燃料(fuel)”。這是一種機制,通過該機制,已編譯的 Wasm 代碼增加了對“操作”進行計數的代碼,根據限制檢查當前計數,如果超出限制,則返回給調用者或事件循環。“燃料(fuel)”是一種有效的機制,但它成本很高:它需要用“計數”來擴充每一段代碼,并經常將該計數存儲到內存中并檢查它。
團隊使用了基于代際的中斷[5]取代了 “燃料(fuel)”機制,性能提升了兩倍。
“
使用 “燃料”機制還是代際中斷,是一種權衡。“燃料”機制更加精準,而代際中斷性能更好。
Cranelift 編譯代碼的質量
Cranelift 用于將Wasm字節碼編譯成計算機可以直接執行的本地機器代碼。
寄存器分配器改造:regalloc2
在過去的一年里,wasmtime引入了新的寄存器分配器 regalloc2[6]。寄存器分配器是編譯器的一個部分,它為程序中的值分配存儲位置。在真正的CPU中,指令對寄存器中的數據進行操作,寄存器是一些小的存儲位置,每個位置可以容納一個值(例如,一個64位的數字)。寄存器分配器決定在什么時候將哪些值保存在哪些寄存器中。做好這一點可以大大改善程序的性能,因為它意味著更少的數值移動。WebAssembly,作為一個抽象的、與硬件無關的虛擬機,沒有大多數指令的輸入和輸出位置的概念。
regalloc2的設計是為了支持更高級的算法,以決定如何向寄存器分配數值。當引入時,它將SpiderMonkey.wasm的運行時性能提高了約5%,將另一個CPU密集型基準測試bz2的性能提高了4%。
更好的模式管理:新的后端、ISLE和持續的調整
指令選擇問題是指選擇最佳的CPU指令來實現一個給定的程序行為。因為每個CPU都有自己獨特的指令集,而且這些指令可以以許多不同的方式組合,這是一個非常難解決的組合難題。Cranelift最初采用了一種新的編譯器后端設計,可以實現更高級的模式匹配,目前采取的方法是用模式匹配DSL(特定領域語言)來表達底層的指令,這樣我們就可以更容易地調整這些模式。
未來:中端優化
在未來,我們計劃為Cranelift引入更先進的中端優化。中端優化器 "是編譯器的一部分,在程序被 "降級"為機器特定的形式之前(也就是在指令選擇之前),以各種方式對程序進行轉換,使其更快。有一套經典的優化方法,幾乎所有的編譯器都會執行,包括簡化常數表達式(1+1變成2)等基本規則。但也有許多更復雜和微妙的轉換。
Cranelift: 編譯時優化
除了優化Cranelift生成的代碼,編譯過程本身如果太慢,那么Wasmtime可能需要很長的時間來啟動新的代碼,將會阻礙生產力(對于Wasm開發人員)和響應能力(對于訪問新應用程序的最終用戶)。因此,編譯器的速度是一個重要的指標。
廣義上講,我們可以通過在后端關鍵部分選擇更好的算法來提高編譯時間,如寄存器分配器或優化通道,或通過做一般的程序優化,如減少內存使用。
regalloc2
切換到 regalloc2 顯著改善了編譯時間,因為寄存器分配占編譯時間的很大一部分:測量單線程時間(不是并行編譯),SpiderMonkey.wasm 的構建速度提高了 6%,bz2 的構建速度提高了 10%。
中端優化器:將多個passes合并為一
算法重新設計也可以大大縮短編譯時間。在我們的中端優化器原型中,我們對編譯器設計的相關部分采取了一種新的方法:幾個不同的 "程序",或以某種方式改造程序的特定算法,被合并成一個統一的框架,只對程序進行一次處理。
標準程序優化
一種特別有效的提高速度的改變是減少內存的分配和使用。程序分配的內存越少,它的運行速度就越快,至少有兩個原因:內存分配器本身可能很慢,而且使用更多的內存也會導致更多的緩沖區未命中和內存流量。由于其多線程編譯模式,Cranelift也傾向于對分配器施加特別大的壓力。
總結
高性能是任何希望成為構建高效、持久系統的基礎的軟件的一個關鍵方面。如果 WebAssembly 想要成功,它的運行速度必須能達到與本地代碼競爭的水平。這也是 wasmtime 性能優化的終極目標。
通過該篇文章我們簡單了解了 Wasmtime 和 Cranelift 性能優化的相關工作,以及當前 wasmtime 1.0 的性能狀態(詳細數據見原文)。后續的文章將介紹該團隊如何確保 Wasmtime 安全以及編譯器生成正確的代碼。
審核編輯:劉清
-
cpu
+關注
關注
68文章
10826瀏覽量
211160 -
中斷
+關注
關注
5文章
895瀏覽量
41395 -
python
+關注
關注
56文章
4782瀏覽量
84460 -
虛擬內存
+關注
關注
0文章
70瀏覽量
8052
原文標題:WebAssembly 動態 | WebAssembly 的發展風險及Wasmtime 1.0 性能概覽
文章出處:【微信號:Rust語言中文社區,微信公眾號:Rust語言中文社區】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論