Rust vs JavaScript: 使用 WebAssembly 提升 66% 的性能
本文将探讨如何在 JavaScript 应用中通过 WebAssembly 显著提升性能,并以斐波那契算法为例说明。我们将从 JavaScript 的单线程问题入手,介绍如何通过多线程(Web Worker)和 WebAssembly 来优化计算性能,最终展示如何通过使用 Rust 和 WebAssembly 提升效率。
JavaScript 的单线程问题
JavaScript 通常在单线程上运行,也就是“主线程”。主线程除了执行 JavaScript 代码,还负责渲染、绘制、布局和处理用户交互。因此,长时间运行的 JavaScript 任务可能会导致浏览器无响应。
为了展示这个问题,我们使用斐波那契算法模拟繁重的计算,观察主线程的阻塞问题。
const calculateFibonacci = (n: number): number => {
if (n <= 1) return n;
return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
};
单线程实现
我们直接在主线程上实现斐波那契算法,并通过按钮点击事件触发计算。由于 JavaScript 是单线程的,长时间的计算会阻塞页面的渲染,导致用户体验不佳。
"use client";
import { useState } from "react";
function Spinner() {
return (
<div className="flex justify-center items-center">
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-blue-500"></div>
</div>
);
}
export default function Home() {
const [result, setResult] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const calculateFibonacci = (n: number): number => {
if (n <= 1) return n;
return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
};
const handleCalculate = () => {
setIsLoading(true);
const result = calculateFibonacci(42);
setResult(result);
setIsLoading(false);
};
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white">
<button
onClick={handleCalculate}
className="mb-8 px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition"
>
Calculate Fibonacci
</button>
{isLoading ? <Spinner /> : <p className="text-xl">Result: {result}</p>}
</div>
);
}
多线程(Web Worker)
为了避免主线程阻塞,我们可以使用 Web Worker 将计算任务卸载到后台线程,从而保持页面的流畅性。
self.addEventListener("message", function (e) {
const n = e.data;
const fibonacci = (n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
};
const result = fibonacci(n);
self.postMessage(result);
});
"use client";
import { useState } from "react";
function Spinner() {
return (
<div className="flex justify-center items-center">
<div className="animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-blue-500"></div>
</div>
);
}
export default function Home() {
const [result, setResult] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleCalculate = () => {
setIsLoading(true);
const worker = new Worker(new URL("./fibonacci-worker.js", import.meta.url));
worker.postMessage(42);
worker.onmessage = (e) => {
setResult(e.data);
setIsLoading(false);
worker.terminate();
};
};
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white">
<button
onClick={handleCalculate}
className="mb-8 px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition"
>
Calculate Fibonacci
</button>
{isLoading ? <Spinner /> : <p className="text-xl">Result: {result}</p>}
</div>
);
}
使用 WebAssembly 优化性能
通过 WebAssembly,我们可以进一步提升性能。我们将展示如何使用 AssemblyScript 和 Rust 编写 WebAssembly 模块,以实现斐波那契算法。
WebAssembly — AssemblyScript
export function fibonacci(n: i32): i32 {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
编译为 WebAssembly 后,调用该模块可以显著加速计算:
"use client";
import { useState } from "react";
export default function Home() {
const [result, setResult] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleCalculate = async () => {
setIsLoading(true);
const wasmModule = await fetch("/release.wasm");
const buffer = await wasmModule.arrayBuffer();
const module = await WebAssembly.instantiate(buffer);
const wasm = module.instance.exports;
const fibResult = wasm.fibonacci(42);
setResult(fibResult);
setIsLoading(false);
};
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white">
<button
onClick={handleCalculate}
className="mb-8 px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition"
>
Calculate Fibonacci
</button>
{isLoading ? <Spinner /> : <p className="text-xl">Result: {result}</p>}
</div>
);
}
WebAssembly — Rust
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
Rust 编写的 WebAssembly 也能显著提升性能:
"use client";
import { useState } from "react";
export default function Home() {
const [result, setResult] = useState<number | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(false);
const handleCalculate = async () => {
setIsLoading(true);
const wasmModule = await fetch("/pkg/rust_wasm_fibonacci_bg.wasm");
const buffer = await wasmModule.arrayBuffer();
const module = await WebAssembly.instantiate(buffer);
const wasm = module.instance.exports;
const fibResult = wasm.fibonacci(42);
setResult(fibResult);
setIsLoading(false);
};
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white">
<button
onClick={handleCalculate}
className="mb-8 px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition"
>
Calculate Fibonacci
</button>
{isLoading ? <Spinner /> : <p className="text-xl">Result: {result}</p>}
</div>
);
}
性能比较
- JavaScript:约 2 秒
- WebAssembly(AssemblyScript):953ms(提升 53%)
- WebAssembly(Rust):684ms(提升 66%)
总结
通过 WebAssembly,我们可以显著提升计算性能。特别是使用 Rust 编写的 WebAssembly 模块,相较于纯 JavaScript 实现,性能提升了 66%。对于需要高性能计算的前端应用程序,WebAssembly 是一种非常有效的优化手段。