正在加载,请稍候…

WebAssembly in Practice: High-Performance Code in the Browser

Learn how to use WebAssembly (Wasm) to run C++, Rust, and Go code in the browser at near-native speed. Covers compilation, JS interop, memory management, and real use cases.

WebAssembly in Practice: High-Performance Code in the Browser

WebAssembly (Wasm) is a binary instruction format that runs in modern browsers at near-native speed. It lets you compile C, C++, Rust, Go, and other languages to a portable binary that runs alongside JavaScript.

Why WebAssembly?

  • Performance: Runs at near-native speed, much faster than JS for compute-heavy tasks
  • Portability: Same binary runs in any browser or Node.js
  • Language flexibility: Use Rust, C++ where they excel
  • Security: Runs in a sandboxed environment

Compiling Rust to WebAssembly

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

# Create a new Rust + Wasm project
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)
}
# Build for web
wasm-pack build --target web

Using Wasm in JavaScript

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();

Memory Management

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()
    }
}

Performance Best Practices

// 1. Minimize JS-Wasm boundary crossings
// BAD: Crossing boundary in a loop
for (let i = 0; i < 1000000; i++) {
  wasmModule.process_single_item(i);
}

// GOOD: Pass bulk data to Wasm
wasmModule.process_all_items(dataArray, 1000000);

// 2. Streaming compilation for large Wasm files
const { instance } = await WebAssembly.instantiateStreaming(
  fetch('/module.wasm'),
  importObject
);

WASI: Wasm Outside the Browser

// WASI allows Wasm to run as a server-side runtime
fn main() {
    println!("Hello from WASI!");
    let content = std::fs::read_to_string("input.txt").unwrap();
    println!("File content: {}", content);
}
# Run with wasmtime
cargo build --target wasm32-wasi
wasmtime target/wasm32-wasi/debug/my-app.wasm

Real-World Use Cases

  1. Video/Image Processing: ffmpeg.wasm for client-side video conversion
  2. Cryptography: Fast crypto operations (bcrypt, AES)
  3. Game Engines: Quake, Doom running in browser
  4. Scientific Computing: Python (Pyodide) in browser
  5. SQLite: sql.js for client-side database

Summary

WebAssembly enables high-performance code in the browser. Key takeaways:

  • Use Rust or C++ for CPU-intensive work
  • Minimize JS/Wasm boundary crossings
  • Work with typed arrays for efficient memory sharing
  • Consider WASI for portable server-side Wasm