WebAssembly(缩写为 Wasm)是基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为编程语言的可移植编译目标,支持在 Web 上部署客户端和服务器应用程序。
安装环境
wasm 需要从一种语言编译成二进制码,我们这里选 C 语言,由于今天只是浅尝辄止,不想污染我的 Windows 开发环境,所以今天的案例跑在 wsl。
需要的软件列表如下:
- Git:版本管理工具,wasm 的编译器需要我们编译,仓库需要拿 Git 克隆下来
- CMake:开源的跨平台自动化建构系统
- 编译器:编译 C 程序的
- Python 2.x:sdk 实际上是 python 写的,所以…
我用的 Ubuntu 系统,其他系统的自便,另外别忘了换清华源。
1
| apt install build-essential cmake python git
|
好,依赖安装好了,接下来编译
1 2 3 4 5
| 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 应用
首先先创建文件和目录:
1 2
| mkdir first-wasm && cd first-wasm touch main.c
|
在 main.c 里随便写个 Hello World:
1 2 3 4 5
| #include <stdio.h>
int main() { printf("Hello World\n"); }
|
然后编译吧:
1
| emcc main.c -o hello.html
|
-o 参数决定输出到哪个 HTML 里,这个编译工具直接帮你把所有 js 都写好了,直接给你送到 html 里。
如果你使用文件资源管理器双击大法打开文件,会发现控制台里因为 CORS 问题导致 wasm 文件没法加载,所以我们需要整个开发服务器,用 http 协议访问
1 2
| 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 写的:
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 27 28 29
| 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 帮忙写的:
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 27 28
| #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++) { } 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 |
好像也没快太多…就那样?