原生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
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677588774121-ace1cf92-7dbc-4d64-9c64-d023e6833b44.png#clientId=uc0455bfb-2a09-4&from=paste&height=71&id=u646df697&originHeight=142&originWidth=1576&originalType=binary&ratio=2&rotation=0&showTitle=false&size=64140&status=done&style=none&taskId=u8df45586-1df9-4c49-9d7f-bc3740a5f42&title=&width=788)
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相互通信时间)
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677577435597-adcfc528-0afb-4ed7-a028-931b7b035aba.png#clientId=uc0455bfb-2a09-4&from=paste&height=109&id=u6fef80f6&originHeight=218&originWidth=1554&originalType=binary&ratio=2&rotation=0&showTitle=false&size=88831&status=done&style=none&taskId=udf48ceed-f0d0-42f9-944a-126656cc8bb&title=&width=777)
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相互通信时间)
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677583329406-9c3cc00c-8c55-467c-b624-b418c1ff6691.png#clientId=uc0455bfb-2a09-4&from=paste&height=121&id=ub772a16f&originHeight=242&originWidth=1740&originalType=binary&ratio=2&rotation=0&showTitle=false&size=103057&status=done&style=none&taskId=ub26861ff-d7f6-4157-8f6b-7cc5a5819fd&title=&width=870)
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不可以使用,后续再看下问题
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677591803862-9ab66b05-ab72-4514-a593-2408fae3871e.png#clientId=uc0455bfb-2a09-4&from=paste&height=61&id=u72077d84&originHeight=122&originWidth=614&originalType=binary&ratio=2&rotation=0&showTitle=false&size=28010&status=done&style=none&taskId=u99a80709-e7f5-4d00-9635-3e4129d2cd2&title=&width=307)
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++
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677665923514-36bd6e6e-4dce-46d9-b7a5-f7023d787d63.png#clientId=u7a1735ec-82eb-4&from=paste&height=283&id=u0f867472&originHeight=566&originWidth=902&originalType=binary&ratio=2&rotation=0&showTitle=false&size=139630&status=done&style=none&taskId=u02e1d0e5-d024-433d-bee2-a913e72a32c&title=&width=451)
android js code
![image.png](https://intranetproxy.alipay.com/skylark/lark/0/2023/png/15256694/1677665960379-d4e5256a-a256-492c-b1d9-5fd538becaae.png#clientId=u7a1735ec-82eb-4&from=paste&height=445&id=u7c4e76b2&originHeight=890&originWidth=2044&originalType=binary&ratio=2&rotation=0&showTitle=false&size=496120&status=done&style=none&taskId=ud653cd7f-44ba-4791-8374-38105a4ab76&title=&width=1022)
代码仓库: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