背景
由於js的靈活語法特定, 比如對象內的成員有很多種可能的類型, 導致JIT的優化效果變差.
如下圖所示代碼, 這裏的sum 和a的每一個元素裏面每一次計算前後,
從什麼類型變成什麼類型, 對於編譯器來說,
難以一次確定下來, 於是JIT方式就無法在這裏起到很好的優化效果.
Asm.js
2012年,Mozilla 的工程師 Alon Zakai 在研究 LLVM 編譯器時產生的想法,
專門做了一個編譯器項目 Emscripten。這個編譯器可以將 C / C++ 代碼編譯成 JS 代碼,
但不是普通的 JS,而是一種叫做 asm.js 的 JavaScript 變體
Javascript 去掉動態類型和垃圾回收之後, 就可以像C++一樣快速生成機器碼.
其實最大的瓶頸是動態類型, 垃圾回收去掉之後, 代碼的穩定性會下降
asm.js 只提供兩種數據類型, 32位整形, 64位浮點
data | 0表示整數,+data 表示浮點數
asm.js 在瀏覽器裏的運行速度相比JS 提升了幾倍.
Asm.js對語法進行了限制, 比如只准使用特定的集中元類型, 整形, 浮點型等.
asm.js對靜態類型的問題做的再好,它始終逃不過要解釋編譯的過程,
而這兩步是JavaScript代碼在引擎執行過程當中消耗時間最多的兩步.
支持更多語言
WebAssembly在Asm.js之後. 提出了更完善的方案.
是可以使用更多類型的語言編寫代碼, 編譯生成wasm在瀏覽器上運行
支持的語言包括C, Rust, Go, Typescript, C#等.
先生成IR 中間碼, 中間碼是一種很原始的語言叫lisp, 在60年提出, 比C語言的誕生還早13年.
然後從IR再生成.wasm, 其實我們在使用命令編譯時, 是沒有呈現IR這一個環節的.
當瀏覽器加載wasm之後, 就會生成特定硬件環境下的, x86/x64或arm機器碼.
這裏不直接給機器碼, 是考慮瀏覽器是跨平臺運行的.
Build wasm
Asm.js 對比 Webassembly
Benchmark測試了一個hash算法, js和wasm在相同計算量的情況下, 性能差別可達幾十倍.
Asm.js比js快幾倍, Webassembly 又比asm.js快一倍, 或者更多.
wast/wat
wasm速度快, 文件小, 但是是二進制文件, 所以不便於閱讀.
所以官方提供了一種wast的文本格式, 方便閱讀和調試.
Wast是文本形式, 如果能習慣這種語法, 可以直接編寫.
然後通過工具wast2wasm可以編譯生成wasm.
Vscode也能找到了插件來以wast的語言形態, 直接瀏覽wasm文件.
AssemblyScript init and build
Assemblyscript可以讓我們基於typescript語言開發
Webassembly的模塊.
由於我們比較熟悉這種語言,
所以我選擇從這個角度來進行講解和演示.
這個環境搭建起來比官方推薦的emscripten方便多了.
AssemblyScript sample
編寫一個簡單的index.ts.
通過命令編譯生成wasm和wat
然後就可以在頁面上加載執行了.
這是最簡單的演示, 真實的情況, 還有內存創建和傳入,
傳參, 傳回調等.
這裏語法上, ts 限制使用從i8~64, u8~u64, f32~f64, bool等類型,
其實就跟native語言差不多了, 比如bool只有一個bit, Array, String等等
What the Webassembly can do?
除此之外, AI, 加密解密的計算也可以通過webassembly來優化的.
Problems
Try.net and Blazor
瀏覽器限制直接加載本地dll文件的.
有一種古老的技術 COM, 可以將dll包裝成可被其它語言調用的文件,
但是隻在windows, IE上兼容, 太侷限, 不安全, 難用, 後來隨着IE的份額滑落, 漸漸沒人用了.
微軟基於webassembly技術, 推出Try.net 和Blazor 在網頁上的提供C#.net運行的能力.
實現的方法是依託wasm的native運行能力, 遠程加載.net的dll文件, 構建運行環境.
Try.net and Blazor
有了webassembly, 網頁上要下載運行一個操作系統也是沒問題的了.
我打開了這個頁面, 可一直在下載, 下了幾分鐘還只有12M, 目標文件有47M,
而且卡死在這個文件上, 後面還有沒有文件還不知道, 我就懶得運行它了.