Mahiru's Humble Abode       博  客   时 间 线   归  档   关 于 与 友 链



WASM + Rust + WebWorker 实现计算器(
Published on Fri Apr 08 2022 23:00:00 GMT+0000

WASM + Rust + WebWorker 实现高性能计算器

假设我们现在需要执行一个 CPU 负载很大的运算。为了不阻塞主线程,我们希望用 Web Worker 将其放到一个新的线程中进行运算,这样就可以避免阻塞主线程。

而为了提高这个运算的性能,我们希望用 Rust 编写的 WASM 程序来运算。

本文就将实现在 Web Worker 中调用 WASM 进行运算。

首先,我们来编写一段 Rust 代码,用于完成实际的计算。

1
2
3
4
5
6
7
8
9
10
11
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
extern "C" {}

#[wasm_bindgen]
pub fn wasm_add(a: i32, b: i32) -> i32 {
a + b
}

然后,将其编译为 WASM ,具体的方法参见我的上一篇博文。

接下来,我们需要创建一个网页,用于测试我们的函数。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>TestWoker</title>
</head>
<body>
<script type="module" src="script.js"></script>
</body>
</html>

script.js里面,我们要创建一个 Web Worker。

1
2
3
4
5
6
7
8
9
10
11
12
13
if (window.Worker) {
//加载worker,注意第二个参数要有,否则无法导入 module
const myWorker = new Worker('./worker.js', {type: 'module'});
myWorker.postMessage([1, 2]);//发送消息
myWorker.postMessage([3, 4]);//发送消息
setTimeout(() => {
myWorker.postMessage([7, 8])
}, 1000); //1秒后发送消息,用于在编译完成后使 worker 收到消息。
myWorker.onmessage = (m) => {
const data = m.data;
console.log('data from wasm: ' + data); //拿到 wasm 计算的结果
}
}

然后在 Web Worker 里面,导入并编译 WASM 程序,在收到消息的时候执行计算,并将结果返回给主线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import init from './pkg/hello_wasm.js';
import {wasm_add} from './pkg/hello_wasm.js';

let isWasmInit = false; //判断是否完成wasm编译

init().then(() => isWasmInit = true); //初始化Wasm(编译)

onmessage = function (m) {
const data = m.data;
if (isWasmInit) { //在接收到 message 时,wasm 已经完成编译。
console.log('Wasm init before message');
run();
} else { //在接收到 message 时,wasm 没有完成编译,所以先编译再执行。
console.log('Wasm not init before message');
init().then(() => {
run();
isWasmInit = true;
}
);
}

function run() {
const res = wasm_add(data[0], data[1]); //调用具体的 wasm 函数
postMessage(res);
}
}

这样,我们就实现了一个不阻塞主线程的,高性能的加法。(好像没有什么卵用

但是想想看,如果这是一个加密或解密算法,或者音视频转码,是不是就很有必要了?