WebAssembly(缩写为 Wasm)是基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为编程语言的可移植编译目标,支持在 Web 上部署客户端和服务器应用程序。
安装环境
wasm 需要从一种语言编译成二进制码,我们这里选 C 语言,由于今天只是浅尝辄止,不想污染我的 Windows 开发环境,所以今天的案例跑在 wsl。
需要的软件列表如下:
- Git:版本管理工具,wasm 的编译器需要我们编译,仓库需要拿 Git 克隆下来
- CMake:开源的跨平台自动化建构系统
- 编译器:编译 C 程序的
- Python 2.x:sdk 实际上是 python 写的,所以…
我用的 Ubuntu 系统,其他系统的自便,另外别忘了换清华源。
apt install build-essential cmake python git
好,依赖安装好了,接下来编译
git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh
中间看到了某 404 公司的资源被下载,所以我们需要做什么呢?我也不知道。
如果现在一切顺利,那么 emcc 编译器已经成功安装了,如果你在 bash 中键入 emcc
,应该不会看到 command not found 之类的东西。
第一个 wasm 应用
首先先创建文件和目录:
mkdir first-wasm && cd first-wasm
touch main.c
在 main.c 里随便写个 Hello World:
#include <stdio.h>
int main() {
printf("Hello World\n");
}
然后编译吧:
emcc main.c -o hello.html
-o 参数决定输出到哪个 HTML 里,这个编译工具直接帮你把所有 js 都写好了,直接给你送到 html 里。
如果你使用文件资源管理器双击大法打开文件,会发现控制台里因为 CORS 问题导致 wasm 文件没法加载,所以我们需要整个开发服务器,用 http 协议访问
npm install http-server -g
http-server hello.html
然后打开 http://localhost:8080/hello.html,就能看见你的第一个 wasm 应用跑起来了。
性能对比
之前一直好奇到底这俩性能的差距能有多大,今天来简单比一下。再强调一次,我水平拉跨的要死,这种性能对比看个乐呵就行了。
比较的内容是,分别计算斐波那契数列的第 30 项、第 40 项、第 50 项各十次,并给出计算这三个数字的平均时间。设备是 ROG 幻 16,CPU 是 Intel 11th i7,运行在 Chrome 101.0.4951.67(正式版),不能保证实验数据非常准确,因为这只是在找乐子。
JavaScript 上场
这里我实际上是拿 TypeScript 写的:
const execTime = (func: () => any) => {
let start = new Date().getTime();
func();
let end = new Date().getTime();
return end - start;
};
const benchmark = (func: () => any) => {
const time = 10;
let result: Array<number> = [];
for (let i = 0; i <= time; i++) result.push(execTime(func));
let sum = result.reduce((prev, next) => prev + next);
return sum / time;
};
const fibonacci = (n: number): number => {
if (n == 1 || n == 2) return 1;
return fibonacci(n - 2) + fibonacci(n - 1);
};
const fibonacciBenchmark = (start: number, end: number, range: number) => {
let map = new Map();
for (let index = start; index <= end; index += range) {
map.set(
index,
benchmark(() => fibonacci(index)),
);
}
return map;
};
console.log(fibonacciBenchmark(30, 50, 10));
最后的结果是:
- 计算 30,平均时间是 5.9ms
- 计算 40,平均时间是 677.5ms
- 计算 50,平均时间是 101165.9ms
不出意料,还算凑合。
C 语言上场
用到的代码是 Expliyh 帮忙写的:
#include <stdio.h>
#include <time.h>
long long fibo(long long n);
int main()
{
clock_t start, end;
start = clock();
for (int i = 0; i < 10; i++) {
// fibo(30);
// fibo(40);
// fibo(50);
}
end = clock();
printf("%lld\n", m);
printf("%ld", (end - start) / 10);
}
long long fibo(long long n)
{
if (n <= 1)
{
return n;
}
return fibo(n - 1) + fibo(n - 2);
}
结果是:
- 计算 30,平均时间是 5ms
- 计算 40,平均时间是 566ms
- 计算 50,平均时间是 71988ms
上个表格对比一下吧:
JavaScript | C | |
---|---|---|
30 | 5.9ms | 5ms |
40 | 677.5ms | 566ms |
50 | 101165.9ms | 71988ms |
好像也没快太多…就那样?