正在加载,请稍候…

WebAssembly 实战:在浏览器中运行高性能代码

学习如何使用 WebAssembly (Wasm) 在浏览器中以接近原生速度运行 C++、Rust 和 Go 代码,涵盖编译、JS 互操作、内存管理及实际用例。

WebAssembly 实战:在浏览器中运行高性能代码

WebAssembly 实战:在浏览器中运行高性能代码

WebAssembly (Wasm) 是一种二进制指令格式,可在现代浏览器中以接近原生速度运行。它允许你将 C、C++、Rust、Go 等语言编译为可移植的二进制文件,与 JavaScript 并行运行。

为什么选择 WebAssembly?

  • 性能:以接近原生速度运行,对于计算密集型任务远快于 JS
  • 可移植性:同一二进制文件可在任何浏览器或 Node.js 中运行
  • 语言灵活性:在 Rust、C++ 擅长的领域使用它们
  • 安全性:在沙盒环境中运行

WebAssembly 实战:在浏览器中运行高性能代码 插图

将 Rust 编译为 WebAssembly

# 安装 wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# 创建一个新的 Rust + Wasm 项目
wasm-pack new hello-wasm
cd hello-wasm
// src/lib.rs
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn fibonacci(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fibonacci(n - 1) + fibonacci(n - 2),
    }
}

#[wasm_bindgen]
pub fn greet(name: &str) -> String {
    format!("Hello, {}! From WebAssembly.", name)
}
# 为 Web 构建
wasm-pack build --target web

在 JavaScript 中使用 Wasm

import init, { fibonacci, greet } from './pkg/hello_wasm.js';

async function run() {
  await init();
  
  console.log(greet("World")); // "Hello, World! From WebAssembly."
  
  const start = performance.now();
  const result = fibonacci(40);
  const end = performance.now();
  console.log(`fibonacci(40) = ${result}, took ${end - start}ms`);
}

run();

WebAssembly 实战:在浏览器中运行高性能代码 插图

内存管理

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct ImageProcessor {
    width: u32,
    height: u32,
    data: Vec<u8>,
}

#[wasm_bindgen]
impl ImageProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new(width: u32, height: u32) -> ImageProcessor {
        ImageProcessor {
            width,
            height,
            data: vec![0; (width * height * 4) as usize],
        }
    }

    pub fn grayscale(&mut self) {
        let pixels = self.data.chunks_mut(4);
        for pixel in pixels {
            let gray = (0.299 * pixel[0] as f32
                + 0.587 * pixel[1] as f32
                + 0.114 * pixel[2] as f32) as u8;
            pixel[0] = gray;
            pixel[1] = gray;
            pixel[2] = gray;
        }
    }

    pub fn data_ptr(&self) -> *const u8 {
        self.data.as_ptr()
    }
}

性能最佳实践

// 1. 最小化 JS-Wasm 边界跨越
// 错误:在循环中跨越边界
for (let i = 0; i < 1000000; i++) {
  wasmModule.process_single_item(i);
}

// 正确:将批量数据传递给 Wasm
wasmModule.process_all_items(dataArray, 1000000);

// 2. 大型 Wasm 文件的流式编译
const { instance } = await WebAssembly.instantiateStreaming(
  fetch('/module.wasm'),
  importObject
);

WebAssembly 实战:在浏览器中运行高性能代码 插图

WASI:浏览器之外的 Wasm

// WASI 允许 Wasm 作为服务端运行时运行
fn main() {
    println!("Hello from WASI!");
    let content = std::fs::read_to_string("input.txt").unwrap();
    println!("File content: {}", content);
}
# 使用 wasmtime 运行
cargo build --target wasm32-wasi
wasmtime target/wasm32-wasi/debug/my-app.wasm

实际用例

  1. 视频/图像处理:ffmpeg.wasm 用于客户端视频转换
  2. 密码学:快速加密操作(bcrypt、AES)
  3. 游戏引擎:Quake、Doom 在浏览器中运行
  4. 科学计算:浏览器中的 Python(Pyodide)
  5. SQLite:sql.js 用于客户端数据库

总结

WebAssembly 使得在浏览器中运行高性能代码成为可能。关键要点:

  • 对 CPU 密集型工作使用 Rust 或 C++
  • 最小化 JS/Wasm 边界跨越
  • 使用类型化数组实现高效内存共享
  • 考虑使用 WASI 实现可移植的服务端 Wasm