前端遇到图像/矩阵/密码学等 CPU 密集任务时,JS 性能成瓶颈。WebAssembly 让 Rust 代码编译为接近原生速度的字节码。
工具链与基础绑定
安装 wasm-pack 后,用 #[wasm_bindgen] 宏把 Rust 函数导出为 JS 可调用形式。复杂结构用 serde + serde_wasm_bindgen 序列化跨语言传递。
use wasm_bindgen::prelude::*;
use serde::{Serialize, Deserialize};
use web_sys::console;
#[derive(Serialize, Deserialize)]
pub struct MatrixResult { rows: usize, cols: usize, data: Vec<f64>, compute_ms: f64 }
#[wasm_bindgen(start)]
pub fn init() {
console_error_panic_hook::set_once();
console::log_1(&"[WASM] Matrix module loaded".into());
}
#[wasm_bindgen]
pub fn matrix_multiply(a_ptr: *mut f64, a_rows: usize, a_cols: usize, b_ptr: *mut f64, b_cols: usize) -> JsValue {
use std::time::Instant;
let t0 = Instant::now();
let a = unsafe { std::slice::from_raw_parts(a_ptr, a_rows * a_cols) };
let b = unsafe { std::slice::from_raw_parts(b_ptr, a_cols * b_cols) };
let mut c = vec![0.0f64; a_rows * b_cols];
for i in 0..a_rows {
for k in 0..a_cols {
let aik = a[i * a_cols + k];
let rb = &b[k * b_cols..(k + 1) * b_cols];
let rc = &mut c[i * b_cols..(i + 1) * b_cols];
for j in 0..b_cols { rc[j] += aik * rb[j]; }
}
}
let r = MatrixResult { rows: a_rows, cols: b_cols, data: c, compute_ms: t0.elapsed().as_secs_f64() * 1000.0 };
serde_wasm_bindgen::to_value(&r).unwrap()
}
#[wasm_bindgen]
pub fn fibonacci_iter(n: u32) -> u64 {
if n <= 1 { return n as u64; }
let (mut prev, mut curr) = (0u64, 1u64);
for _ in 2..=n { let next = prev.saturating_add(curr); prev = curr; curr = next; }
curr
}
#[wasm_bindgen(js_name = "allocateFloat64Array")]
pub fn allocate_f64(len: usize) -> *mut f64 {
let mut v = Vec::with_capacity(len);
let p = v.as_mut_ptr();
std::mem::forget(v);
p
}
JS 调用与内存共享
WASM 内存是共享 ArrayBuffer,通过 TypedArray 视图直接读写比参数序列化快 10-100 倍。大数据时直接分配内存给 JS 端填充,再传指针和尺寸给 WASM。
| 对比项 | 纯 JS | WASM (Rust) | 加速倍数 | 适用场景 |
|---|---|---|---|---|
| 矩阵乘法 1024x1024 | ~1800ms | ~85ms | ~21x | 3D/游戏/AI推理 |
| Fibonacci(n=40) | ~980ms | ~8ms | ~120x | 数学计算 |
| SHA-256 1MB | ~42ms | ~6ms | ~7x | 密码学/校验 |
最佳实践
适合计算密集型循环,不要做 DOM 操作。优先 TypedArray 共享内存而非大对象参数,发布构建用 wasm-opt 优化体积。