
最近研究 WASM 性能的时候做了一个不同编程语言的测试。把同样的斐波那契算法分别用Rust、Golang、MoonBit写一遍然后都编译成WASM看看谁的产物小、谁跑得快。这次为了保证公平三个语言统一使用 64 位整数进行对比。结果测完发现差距还是挺有代表性的。为什么要测这个说实话WASM 这几年越来越火了。浏览器里跑高性能计算、边缘函数、Serverless、小程序插件都离不开它。但问题来了选哪门语言写 WASM 更合适Rust生态最成熟Go是后端老大哥MoonBit则是这两年专门为WASM和云原生设计的新语言。我自己平时也在用MoonBit所以一直想知道同样是编译到WASM它跟Rust、Go比起来到底处在什么位置这次就用一个最朴素的斐波那契算法统一用 64 位整数实现从产物体积和执行速度两个维度做一个简单的横向评测。测试环境机器Intel i9-12900HNode.jsv24.9.0测试工具mitata语言版本Rust 1.96.1Go 1.26.3MoonBit 0.10.2算法分递归版和迭代版测试参数分别是n30、n35、n40。三个实现都编译成可在Node.js中加载运行的WASM在同一个 JS 宿主环境下被调用。产物体积对比先来看看到底编译出来多大。语言原始大小gzip 后MoonBit211 B178 BRust286 B235 BGo1.84 MB565.78 KBMoonBit211 字节Rust286 字节俩人都很小。MoonBit作为一个新语言能把产物压到这个级别说明它的编译器确实是为WASM专门优化过的。Rust也不差286 字节同样是顶尖水准。Go则是1.84 MB。原因大家都清楚Go的 WASM 产物要带一整个 runtime 进去GC、调度器、协程这些东西一个都不少。这是Go的设计选择不是缺点只是不太适合对体积敏感的场景。性能对比递归版斐波那契nRustMoonBitGo305.74 ms5.00 ms14.24 ms3555.77 ms50.03 ms154.23 ms40667.43 ms662.88 ms1.70 s递归场景下MoonBit和Rust非常接近MoonBit略快一点。Go慢一些n40的时候大概是MoonBit的 2.5 倍左右。这个结果并不意外Go在 WASM 下有 runtime 和 GC 开销递归这种大量函数调用的场景本来就不是它的强项。迭代版斐波那契nRustMoonBitGo3035.22 ns14.70 ns21.29 µs3538.52 ns12.04 ns18.33 µs4043.34 ns12.29 ns16.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 场景。不知道你怎么看呢欢迎在评论区留言。