说起来,有一个多月没写博客了(学习笔记不算),今天来整个活,整一个 wasm 项目并对比下相对于 JavaScript 的性能,不过由于我自己是个半吊子,我整不出一个完善的对比环节,所以后面比较性能的内容可能有诸多纰漏敬请谅解。
WebAssembly(缩写为Wasm)是基于堆栈的虚拟机的二进制指令格式。Wasm 被设计为编程语言的可移植编译目标,支持在 Web 上部署客户端和服务器应用程序。
wasm 需要从一种语言编译成二进制码,我们这里选 C 语言,由于今天只是浅尝辄止,不想污染我的 Windows 开发环境,所以今天的案例跑在 wsl。
需要的软件列表如下:
我用的 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 之类的东西。
首先先创建文件和目录:
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(正式版),不能保证实验数据非常准确,因为这只是在找乐子。
这里我实际上是拿 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));
最后的结果是:
不出意料,还算凑合。
用到的代码是 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);
}
结果是:
上个表格对比一下吧:
JavaScript | C | |
---|---|---|
30 | 5.9ms | 5ms |
40 | 677.5ms | 566ms |
50 | 101165.9ms | 71988ms |
好像也没快太多...就那样?不过这目前还没上多线程,可惜我暂时无法实现,只能待之后再议。待我 Golang 大成,必然要好好折腾 wasm 一次。