前言

WebAssembly (WASM) 已经从最初的高性能计算补充,发展成为现代 Web 应用的核心技术。2026年,WASM 2.0 带来了垃圾回收、线程并行、组件模型等重要特性,使其应用场景大幅扩展。本文将深入探讨 WASM 2.0 的新特性和实际应用。

WASM 2.0 核心特性

1. 垃圾回收 (GC)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Rust 编写 WASM 模块
use wasm_bindgen::prelude::*;

// 传统方式:手动管理内存
#[wasm_bindgen]
pub struct Point {
    x: f64,
    y: f64,
}

#[wasm_bindgen]
impl Point {
    #[wasm_bindgen(constructor)]
    pub fn new(x: f64, y: f64) -> *mut Point {
        let point = Box::new(Point { x, y });
        Box::into_raw(point)
    }

    pub fn distance(&self, other: &Point) -> f64 {
        ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()
    }
}

// WASI 2.0:内置垃圾回收
// 支持 JS 对象直接操作
#[wasm_bindgen]
pub struct Geometry {
    points: Vec<js_sys::Array>,
}

#[wasm_bindgen]
impl Geometry {
    #[wasm_bindgen(constructor)]
    pub fn new() -> Geometry {
        Geometry {
            points: Vec::new(),
        }
    }

    pub fn add_point(&mut self, point: &JsValue) {
        self.points.push(point.clone().unchecked_into());
    }
}

2. 线程并行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 多线程图像处理
use wasm_bindgen::prelude::*;
use web_sys::{Worker, WorkerOptions};
use js_sys::{Array, Promise};
use wasm_thread::ThreadPool;

#[wasm_bindgen]
pub struct ImageProcessor {
    pool: ThreadPool,
}

#[wasm_bindgen]
impl ImageProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new(threads: usize) -> Result<ImageProcessor, JsValue> {
        let pool = ThreadPool::new(threads)
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(ImageProcessor { pool })
    }

    // 并行图像模糊
    pub fn blur_parallel(
        &self,
        image_data: &[u8],
        width: usize,
        height: usize,
        radius: usize
    ) -> Result<Vec<u8>, JsValue> {
        let chunk_size = height / self.pool.thread_count();
        let mut results = vec![Vec::new(); self.pool.thread_count()];

        self.pool.scope(|s| {
            for (i, result) in results.iter_mut().enumerate() {
                let start = i * chunk_size;
                let end = if i == self.pool.thread_count() - 1 {
                    height
                } else {
                    start + chunk_size
                };

                s.spawn(move |_| {
                    *result = blur_chunk(
                        image_data,
                        width,
                        start,
                        end,
                        radius
                    );
                });
            }
        });

        // 合并结果
        let mut result = Vec::with_capacity(image_data.len());
        for chunk in results {
            result.extend_from_slice(&chunk);
        }

        Ok(result)
    }
}

fn blur_chunk(
    image_data: &[u8],
    width: usize,
    start_row: usize,
    end_row: usize,
    radius: usize
) -> Vec<u8> {
    // 实现局部模糊算法
    let mut result = Vec::new();
    // ... 模糊处理逻辑
    result
}

3. 组件模型

;; WASM 组件定义
(component
  (type (;0;) (func (param "x" s32) (result s32)))
  (type (;1;) (func (param "x" s32) (param "y" s32) (result s32)))

  // 导出接口
  (export "add" (func $add))
  (export "multiply" (func $multiply))

  // 导入接口
  (import "env" "log" (func $log (param s32)))

  (func $add (type 0) (param $x s32) (result s32)
    local.get $x
    i32.const 1
    i32.add)

  (func $multiply (type 1) (param $x s32) (param $y s32) (result s32)
    local.get $x
    local.get $y
    i32.mul)
)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// JavaScript 中使用 WASM 组件
import { add, multiply } from './math.component';

// 类型安全的接口
const result1 = add(10); // 11
const result2 = multiply(5, 3); // 15

// 组件组合
const pipeline = compose([
  multiplyByTwo,
  addTen,
  squareResult
]);

应用场景

1. AI 模型推理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 使用 ONNX Runtime 运行 AI 模型
use ort::{Environment, Session, SessionBuilder};

#[wasm_bindgen]
pub struct ModelRunner {
    session: Session,
}

#[wasm_bindgen]
impl ModelRunner {
    #[wasm_bindgen(constructor)]
    pub async fn new(model_url: &str) -> Result<ModelRunner, JsValue> {
        // 加载模型
        let model_bytes = fetch_model(model_url).await?;

        let environment = Environment::builder()
            .with_name("ModelEnvironment")
            .build()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        let session = SessionBuilder::new(&environment)
            .with_model_from_memory(&model_bytes)
            .map_err(|e| JsValue::from_str(&e.to_string()))?
            .with_execution_mode(ort::ExecutionMode::Parallel)
            .with_optimization_level(ort::GraphOptimizationLevel::All)
            .build()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(ModelRunner { session })
    }

    pub fn predict(
        &self,
        input: &[f32]
    ) -> Result<Vec<f32>, JsValue> {
        // 准备输入张量
        let input_tensor = ort::Value::from_array(
            self.session.allocator(),
            &[1, 224, 224, 3],
            input
        ).map_err(|e| JsValue::from_str(&e.to_string()))?;

        // 运行推理
        let outputs = self.session.run(
            ort::inputs!["input" => input_tensor.view()]
        ).map_err(|e| JsValue::from_str(&e.to_string()))?;

        // 提取输出
        let output = outputs["output"]
            .try_extract::<f32>()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(output.to_vec())
    }
}

2. 视频编解码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// FFmpeg WASM 封装
use ffmpeg_next as ffmpeg;

#[wasm_bindgen]
pub struct VideoTranscoder {
    input_context: ffmpeg::format::context::Input,
}

#[wasm_bindgen]
impl VideoTranscoder {
    #[wasm_bindgen(constructor)]
    pub fn new(input_url: &str) -> Result<VideoTranscoder, JsValue> {
        ffmpeg::init()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        let input_context = ffmpeg::format::input(&input_url)
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(VideoTranscoder { input_context })
    }

    pub fn transcode_to_mp4(
        &self,
        output_path: &str,
        video_bitrate: u32,
        audio_bitrate: u32
    ) -> Result<(), JsValue> {
        // 创建输出上下文
        let mut output_context = ffmpeg::format::output(&output_path)
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        // 转码视频流
        for (i, stream) in self.input_context.streams().enumerate() {
            if stream.codec().medium() == ffmpeg::media::Type::Video {
                self.transcode_video_stream(
                    stream,
                    &mut output_context,
                    video_bitrate
                )?;
            } else if stream.codec().medium() == ffmpeg::media::Type::Audio {
                self.transcode_audio_stream(
                    stream,
                    &mut output_context,
                    audio_bitrate
                )?;
            }
        }

        // 写入文件头
        output_context.write_header()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        // 转码帧
        self.transcode_frames(&mut output_context)?;

        // 写入文件尾
        output_context.write_trailer()
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(())
    }
}

3. 加密计算

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 端到端加密
use aes_gcm::{Aes256Gcm, Key, Nonce};
use rand::RngCore;

#[wasm_bindgen]
pub struct CryptoEngine {
    key: Key<Aes256Gcm>,
}

#[wasm_bindgen]
impl CryptoEngine {
    #[wasm_bindgen(constructor)]
    pub fn new(password: &str) -> CryptoEngine {
        // 从密码派生密钥
        let key = derive_key(password);

        CryptoEngine { key }
    }

    pub fn encrypt(
        &self,
        plaintext: &[u8]
    ) -> Result<EncryptedData, JsValue> {
        // 生成随机 nonce
        let mut nonce_bytes = [0u8; 12];
        rand::thread_rng().fill_bytes(&mut nonce_bytes);
        let nonce = Nonce::from_slice(&nonce_bytes);

        // 加密
        let cipher = Aes256Gcm::new(&self.key);
        let ciphertext = cipher.encrypt(nonce, plaintext)
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(EncryptedData {
            nonce: nonce_bytes.to_vec(),
            ciphertext: ciphertext.to_vec(),
        })
    }

    pub fn decrypt(
        &self,
        data: &EncryptedData
    ) -> Result<Vec<u8>, JsValue> {
        let nonce = Nonce::from_slice(&data.nonce);
        let cipher = Aes256Gcm::new(&self.key);

        let plaintext = cipher.decrypt(nonce, data.ciphertext.as_ref())
            .map_err(|e| JsValue::from_str(&e.to_string()))?;

        Ok(plaintext)
    }
}

#[wasm_bindgen]
pub struct EncryptedData {
    pub nonce: Vec<u8>,
    pub ciphertext: Vec<u8>,
}

性能优化技巧

1. 内存管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 线性内存优化
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct MemoryPool {
    heap: Vec<u8>,
    free_blocks: Vec<(usize, usize)>,
}

#[wasm_bindgen]
impl MemoryPool {
    #[wasm_bindgen(constructor)]
    pub fn new(initial_size: usize) -> MemoryPool {
        MemoryPool {
            heap: vec![0; initial_size],
            free_blocks: vec![(0, initial_size)],
        }
    }

    pub fn allocate(&mut self, size: usize) -> Result<usize, JsValue> {
        // 寻找合适的空闲块
        let index = self.free_blocks.iter()
            .position(|(offset, block_size)| {
                *block_size >= size
            });

        match index {
            Some(i) => {
                let (offset, block_size) = self.free_blocks.remove(i);

                // 如果块太大,分割它
                if block_size > size + 32 {
                    self.free_blocks.push((offset + size, block_size - size));
                    // 排序以保持合并顺序
                    self.free_blocks.sort();
                }

                Ok(offset)
            },
            None => {
                // 需要扩展内存
                self.expand_heap(size)?;
                self.allocate(size)
            }
        }
    }

    pub fn deallocate(&mut self, offset: usize, size: usize) {
        // 标记为空闲
        self.free_blocks.push((offset, size));

        // 合并相邻的空闲块
        self.coalesce_blocks();
    }

    fn coalesce_blocks(&mut self) {
        self.free_blocks.sort();

        let mut i = 0;
        while i < self.free_blocks.len() - 1 {
            let (offset1, size1) = self.free_blocks[i];
            let (offset2, size2) = self.free_blocks[i + 1];

            // 检查是否可以合并
            if offset1 + size1 == offset2 {
                self.free_blocks[i] = (offset1, size1 + size2);
                self.free_blocks.remove(i + 1);
            } else {
                i += 1;
            }
        }
    }
}

2. SIMD 优化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// SIMD 矢量化
use std::arch::wasm32::*;

#[wasm_bindgen]
pub fn vector_add_simd(a: &[f32], b: &[f32]) -> Vec<f32> {
    assert_eq!(a.len(), b.len());

    let mut result = vec![0.0f32; a.len()];
    let chunks = a.len() / 4;

    // 使用 SIMD 处理
    for i in 0..chunks {
        unsafe {
            let a_vec = v128_load(a.as_ptr().add(i * 4));
            let b_vec = v128_load(b.as_ptr().add(i * 4));
            let result_vec = f32x4_add(a_vec, b_vec);
            v128_store(result.as_mut_ptr().add(i * 4), result_vec);
        }
    }

    // 处理剩余元素
    for i in (chunks * 4)..a.len() {
        result[i] = a[i] + b[i];
    }

    result
}

// 矩阵乘法优化
#[wasm_bindgen]
pub fn matrix_multiply_simd(
    a: &[f32],
    b: &[f32],
    n: usize
) -> Vec<f32> {
    let mut result = vec![0.0f32; n * n];

    for i in 0..n {
        for j in 0..n {
            let mut sum = f32x4_splat(0.0);

            for k in (0..n).step_by(4) {
                unsafe {
                    let a_vec = f32x4_load(a.as_ptr().add(i * n + k));
                    let b_vec = f32x4_load(b.as_ptr().add(k * n + j));
                    let product = f32x4_mul(a_vec, b_vec);
                    sum = f32x4_add(sum, product);
                }
            }

            unsafe {
                let mut total = [0.0f32; 4];
                f32x4_store(total.as_mut_ptr(), sum);
                result[i * n + j] = total.iter().sum();
            }
        }
    }

    result
}

与 JavaScript 互操作

1. 高效数据传输

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// JavaScript 端
import { processImage } from './image_processor';

class ImageProcessor {
  private async loadImage(url: string): Promise<ImageData> {
    const response = await fetch(url);
    const blob = await response.blob();
    const bitmap = await createImageBitmap(blob);

    const canvas = new OffscreenCanvas(
      bitmap.width,
      bitmap.height
    );
    const ctx = canvas.getContext('2d')!;
    ctx.drawImage(bitmap, 0, 0);

    return ctx.getImageData(
      0, 0,
      bitmap.width,
      bitmap.height
    );
  }

  async process(url: string): Promise<ImageData> {
    const imageData = await this.loadImage(url);

    // 零拷贝传输
    const buffer = imageData.data.buffer;
    const result = await processImage(
      buffer,
      imageData.width,
      imageData.height
    );

    // 直接返回处理后的数据
    return new ImageData(
      new Uint8ClampedArray(result),
      imageData.width,
      imageData.height
    );
  }
}

// 使用 SharedArrayBuffer 实现真正的零拷贝
class SharedImageProcessor {
  private sharedBuffer: SharedArrayBuffer;

  constructor() {
    // 创建共享内存
    this.sharedBuffer = new SharedArrayBuffer(1920 * 1080 * 4);
  }

  async process(imageData: ImageData): Promise<void> {
    // 写入共享内存
    const view = new Uint8ClampedArray(this.sharedBuffer);
    view.set(imageData.data);

    // 通知 Worker 处理
    const worker = new Worker('processor-worker.js');
    worker.postMessage({
      buffer: this.sharedBuffer,
      width: imageData.width,
      height: imageData.height
    }, [this.sharedBuffer]);

    // 等待处理完成
    await new Promise(resolve => {
      worker.onmessage = (e) => {
        if (e.data.done) {
          resolve();
        }
      };
    });
  }
}

2. 异步操作

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// WASM 端异步支持
use wasm_bindgen_futures::future_to_promise;
use js_sys::Promise;

#[wasm_bindgen]
pub struct AsyncProcessor {
    executor: TaskExecutor,
}

#[wasm_bindgen]
impl AsyncProcessor {
    #[wasm_bindgen(constructor)]
    pub fn new() -> AsyncProcessor {
        AsyncProcessor {
            executor: TaskExecutor::new(),
        }
    }

    pub fn process_async(
        &self,
        data: Vec<u8>
    ) -> Promise {
        future_to_promise(async move {
            // 模拟异步操作
            let result = web_sys::window()
                .unwrap()
                .fetch_with_str("https://api.example.com/data")
                .await?;

            // 处理数据
            Ok(JsValue::from_str("Processing complete"))
        })
    }
}

调试和测试

1. WASM 单元测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_vector_add() {
        let a = vec![1.0, 2.0, 3.0, 4.0];
        let b = vec![5.0, 6.0, 7.0, 8.0];
        let result = vector_add_simd(&a, &b);

        assert_eq!(result, vec![6.0, 8.0, 10.0, 12.0]);
    }

    #[test]
    fn test_matrix_multiply() {
        let a = vec![1.0, 2.0, 3.0, 4.0];
        let b = vec![5.0, 6.0, 7.0, 8.0];
        let result = matrix_multiply_simd(&a, &b, 2);

        assert!((result[0] - 19.0).abs() < 0.001);
        assert!((result[3] - 50.0).abs() < 0.001);
    }
}

2. 性能分析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// WASM 性能分析工具
class WasmProfiler {
  private measurements: Map<string, number[]> = new Map();

  measure(name: string, fn: () => void): void {
    const start = performance.now();
    fn();
    const duration = performance.now() - start;

    if (!this.measurements.has(name)) {
      this.measurements.set(name, []);
    }

    this.measurements.get(name)!.push(duration);
  }

  report(): PerformanceReport {
    const report: PerformanceReport = {};

    for (const [name, measurements] of this.measurements) {
      const avg = measurements.reduce((a, b) => a + b) / measurements.length;
      const min = Math.min(...measurements);
      const max = Math.max(...measurements);
      const p95 = measurements.sort((a, b) => a - b)[
        Math.floor(measurements.length * 0.95)
      ];

      report[name] = { avg, min, max, p95, count: measurements.length };
    }

    return report;
  }
}

// 使用示例
const profiler = new WasmProfiler();

// 对比 JavaScript 和 WASM 性能
profiler.measure('js-add', () => {
  const result = new Float32Array(1000000);
  for (let i = 0; i < result.length; i++) {
    result[i] = array1[i] + array2[i];
  }
});

profiler.measure('wasm-add', () => {
  const result = vector_add_simd(array1, array2);
});

console.table(profiler.report());

总结

WebAssembly 2.0 的关键进展:

  1. 垃圾回收:简化内存管理,提升开发效率
  2. 线程并行:充分利用多核 CPU
  3. 组件模型:更好的模块化和互操作性
  4. 性能优化:SIMD 和内存管理优化
  5. 生态完善:丰富的工具和库支持

WASM 已成为 Web 高性能计算的标准选择,特别适合:

  • AI 模型推理
  • 视频/音频处理
  • 加密计算
  • 科学计算
  • 游戏引擎

掌握 WASM 开发,让你的 Web 应用突破性能瓶颈。


相关工具: