原生js
1 2 3 4 5 6 7 8
| function fib(n) { if (n <= 0) return 0; if (n <= 2) return 1; return fib(n - 2) + fib(n - 1); } const start = Date.now(); console.log(fib(45)); console.log(`cost=${Date.now() - start}`)
|
耗时:7421ms
4种wasm编译
AssemblyScript
参考:https://www.assemblyscript.org/getting-started.html#setting-up-a-new-project
index.ts
1 2 3 4 5
| export function fib(n: i32): i32 { if (n <= 0) return 0; if (n <= 2) return 1; return fib(n - 2) + fib(n - 1); }
|
1 2 3 4
| import { fib } from "../build/debug.js"; const start = Date.now() console.log(fib(45)) console.log(`cost = ${Date.now() - start}`)
|
耗时:5337ms (包括js相互通信时间)
C/C++
1 2 3 4 5 6 7
| extern "C" { int fib(int n) { if (n <= 0) return 0; if (n <= 2) return 1; return fib(n - 2) + fib(n - 1); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
let myModule = require('out/index.js') myModule.onRuntimeInitialized = function () { const start = Date.now(); console.log(myModule._fib(45)) console.log(`cost=${Date.now() - start}`) }
const path = require('path') const fs = require('fs')
const filePath = path.resolve(__dirname, 'out/index.wasm') const buffer = fs.readFileSync(filePath) WebAssembly.instantiate(buffer).then((results) => { const start = Date.now(); console.log(results.instance.exports.fib(45)) console.log(`cost=${Date.now() - start}`) });
|
耗时:5029ms (包括js相互通信时间)
Rust
1 2 3 4 5 6 7 8 9 10
| extern crate wasm_bindgen;
use wasm_bindgen::prelude::*;
#[wasm_bindgen] pub fn fib(n: i32) -> i32 { if n <= 0 { return 0; } if n <= 2 { return 1; } return fib(n - 2) + fib(n - 1); }
|
1 2 3 4 5 6 7 8 9
| const api = import('./pkg/index.js')
api.then(api => { const start = Date.now() console.log(api.fib(45)) console.log(`cost=${Date.now() - start}`) }).catch((e)=>{ console.log(e) })
|
耗时:8858ms 目前rust只能是web页面的形式使用,nodejs不可以使用,后续再看下问题
wat汇编
难度比较大,简单的wat可以实现;使用wat2wasm工具,将wat转为wasm
wat字节码
s-表达式
(module (func (param) (result) (locals) (body) ))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| (module (import "console" "log" (func $log (param i32))) ;;导入js (func (export "add") (param $lhs i32) (param $rhs i32) (result i32) ;;导出func local.get $lhs ;;读取第一个参数$lhs local.get $rhs ;;读取第二个参数$rhs i32.add) ;;上面两个数据出栈,相加值入栈 (func $getAnswer (result i32) ;; getAnswer方法 i32.const 20) ;; 入栈20, (func (export "getAnswerPlus1") (result i32) call $getAnswer i32.const 1 i32.add)
(func (export "log") i32.const 123 call $log) ;;调用js中的log )
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| const path = require('path') const fs = require('fs')
const imports = { console: { log: function log(arg) { console.log(arg) } } }
const filePath = path.resolve(__dirname, 'demo.wasm') const buffer = fs.readFileSync(filePath) WebAssembly.instantiate(buffer,imports).then((results) => { console.log(results.instance.exports.add(2, 2)) console.log(results.instance.exports.getAnswerPlus1()) console.log(results.instance.exports.log()) })
|
Memory wat
1 2 3 4 5 6 7 8
| (module (import "console" "log" (func $log (param i32 i32))) (import "js" "mem" (memory 1)) (data (i32.const 0) "Hi") (func (export "writeHi") i32.const 0 ;; offset,从0开始读取,这个设置log的第一个参数为1 i32.const 2 ;; length ,因为Hi字符串的长度为2,这里设置log的第二个参数为2 call $log))
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var memory = new WebAssembly.Memory({initial: 1}); var importObj = { console: { log: function consoleLogString(offset, length) { var bytes = new Uint8Array(memory.buffer, offset, length); var string = new TextDecoder('utf8').decode(bytes); console.log(string); } }, js: { mem: memory } };
const filePath = path.resolve(__dirname, 'memory.wasm') const buffer = fs.readFileSync(filePath) WebAssembly.instantiate(buffer,importObj).then((results) => { results.instance.exports.writeHi(); });
|
Table wat
导出表格,直接访问
1 2 3 4 5 6 7 8 9
| (module (table $table 2 funcref) (elem (i32.const 0) $f1 $f2) (func $f1 (result i32) i32.const 42) (func $f2 (result i32) i32.const 13) (export "tbl" (table $table)) ;; 导出表格 )
|
1 2 3 4 5
| const filePath = path.resolve(__dirname, 'table.wasm') const buffer = fs.readFileSync(filePath) WebAssembly.instantiate(buffer).then((results) => { console.log(results.instance.exports.tbl.get(0)()) });
|
通过函数访问表格
1 2 3 4 5 6 7 8 9 10 11 12 13
| (module (table 2 funcref) (func $f1 (result i32) i32.const 42) (func $f2 (result i32) i32.const 13) (elem (i32.const 0) $f1 $f2) (type $return_i32 (func (result i32))) (func (export "callByIndex") (param $i i32) (result i32) ;;导出函数,通过index来调用表格 local.get $i call_indirect (type $return_i32) ) )
|
1 2 3 4 5 6
| const filePath = path.resolve(__dirname, 'table2.wasm') const buffer = fs.readFileSync(filePath) WebAssembly.instantiate(buffer).then((results) => { console.log(results.instance.exports.callByIndex(0)) console.log(results.instance.exports.callByIndex(1)) });
|
- wasm 只支持4种数据类型传递:i32,i64,f32,f64;其他数据类型使用memory传递
- 一个内存和表格可以被多个模块实例使用;js中定义内存和表格,初始化多个模块时传入同一个内存和表格,这样多个模块可以相关通信;相当于动态链接
Android端 V8 WebAssembly
c++
android js code
代码仓库:https://code.alibaba-inc.com/amap_mini/webassembly-demo
参考:
https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html?highlight=exported_functions#calling-compiled-c-functions-from-javascript-using-ccall-cwrap
https://developer.mozilla.org/zh-CN/docs/WebAssembly