最近研究 WASM 性能的时候,做了一个不同编程语言的测试。
把同样的斐波那契算法,分别用 Rust、Golang、MoonBit 写一遍,然后都编译成 WASM,看看谁的产物小、谁跑得快。
这次为了保证公平,三个语言统一使用 64 位整数进行对比。
结果测完发现,差距还是挺有代表性的。
为什么要测这个?
说实话,WASM 这几年越来越火了。
浏览器里跑高性能计算、边缘函数、Serverless、小程序插件,都离不开它。
但问题来了:选哪门语言写 WASM 更合适?
Rust 生态最成熟,Go 是后端老大哥,MoonBit 则是这两年专门为 WASM 和云原生设计的新语言。
我自己平时也在用 MoonBit,所以一直想知道:同样是编译到 WASM,它跟 Rust、Go 比起来到底处在什么位置?
这次就用一个最朴素的斐波那契算法,统一用 64 位整数实现,从产物体积和执行速度两个维度,做一个简单的横向评测。
测试环境
- 机器:
Intel i9-12900H - Node.js:
v24.9.0 - 测试工具:
mitata - 语言版本:
Rust 1.96.1Go 1.26.3MoonBit 0.10.2
算法分递归版和迭代版,测试参数分别是 n=30、n=35、n=40。
三个实现都编译成可在 Node.js 中加载运行的 WASM,在同一个 JS 宿主环境下被调用。
产物体积对比
先来看看到底编译出来多大。
| 语言 | 原始大小 | gzip 后 |
|---|---|---|
| MoonBit | 211 B | 178 B |
| Rust | 286 B | 235 B |
| Go | 1.84 MB | 565.78 KB |
MoonBit 211 字节,Rust 286 字节,俩人都很小。
MoonBit 作为一个新语言,能把产物压到这个级别,说明它的编译器确实是为 WASM 专门优化过的。
Rust 也不差,286 字节同样是顶尖水准。
Go 则是 1.84 MB。
原因大家都清楚,Go 的 WASM 产物要带一整个 runtime 进去,GC、调度器、协程这些东西一个都不少。
这是 Go 的设计选择,不是缺点,只是不太适合对体积敏感的场景。
性能对比
递归版斐波那契
| n | Rust | MoonBit | Go |
|---|---|---|---|
| 30 | 5.74 ms | 5.00 ms | 14.24 ms |
| 35 | 55.77 ms | 50.03 ms | 154.23 ms |
| 40 | 667.43 ms | 662.88 ms | 1.70 s |
递归场景下,MoonBit 和 Rust 非常接近,MoonBit 略快一点。
Go 慢一些,n=40 的时候大概是 MoonBit 的 2.5 倍左右。
这个结果并不意外,Go 在 WASM 下有 runtime 和 GC 开销,递归这种大量函数调用的场景本来就不是它的强项。
迭代版斐波那契
| n | Rust | MoonBit | Go |
|---|---|---|---|
| 30 | 35.22 ns | 14.70 ns | 21.29 µs |
| 35 | 38.52 ns | 12.04 ns | 18.33 µs |
| 40 | 43.34 ns | 12.29 ns | 16.05 µs |
迭代版的差距更明显。
MoonBit 是 12到15 纳秒级别,Rust 是 35到43 纳秒,Go 则是 16到21 微秒。
注意单位,Go 是微秒,其他两个是纳秒。
Go 慢这么多,不是说它的算法不行,而是 JS 调用 Go WASM 的运行时开销太大了。
每次调用都要经过 Go 的 JS 互操作层,参数转来转去,GC 还要插一脚,轻量任务根本吃不消。
我怎么看?
说实话,这次测试给了我三个挺深的印象。
第一个,MoonBit 在这个测试里表现不错。
产物最小,速度最快,而且它不是只编译到 WASM。
WASM、JavaScript、Native 三个后端都能输出,语法上有 GC、模式匹配、强类型系统,配套的 IDE 和包管理也还算完整。
从我实际使用的感受来看,如果你做的是边缘函数、Serverless、小程序插件这种对产物体积和冷启动敏感的场景,MoonBit 是一个可以考虑的选项。
当然,它的生态还在成长期,第三方库和社区规模跟 Rust 比还有差距。
第二个,Rust 依然是 WASM 的稳妥之选。
产物 286 字节,性能跟 MoonBit 在同一个量级,生态成熟度更是遥遥领先。
wasm-bindgen、wasm-pack、wasm-opt 这些工具链已经非常完善了,遇到问题基本都能查到解决方案。
如果你要做长期维护、团队规模较大的项目,Rust 目前还是更稳的选择。
第三个,Go 标准编译器的 WASM 产物确实偏大。
但这是 Go 的设计取舍,它本来就是为了服务端、高并发、快速开发而设计的。
产物大是因为自带 runtime,调用开销高是因为 JS 和 Go runtime 之间需要频繁转换。
如果你已经有大量 Go 代码要复用,或者团队成员都是 Go 出身,那用标准 Go 编译 WASM 也能接受。
但如果从头选型,TinyGo 会是比标准 Go 更适合 WASM 的方案。
最后三句话总结
Rust生态最成熟,文档最丰富。MoonBit产物更小、迭代性能更好。Go标准编译器不太适合对体积敏感的 WASM 场景。
不知道你怎么看呢?欢迎在评论区留言。
感谢阅读,我是前端之虎陈随易,公众号 陈随易,个人网站:https://chensuiyi.me。