JS vs WASM 性能对比(Benchmark)

同一个算法,JavaScript 和 Rust WASM 各跑一遍

点击「运行全部」开始

每项先预热 1 次,再取 3 次运行的中位数  |  结果列 ✓ 表示两边算出的结果完全一致

📖 原理说明

把同一个算法分别用 JavaScript 和 Rust 写一遍,在同一个浏览器里各跑一次,直接比较耗时。两边的实现逐行对应,结果也必须完全一致,这样比较的才是语言和运行时本身,而不是算法差异。

🧮算法原理

加速比 = TJS / TWASM大于 1 表示 WASM 更快
素数筛
埃拉托斯特尼筛法:从 2 开始,把每个素数的倍数标记为合数,剩下未被标记的就是素数。主要考验对大数组的密集读写。
递归斐波那契
fib(n) = fib(n−1) + fib(n−2) 的朴素递归,调用次数随 n 指数增长,几乎只有函数调用和整数加法,用来衡量函数调用开销。
排序
用 xorshift 伪随机数生成器按同一个种子产生相同的随机数组,再排序,最后取首、中、尾三个元素异或作为校验值。Rust 使用 sort_unstable(模式消除快速排序),JS 使用 Uint32Array.prototype.sort。
矩阵乘法
两个按固定公式填充的 N×N 浮点矩阵相乘,复杂度 O(N³)。采用 i-k-j 循环顺序,让最内层循环连续访问内存,对 CPU 缓存友好。
测量方法
每项先运行 1 次作为预热,让 JS 引擎完成即时编译(JIT)优化,然后连续运行 3 次,取中位数,减少偶然波动的影响。

WASM 并不总是更快。现代 JS 引擎的 JIT 会把类型稳定的热点循环编译成接近原生的机器码,差距可能只有百分之几十;像 TypedArray.sort 这样的内置函数本身就是引擎用 C++ 实现的。WASM 的优势在于性能稳定、可预测:没有 JIT 预热,没有垃圾回收停顿,也不会因为类型变化而退回到慢速路径。

🔄Rust 与 JavaScript 的分工

  1. JSJS 按选定的规模依次运行 4 个测试项。每个测试项先运行 JS 版(sieveJS、fibJS 等),用 performance.now() 计时。
  2. Rust再调用对应的 Rust 导出函数(bench_sieve、bench_fib、bench_sort、bench_matmul)。它们只接收一个数字参数、返回一个数字,跨边界的开销可以忽略。
  3. JSJS 比较两边的返回值,计算加速比并画出对比条。每个测试项之间让出一次主线程,页面不会卡死。

⚡性能要点

  • 参考数据(一台 Linux 测试机,Chromium 152,「中」规模):素数筛 WASM 快 1.3×,递归斐波那契快 2.0×,排序快 1.7×,矩阵乘法快 2.0×。不同浏览器、不同 CPU 的结果可能差别很大。
  • 差距普遍在 2 倍以内,这说明 JIT 已经很强;WASM 更大的价值在于性能稳定可预测,以及可以直接复用现有的 Rust / C / C++ 代码。
  • Rust 编译时开启了 opt-level = "s"(优化体积)和 LTO。改成 opt-level = 3 可能更快,但 .wasm 文件会更大。
  • 为了避免 32 位溢出,Rust 版素数筛在计算 i × i 之前先检查 i ≤ n / i,因为 wasm32 上的 usize 只有 32 位。

源码crates/algorithms/benchmark/src/lib.rswww/benchmark/index.js含逐行对应的 JS 版本