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的鏈接自己體驗一下,大家注意上網的方式。