WebAssembly的初步了解
- 2019 年 10 月 4 日
- 筆記
技術背景
2017年2月28日,四大瀏覽器,IE,FF,Chrome,Safari宣布達成共識,即 WebAssembly 的 MVP (最小化可行產品)已經完成。大約一周後,Firefox會默認打開 WebAssembly 支援,而Chrome則在第二周開始。它也可用於預覽版本的Edge和Safari。如今過去兩年多了,發展還是挺迅速的。
使用WebAssembly,可以更快地在 web 應用上運行程式碼。這裡有 幾個 WebAssembly 程式碼運行速度比 JavaScript 高效的原因。
文件載入 – WebAssembly 文件體積更小,所以下載速度更快。
解析 – 解碼 WebAssembly 比解析 JavaScript 要快
編譯和優化 – 編譯和優化所需的時間較少,因為在將文件推送到伺服器之前已經進行了更多優化,JavaScript 需要為動態類型多次編譯程式碼
重新優化 – WebAssembly 程式碼不需要重新優化,因為編譯器有足夠的資訊可以在第一次運行時獲得正確的程式碼
執行 – 執行可以更快,WebAssembly 指令更接近機器碼
垃圾回收 – 目前 WebAssembly 不直接支援垃圾回收,垃圾回收都是手動控制的,所以比自動垃圾回收效率更高。
目前瀏覽器中的 MVP(最小化可行產品) 已經很快了。在接下來的幾年裡,隨著瀏覽器的發展和新功能的增加,它將在未來幾年內變得更快。沒有人可以肯定地說,這些性能改進可以實現什麼樣的應用。但是,如果過去有任何跡象,我們可以期待驚奇。
什麼時候使用WebAssembly?
說了這麼多,我到底什麼時候該使用它呢?總結下來,大部分情況分兩個點。
- 對性能有很高要求的App/Module/遊戲
- 在Web中使用C/C++/Rust/Go的庫 舉個簡單的例子。如果你要實現的Web版本的Ins或者Facebook, 你想要提高效率。那麼就可以把其中對圖片進行壓縮、解壓縮、處理的工具,用C++實現,然後再編譯回WebAssembly。
WebAssembly的幾個開發工具
- AssemblyScript。支援直接將TypeScript編譯成WebAssembly。這對於很多前端同學來說,入門的門檻還是很低的。
- Emscripten。可以說是WebAssembly的靈魂工具不為過,上面說了很多編譯,這個就是那個編譯器。將其他的高級語言,編譯成WebAssembly。
- WABT。是個將WebAssembly在位元組碼和文本格式相互轉換的一個工具,方便開發者去理解這個wasm到底是在做什麼事。
WebAssembly的意義
在我的個人理解上,WebAssembly並沒有要替代JavaScript,一統天下的意思。我總結下來就兩個點。
- 給了Web更好的性能
- 給了Web更多的可能 關於WebAssembly的性能問題,之前也花了很大的篇幅講過了。而更多的可能,隨著WebAssembly的技術越來越成熟,勢必會有更多的應用,從Desktop被搬到Web上,這會使本來已經十分強大的Web更加豐富和強大。
WebAssembly實操
要進行這個實際操作,你需要安裝上文提到過的編譯器Emscripten,然後按照這個步驟去安裝。以下的步驟都默認為你已經安裝了Emscripten。
WebAssembly在Node中的應用
導入Emscripten環境變數
進入到你的emscripten安裝目錄,執行以下程式碼。
source emsdk/emsdk_env.sh
新建C文件
用C實現一個求和文件test.c
,如下。
int add(int a, int b) { return a + b; }
使用Emscripten編譯C文件
在同樣的目錄下執行如下程式碼。
emcc test.c -Os -s WASM=1 -s SIDE_MODULE=1 -o test.wasm
emcc
就是Emscripten編譯器,test.c
是我們的輸入文件,-Os
表示這次編譯需要優化,-s WASM=1
表示輸出wasm的文件,因為默認的是輸出asm.js,-s SIDE_MODULE=1
表示就只要這一個模組,不要給我其他亂七八糟的程式碼,-o test.wasm
是我們的輸出文件。
編譯成功之後,當前目錄下就會生成test.wasm
。
編寫在Node中調用的程式碼
新建一個js文件test.js
。程式碼如下。
const fs = require('fs'); let src = new Uint8Array(fs.readFileSync('./test.wasm')); const env = { memoryBase: 0, tableBase: 0, memory: new WebAssembly.Memory({ initial: 256 }), table: new WebAssembly.Table({ initial: 2, element: 'anyfunc' }), abort: () => {throw 'abort';} } WebAssembly.instantiate(src, {env: env}) .then(result => { console.log(result.instance.exports._add(20, 89)); }) .catch(e => console.log(e));
執行test.js
運行以下程式碼。
node test.js
然後就可以看到輸出的結果109了。
WebAssembly在React當中的應用
通過fetch的方法調用
直接用fetch的方式。大概的調用方式如下。
const fibonacciUrl = './fibonacci.wasm'; const {_fibonacci} = await this.getExportFunction(fibonacciUrl);
而getExportFunction
具體程式碼如下。
getExportFunction = async (url) => { const env = { memoryBase: 0, tableBase: 0, memory: new WebAssembly.Memory({ initial: 256 }), table: new WebAssembly.Table({ initial: 2, element: 'anyfunc' }) }; const instance = await fetch(url).then((response) => { return response.arrayBuffer(); }).then((bytes) => { return WebAssembly.instantiate(bytes, {env: env}) }).then((instance) => { return instance.instance.exports; }); return instance; };
通過import C文件來調用
先通過Import的方式來引進依賴。
import wasmC from './add.c';
然後進行調用。具體的方式如下。
wasmC({ 'global': {}, 'env': { 'memoryBase': 0, 'tableBase': 0, 'memory': new WebAssembly.Memory({initial: 256}), 'table': new WebAssembly.Table({initial: 0, element: 'anyfunc'}) } }).then(result => { const exports = result.instance.exports; const add = exports._add; const fibonacci = exports._fibonacci; console.log('C return value was', add(2, 5643)); console.log('Fibonacci', fibonacci(2)); });
WebAssembly在大型項目中的應用
在這裡能夠舉的例子還是很多,比如AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack等等。拿其中幾個來簡單說一下。
AutoCAD
這是一個用於畫圖的軟體,在很長的一段時間是沒有Web的版本的,原因有兩個,其一,是Web的性能的確不能滿足他們的需求。其二,在WebAssembly沒有面世之前,AutoCAD是用C++實現的,要將其搬到Web上,就意味著要重寫他們所有的程式碼,這代價十分的巨大。
而在WebAssembly面世之後,AutoCAD得以利用編譯器,將其沉澱了30多年的程式碼直接編譯成WebAssembly,同時性能基於之前的普通Web應用得到了很大的提升。正是這些原因,得以讓AutoCAD將其應用從Desktop搬到Web中。
Google Earth
Google Earth也就是Google地球,因為需要展示很多3D的影像,對性能要求十分高,所以採取了一些Native的技術。最初的時候就連Google Chrome瀏覽器都不支援Web的版本,需要單獨下載Google Earth的Destop應用。而在WebAssembly之後呢,Google地球推出了Web的版本。而據說下一個可以運行Google地球的瀏覽器是FireFox。
Unity和Unreal遊戲引擎
這裡給兩個YouTube的鏈接自己體驗一下,大家注意上網的方式。