WebAssembly
是最近十年 web 技術發展中最重大的一個新技術。很多人可能都聽說過它最重要的一個特性:效能好,執行快。那WebAssembly
究竟是什麼?是什麼使得它效能好執行快的呢?
WebAssembly是什麼?
WebAssembly 是一種能把除了JavaScript以外的程式語言編寫的程式碼經過編譯器編譯轉換為能在現代瀏覽器中執行的程式碼的技術。眾所周知,JavaScript
在 Web 中的地位一直獨步天下,無 yan 能敵,所以WebAssembly
所指的效能優勢,是針對 JavaScript 而言的。WebAssembly
並不是為了替代 JavaScript 出現的,而是希望與 JavaScript 並駕齊驅共同開發出效能更高的應用。
首先看看 JavaScript 的效能歷史
JavaScript
誕生於1995年,目的是為了給瀏覽器 HTML 網頁增加動態互動功能,並沒有考慮太多效能問題,事實證明在前十年裡瀏覽器也不需要它有多快。一切變化發生在2005年,谷歌在多款互動應用中使用Ajax
技術讓互動體驗得到了極大的提升,讓人們認識到了原來網頁能做的事情遠遠不是內容的展示和表單的提交。
到2008年,JavaScript
在瀏覽器中低下的執行效率,已經成為限制程式猿們在網頁施展拳腳的一大阻礙。突然,有個叫谷歌的廠商推出了一款叫Chrome
的瀏覽器,它與以往瀏覽器最大的不同在於內建了一個 JITs(just-in-time compilers)
,一個能在 js 程式碼執行時根據某些模式動態編譯程式碼為能在瀏覽器中更高效執行的程式碼的技術,詳細介紹可以看這篇文章: a crash course in just in time(jit) compilers。從此拉開了瀏覽器效能大戰的序幕。
時間再推進10年到2018年,此時JavaScript
的使用場景已經遠遠超過了原先的想象:服務端、網頁遊戲、WebVR/AR、圖片/視訊處理等等場景,JITs 都已經不能滿足這些對效能日益嚴苛的場景了,此時我們需要更進一步的突破,這個就是 WebAssembly。
為什麼WebAssembly更快?
就如第一部分所提,WebAssembly
的效能優勢是針對 JavaScript 而言的,下面我們分別從 JavaScript 和 WebAssembly 的執行過程一一對比優勢到底在哪:
獲取
由於 WebAssembly 是由編譯器生產出來的,並且將被瀏覽器直接解析執行,可以節省那些為了給人類閱讀而新增的不必要程式碼,從而可以做到檔案大小甚至比經過壓縮的 JS 程式碼更小。所以在相同的網路情況下,從伺服器獲取一個 WB 檔案會比獲取一個 JS 檔案 更快。
解析
當 JS 檔案成功到達瀏覽器之後,瀏覽器會將其解析成一棵抽象語法樹
(Abstract Syntax Tree)(但是隻會先解析當前需要執行的那部分程式碼,而其餘未執行的函式將會儲存成存根),然後再轉換為 JS 引擎識別的 IR(intermediate representation) 層位元組碼(認識 JAVA 的應該對這個詞不陌生)。
反過來我們看 WebAssembly 本身已經是經過高階語言編譯出來的 IR 層程式碼了,不需要在瀏覽器端進行解析而只需要把經過壓縮的內容解碼出來,節省了相當多的時間。
編譯和優化
這個階段是 JITs
負責做的事情,不同瀏覽器對 WebAssembly 的處理可能會有細微差別,我們以都使用 JITs
進行優化的場景來看看為何 WebAssembly 會比 JavaScript 更快,有以下三點:
(閱讀下面內容需要先對 JIT 有一定認識,不清楚的可以先看 這裡
- 由於 WebAssembly 的輸入型別是固定的(byte),所以不需要通過執行程式碼這種方式去檢查輸入型別來進行編譯優化;
- 在 JavaScript 中相同一段程式碼可能因為輸入值不同需要分別編譯成不同的版本,而 WebAssembly 也不需要進行這種冗餘的操作,原因如上;
3、 WebAssembly 在從高階語言(C/C++/Rust)編譯而來的時候,已經經過編譯器優化一次了,所以在
JITs
中需要做的事情更少;
重優化
還是由於 JavaScript 動態型別的原因,一段經過了深度優化的程式碼,可能因為這次執行的時候輸入值型別變了,導致 JITs
需要根據輸入值型別重新進行一次上一步的優化工作,這也需要花費一定的時間。
而 WebAssembly 輸入值固定,JITs
不需要在每次程式碼執行時去計算輸入值的型別,從而不會發生重優化這樣的事情。
執行
JavaScript 程式碼一般是人寫的,而 WebAssembly 是由編譯器編譯出來的,是直接針對機器產生的程式碼,會包含更多對機器效能優異的指令(instructions),這部分差異針對不同的功能程式碼 WebAssembly 可能會比 JavaScript 快 10%~800%。
垃圾回收
我們都知道在 JavaScript 中不必人工去執行變數的釋放和記憶體的回收,因為 JS 引擎有自動垃圾回收功能,能自行判斷該回收什麼東西甚至足夠智慧知道在何時進行回收操作。但是這還是存在天花板可能會影響程式碼的執行。
在目前為止,WebAssembly 都不支援自動垃圾回收,記憶體由程式碼手動管理(由於使用了 C/C++編寫),這將會加大開發者編碼的難度,但能保證程式碼效能更可控。
總結
總的來說,大多數場景下 WebAssembly 比 JavaScript 效能更好是因為:
- WebAssembly 程式碼更小的體積;
- 解碼 WebAssembly 比解析轉譯 JavaScript 用的時間更少;
- 優化 WebAssembly 的用時比優化 JavaScript 的更短,因為前者是已經經過一次編譯優化並且面向機器的程式碼;
- WebAssembly 沒有重優化這個過程;
- WebAssembly 包含對機器更友好的指令;
- JavaScript 無法人為控制垃圾回收,而 WebAssembly 可以有效控制記憶體回收的時機;
=============
本文主要知識和靈感來源於:
- A cartoon intro to WebAssembly系列文章,感謝作者。後續本部落格會翻譯幾篇此作者編寫的 WebAssembly 使用教程。
- WebAssembly Concepts
- WebAssembly format
版權宣告:原創文章,如需轉載,請註明出處“本文首發於xlaoyu.info”