原文鏈接: https://github.com/chenshenhai/blog/issues/38
前段時間開發影像處理工具 Pictool [1] 后,遇到影像處理的高頻的計算瓶頸,在尋找高頻計算的前端能力解決方案程序中,入門學習了一下 AssemblyScript [2] 在前端中的應用,
入門的程序中踩了不少坑,例如使用 AssemblyScript [2] 開發 wasm 時候,發現 npm 包 assemblyscript 已經不維護了,需要自己人工添加成從 Github 倉庫參考 assemblyscript 的 npm 模塊,
與此同時,網上很多教程說明已經有點 “過時” 了,大部分按照教程步驟后實作的代碼都運行不起來,最后參考原有網上的教程,一步步踩坑,實作了demo,同時也寫下這篇文章作為筆記!

圖片來源自網路
什么是WebAssembly [2]
- 計算機是不能直接識別運行高級語言(C/C++, Java, JavaScript等),
- 計算機能讀懂是0和1的電子元件信號,對應到運行的機器碼,
- 在前端瀏覽器領域里,JS是解釋執行,也就是運行到哪就解釋成機器碼讓計算機讀懂并執行,在高頻計算性能上有一定的瓶頸,
- WebAssembly 位元組碼是接近計算機能識別的機器碼,只要運行環境有對應的虛擬機,能快速加載運行,
WebAssembly 優勢
在前端主要的優勢有
- 體積小
- 加載快
- 兼容強
WebAssembly 前端能力現狀
- Node.js 目前已經支持了 WebAssembly
- 大部分主流瀏覽器廠商也支持了 WebAssembly

- 什么是 AssemblyScript
- AssemblyScript 是 TypeScript 的一個子集 ,
- 可以用 TypeScript 語法撰寫功能,然后編譯成 wasm,對前端來說比較友好,
快速上手 AssemblyScript 撰寫 wasm
注:如想更快速嘗試,可以直接去該 demo 倉庫獲取原始碼使用, github.com/chenshenhai/assemblyscript-demo
1、安裝 AssemblyScript
由于 AssemblyScript 的 npm 官方模塊已經停止維護,所以 AssemblyScript 的模塊需要從 Github 來源安裝,

在 package.json 的依賴加入 AssemblyScript 模塊的 Github 來源
{ // ... "devDependencies": { "assemblyscript": "github:assemblyscript/assemblyscript" // ... }}
再執行 npm install 從 Github 下載該模塊到本地 node_module 中
2、撰寫功能代碼
撰寫一個 斐波那契數列 函式
在 demo 的目錄 ./src/index.ts 中
export function fib(num: i32): i32 { if (num === 1 || num === 2) { return 1; } else { return fib(num - 1) + fib(num - 2) }}
3、編譯成wasm
在 package.json 撰寫編譯腳本
{ // ... "scripts": { "build": "npm run build:untouched && npm run build:optimized", "build:untouched": "./node_modules/assemblyscript/bin/asc src/index.ts -t dist/module.untouched.wat -b dist/module.untouched.wasm --validate --sourceMap --measure", "build:optimized": "./node_modules/assemblyscript/bin/asc src/index.ts -t dist/module.optimized.wat -b dist/module.optimized.wasm --validate --sourceMap --measure --optimize" // ... },}
在專案根目錄開始執行編譯
npm run build
編譯后會在 ./dist/ 目錄下產生編譯后的幾種 wasm 檔案格式
├── dist│ ├── module.optimized.wasm│ ├── module.optimized.wasm.map│ ├── module.optimized.wat│ ├── module.untouched.wasm│ ├── module.untouched.wasm.map│ └── module.untouched.wat
4、Node.js使用
在 ./example/node/module.js 檔案中,封裝 wasm 的 CommonJS使用模塊,
const fs = require('fs');const path = require('path');
const wasmFile = fs.readFileSync(path.join(__dirname, '..', '..', './dist/module.optimized.wasm'))
const wasm = new WebAssembly.Module(wasmFile, {});
module.exports = new WebAssembly.Instance(wasm, { env: { memoryBase: 0, tableBase: 0, memory: new WebAssembly.Memory({ initial: 256, maximum: 512, }), table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc', }), abort: console.log, },}).exports;
Node.js 使用 wasm 的封裝模塊
const mod = require('./module');
const result = mod.fib(40);console.log(result);
執行結果會出現
> 102334155
3、瀏覽器使用
在 ./example/browser/ 目錄下部署瀏覽器訪問的服務
├── dist│ ├── module.optimized.wasm│ └── module.untouched.wasm├── example│ ├── browser│ │ ├── demo.js│ │ ├── index.html│ │ └── server.js
臨時瀏覽器可訪問的服務,這里用 koa 來搭建服務,
具體實作在 ./example/browser/server.js 檔案中
const Koa = require('koa')const path = require('path')const static = require('koa-static')
const app = new Koa()
const staticPath = './../../'
app.use(static( path.join( __dirname, staticPath)))
app.listen(3000, () => { console.log('[INFO]: server starting at port 3000'); console.log('open: <http://127.0.0.1:3000/example/browser/index.html>')})
瀏覽器使用 wasm 模塊
具體實作在 ./example/browser/demo.js 檔案中實作
fetch('/dist/module.optimized.wasm') .then(res => res.arrayBuffer()) .then((wasm) => { return new WebAssembly.instantiate(wasm, { env: { memoryBase: 0, tableBase: 0, memory: new WebAssembly.Memory({ initial: 256, maximum: 512, }), table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc', }), abort: console.log, }, }) }).then(mod => { const result = mod.instance.exports.fib(40); console.log(result) });
啟動服務,訪問頁面后也是顯示了結果
> 102334155
對比性能測驗
1、Node.js 環境對比wasm和原生js
測驗代碼
const mod = require('./module');
const start = Date.now();mod.fib(40)// 列印 Node.js 環境下 wasm 計算 斐波那契數列 引數為40 的耗時結果console.log(`nodejs-wasm time consume: ${Date.now() - start} ms`)
// 原生Node.js實作的 斐波那契數列 函式function pureFib(num) { if (num === 1 || num === 2) { return 1; } else { return pureFib(num - 1) + pureFib(num - 2) }}
const startPure = Date.now()pureFib(40);// 列印 Nodejs環境下 原生js 計算 斐波那契數列 引數為40 的耗時結果console.log(`nodejs-js time consume: ${Date.now() - startPure} ms`)
測驗結果
Node.js環境下,原生js 執行耗時 833 ms
Node.js環境下,wasm 執行耗時 597 ms
對比下來,在Node.js 環境中, wasm 計算 斐波那契數列 比 原生js 執行快了接近 30%
2、瀏覽器環境對比wasm和原生js
瀏覽器測驗代碼
const $body = document.querySelector('body');
fetch('/dist/module.optimized.wasm') .then(res => res.arrayBuffer()) .then((wasm) => { return new WebAssembly.instantiate(wasm, { env: { memoryBase: 0, tableBase: 0, memory: new WebAssembly.Memory({ initial: 256, maximum: 512, }), table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc', }), abort: console.log, }, }) }).then(mod => {
const start = Date.now(); mod.instance.exports.fib(40); const logWasm = `browser-wasm time consume: ${Date.now() - start} ms`; $body.innerHTML = $body.innerHTML + `<p>${logWasm}</p>` // 列印 瀏覽器環境下 wasm 計算 斐波那契數列 引數為40 的耗時結果 console.log(logWasm) });
// 列印 瀏覽器環境下 原生js 計算 斐波那契數列 引數為40 的耗時結果 function pureFib(num) { if (num === 1 || num === 2) { return 1; } else { return pureFib(num - 1) + pureFib(num - 2) } } const startPure = Date.now() pureFib(40); const logPure = `browser-js time consume: ${Date.now() - startPure} ms`; $body.innerHTML = $body.innerHTML + `<p>${logPure}</p>` console.log(logPure);
測驗結果

Chrome環境下,原生js 執行耗時 884 ms
Chrome環境下,wasm 執行耗時 612 ms
對比下來,在Chrome瀏覽器中 wasm 計算 斐波那契數列 比 原生js 執行快了也是接近 30%
從上述 Node.js 和 Chrome 環境下運行 wasm 和 原生js 的對比中,wasm的在高頻計算的場景下,耗時的確是比原生js低,同時都是接近 30% 的計算性能提升,
參考資料
[1] Pictool -用TypeScript寫了個低配版H5美圖工具 : https://github.com/chenshenhai/blog/issues/37
[2] IBM開發者檔案: WebAssembly 現狀與實戰 : https://www.ibm.com/developerworks/cn/web/wa-lo-webassembly-status-and-reality/index.html
[3] 奇舞周刊: 20分鐘上手 webAssembly: https://juejin.im/post/5b7a3f3cf265da43606e9109
[4] WebAssembly.LinkError()報錯 : https://cunzaizhuyi.github.io/WebAssembly-LinkError/
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/250698.html
標籤:其他
上一篇:初識HTML5
下一篇:10分鐘學會QQ開放登錄
