0%

WebAssembly

原生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

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

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
// 1.调用index导出的js

let myModule = require('out/index.js')
myModule.onRuntimeInitialized = function () {
const start = Date.now();
// console.log(myModule.ccall("fib", 'number', ['number'], [45], null))
console.log(myModule._fib(45))
console.log(`cost=${Date.now() - start}`)
}

// 2.不使用index.js 直接加载wasm
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

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

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}); //1页内存为64kb
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
android js code
image.png

代码仓库: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