WebAssembly 入门
概述
什么是 WebAssembly
WebAssembly(简称 Wasm)是一种低级的、可移植的二进制指令格式,专为 Web 平台设计。它被设计为一种编译目标,使 C/C++、Rust、Go 等高级语言能够以接近原生的速度在浏览器中运行。Wasm 以 .wasm 二进制格式分发,体积紧凑,加载快速,且具有可预测的性能特征。
Wasm 的核心设计目标包括:
- 高性能:接近原生的执行速度,利用底层硬件能力
- 安全:运行在沙箱环境中,遵循浏览器的同源策略
- 可移植:跨平台、跨浏览器一致执行
- 开放:作为 W3C 开放标准,任何人都可以贡献和实现
Wasm 并不是要取代 JavaScript,而是与 JavaScript 互补。两种语言可以互相调用,发挥各自的优势——JavaScript 处理 DOM 操作和 UI 交互,Wasm 处理计算密集型任务。
二进制格式与沙箱
Wasm 以二进制格式分发,而非文本格式。这种格式被设计为能够被浏览器快速解码和编译。一个 .wasm 文件包含:
- 类型区:定义函数签名(参数和返回类型)
- 函数区:包含本地变量定义和字节码指令
- 内存区:定义线性内存的初始大小和最大大小
- 导出区:暴露给 JavaScript 调用的函数和内存
- 导入区:从宿主环境(JavaScript)导入的函数和内存
Wasm 的沙箱模型确保了安全性。每个 Wasm 模块运行在一个隔离的虚拟环境中:
- 线性内存:模块只能访问自己的线性内存,无法直接访问宿主内存
- 无系统调用:模块不能直接执行系统调用,所有 I/O 必须通过导入函数
- 类型安全:Wasm 的验证器在加载时检查类型一致性
- 控制流完整性:间接调用受到限制和验证
性能优势
Wasm 相较于 JavaScript 的性能优势源于多个方面:
- 预编译:Wasm 二进制格式可以被浏览器直接编译为机器码,无需像 JS 那样经过解析和 JIT 热身
- 类型确定:Wasm 是强类型的,没有类型混淆开销
- 无 GC 暂停:Wasm 目前不包含垃圾收集器,避免了 GC 引起的停顿
- 确定性执行:指令语义精确定义,编译器可以生成高度优化的机器码
- SIMD 支持:单指令多数据指令集,适合并行计算
在计算密集型场景下,Wasm 通常比 JavaScript 快 1.5 到 3 倍,在某些场景下(如图像处理、物理模拟)可以达到 5 到 10 倍的提升。
与 JavaScript 的互操作
Wasm 与 JavaScript 之间的互操作是通过导入和导出机制实现的:
- 导出:Wasm 模块可以导出函数、内存、表和全局变量,供 JavaScript 调用
- 导入:Wasm 模块可以导入 JavaScript 函数(称为"导入函数"),用于执行 Wasm 无法直接完成的操作
一个典型的互操作模式如下:
// 加载 Wasm 模块
const response = await fetch('module.wasm');
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
// 提供导入函数
js_log: (ptr, len) => {
const str = new TextDecoder().decode(
new Uint8Array(instance.exports.memory.buffer, ptr, len)
);
console.log(str);
},
},
});
// 调用导出的 Wasm 函数
const result = instance.exports.compute(42);应用场景概览
Wasm 在以下领域展现出巨大的应用潜力:
- 图像和视频处理:批量像素操作、滤镜、编解码
- 音视频编解码:FFmpeg 等原生编解码库的 Web 移植
- 3D 渲染和游戏:游戏引擎(Unity、Unreal)的 Web 部署
- 科学计算:数据模拟、数值分析、机器学习推理
- 加密和哈希:高吞吐量密码学操作
- 数据压缩:gzip、brotli、zstd 等压缩算法
- 数据库:SQLite 等数据库的浏览器端运行
- CAD 和设计工具:Figma、AutoCAD Web 等复杂桌面应用的 Web 化
历史与现状
WebAssembly 的发展历程:
| 年份 | 里程碑 |
|---|---|
| 2015 | Mozilla 发布 asm.js,Wasm 的前身 |
| 2017 | 四大浏览器(Chrome、Firefox、Safari、Edge)达成共识,Wasm MVP 发布 |
| 2019 | Wasm 成为 W3C 推荐标准 |
| 2020 | WASI 提案推进,Wasm 向服务端扩展 |
| 2022 | WasmGC、SIMD 等扩展提案标准化 |
| 2023-2025 | Wasm 在云原生、边缘计算领域快速增长 |
截至 2026 年,Wasm 已获得所有主流浏览器和运行时(Node.js、Deno、Bun)的原生支持。Wasm 生态系统蓬勃发展,工具链成熟,已被广泛用于生产环境。
Emscripten
C/C++ 到 Wasm 编译
Emscripten 是一个完整的编译器工具链,能够将 C 和 C++ 代码编译为 WebAssembly。它基于 LLVM/Clang,将 C/C++ 编译为 LLVM 中间表示,再进一步编译为 Wasm 二进制格式。
安装 Emscripten:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh编译一个简单的 C 程序:
// hello.c
#include <emscripten/emscripten.h>
EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}emcc hello.c -O3 -s WASM=1 -o hello.js这将生成 hello.wasm 和 hello.js(加载器和胶水代码)。
Emscripten 运行时
Emscripten 生成的胶水代码包含一个完整的运行时环境,负责:
- Wasm 模块加载和实例化:自动处理异步加载
- 内存管理:模拟 C 的
malloc/free - 文件系统:模拟虚拟文件系统(MEMFS、NODEFS、IDBFS)
- 标准库支持:GLibc、SDL、OpenGL、pthread 等库的适配
编译选项控制运行时的特性:
# 启用优化的同时减小胶水代码体积
emcc program.c -O3 -s WASM=1 -s MODULARIZE=1 -o program.js
# 启用 pthread 支持(多线程)
emcc program.c -O3 -s WASM=1 -s USE_PTHREADS=1 -o program.jsFFmpeg 案例
FFmpeg 是音频视频处理领域的事实标准库。通过 Emscripten 将其编译为 Wasm,可以在浏览器端实现视频转码、截图、滤镜等操作,无需服务端支持。
社区项目 ffmpeg.wasm 实现了这一目标。其典型的编译流程如下:
# 配置 FFmpeg 为 Emscripten 交叉编译
emconfigure ./configure \
--cc=emcc --cxx=em++ \
--enable-cross-compile \
--target-os=none --arch=x86_32 \
--disable-asm --disable-doc \
--disable-programs --disable-stripping
# 编译
emmake make -j4
# 生成 Wasm 模块
emcc -O3 -s WASM=1 \
-s TOTAL_MEMORY=256MB \
-s EXPORTED_FUNCTIONS='["_avcodec_encode_video"]' \
-o ffmpeg.js ffmpeg_output/*.o在浏览器端使用 ffmpeg.wasm:
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile } from '@ffmpeg/util';
const ffmpeg = new FFmpeg();
await ffmpeg.load();
ffmpeg.writeFile('input.mp4', await fetchFile('/video.mp4'));
await ffmpeg.exec(['-i', 'input.mp4', '-vf', 'scale=320:240', 'output.mp4']);
const data = await ffmpeg.readFile('output.mp4');SIMD 支持
SIMD(单指令多数据)是 Wasm 的一个重要扩展,允许在同一指令中对多个数据元素并行操作。Emscripten 通过 LLVM 的自动向量化或显式的 SIMD 内建函数来利用这一能力。
// 使用 SIMD 内建函数进行并行加法(C 语言)
#include <wasm_simd128.h>
void add_arrays(float* a, float* b, float* out, int len) {
for (int i = 0; i < len; i += 4) {
v128_t va = wasm_v128_load(&a[i]);
v128_t vb = wasm_v128_load(&b[i]);
v128_t vr = wasm_f32x4_add(va, vb);
wasm_v128_store(&out[i], vr);
}
}编译时需要启用 SIMD 支持:
emcc simd_example.c -O3 -msimd128 -o simd_example.jsSIMD 对图像处理、音频合成、物理模拟等数据并行任务能带来 2 到 4 倍的性能提升。
Rust → Wasm
wasm-pack
Rust 拥有目前最成熟的 Wasm 开发生态。wasm-pack 是 Rust → Wasm 的核心工具,负责编译、打包和发布 Wasm 模块到 npm。
安装 wasm-pack:
cargo install wasm-pack创建一个 Rust Wasm 项目:
cargo init --lib wasm-demo在 Cargo.toml 中配置:
[package]
name = "wasm-demo"
version = "0.1.0"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"编写 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),
}
}编译为 Wasm:
wasm-pack build --target web编译结果位于 pkg/ 目录,包含 .wasm 文件、生成的 JavaScript 绑定和 TypeScript 类型声明。
wasm-bindgen
wasm-bindgen 是 Rust 和 JavaScript 之间的桥梁。它解决了 Wasm 只能传递整数和浮点数等基本类型的限制,使复杂的对象、字符串、数组等可以双向传递。
核心功能:
| 功能 | 描述 |
|---|---|
| 类型转换 | String、Vec<u8>、JsValue 等类型自动转换 |
| 结构体导出 | Rust 结构体可以作为 JavaScript 类使用 |
| 回调 | Rust 函数可以作为 JavaScript 回调传递 |
| 闭包 | 支持闭包在 JavaScript 和 Rust 之间传递 |
示例:
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) -> Point {
Point { x, y }
}
pub fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
(dx * dx + dy * dy).sqrt()
}
}在 JavaScript 中使用:
import init, { Point } from './pkg/wasm_demo.js';
await init();
const p1 = new Point(0, 0);
const p2 = new Point(3, 4);
console.log(p1.distance(p2)); // 5wasm-bindgen-futures
wasm-bindgen-futures 提供了 Rust Future 和 JavaScript Promise 之间的互操作能力,使得 Rust 代码可以在 Wasm 中执行异步操作。
核心功能:
JsFuture:将 JavaScriptPromise转换为 RustFuturefuture_to_promise:将 RustFuture转换为 JavaScriptPromisespawn_local:在当前执行上下文中启动一个Future
示例:
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::Response;
#[wasm_bindgen]
pub async fn fetch_data(url: &str) -> Result<String, JsValue> {
let window = web_sys::window().unwrap();
let resp_value = JsFuture::from(window.fetch_with_str(url)).await?;
let resp: Response = resp_value.dyn_into()?;
let text = JsFuture::from(resp.text()?).await?;
Ok(text.as_string().unwrap())
}web-sys 与 js-sys
web-sys 提供了 Web API 的 Rust 绑定,几乎涵盖了所有浏览器 API:
| 类别 | 包含的 API |
|---|---|
| DOM | document、element、event 操作 |
| Canvas | 2D 和 WebGL 渲染 |
| WebSocket | 实时通信 |
| Web Worker | 多线程 |
| Audio | 音频处理和合成 |
| Fetch | HTTP 请求 |
js-sys 提供了 JavaScript 核心对象和函数的绑定:
| 功能 | 对应 JavaScript |
|---|---|
js_sys::Date | Date 对象 |
js_sys::Math | Math 对象 |
js_sys::JSON | JSON.parse、JSON.stringify |
js_sys::Array | Array 对象 |
js_sys::Function | Function 对象 |
js_sys::Reflect | Reflect API |
示例——在 Canvas 上绘制:
use wasm_bindgen::prelude::*;
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement};
#[wasm_bindgen]
pub fn draw_circle(canvas: &HtmlCanvasElement) -> Result<(), JsValue> {
let ctx: CanvasRenderingContext2d = canvas
.get_context("2d")?
.unwrap()
.dyn_into()?;
ctx.begin_path();
ctx.arc(100.0, 100.0, 50.0, 0.0, std::f64::consts::PI * 2.0)?;
ctx.stroke();
Ok(())
}AssemblyScript
TypeScript 语法
AssemblyScript 是 TypeScript 的一个严格子集,专门用于编译为 WebAssembly。它保留了 TypeScript 的语法风格,但引入了针对 Wasm 的静态类型系统。
与标准 TypeScript 的关键区别:
| 特性 | TypeScript | AssemblyScript |
|---|---|---|
| 整数类型 | number | i32、i64、u32、u64 |
| 浮点类型 | number | f32、f64 |
| 整数大小 | 无 | 8/16/32/64 位 |
| 引用类型 | 对象 | String、Array |
| GC | 自动 | 手动引用计数 |
基本示例:
// assembly/index.ts
export function fibonacci(n: i32): i64 {
if (n <= 1) return n as i64;
let a: i64 = 0;
let b: i64 = 1;
for (let i = 2; i <= n; i++) {
let temp = a + b;
a = b;
b = temp;
}
return b;
}
export function greet(name: string): string {
return "Hello, " + name + "!";
}编译器
AssemblyScript 编译器基于 Binaryen 实现,分为前端和后端:
- 前端:解析 AssemblyScript(TypeScript 子集),生成 Binaryen IR
- 后端:Binaryen IR 经过优化后生成 Wasm 二进制格式
安装和使用:
# 安装
npm install --save-dev assemblyscript
# 初始化
npx asinit .
# 编译
npx asc assembly/index.ts -O3 -o build/optimized.wasm
# 生成绑定
npx asc assembly/index.ts -O3 --exportRuntime -o build/bundled.wasm编译选项:
| 选项 | 作用 |
|---|---|
-O3 | 激进优化 |
--exportRuntime | 导出运行时函数(内存分配) |
--bindings | 生成 JavaScript 绑定 |
--sourceMap | 生成源码映射 |
--optimize | 开启优化 |
在 JavaScript 中使用:
import loader from '@assemblyscript/loader';
const wasm = await loader.instantiate(
fetch('/build/optimized.wasm')
);
const result = wasm.exports.fibonacci(40);
console.log(result); // 102334155与 Rust Wasm 对比
| 维度 | AssemblyScript | Rust (wasm-pack) |
|---|---|---|
| 学习曲线 | 低(TypeScript 开发者友好) | 中等(需学习 Rust) |
| 类型系统 | 简化静态类型 | 丰富所有权系统 |
| 运行时开销 | 引用计数 GC | 无 GC(需手动管理) |
| 性能 | 较好 | 优秀 |
| 包体积 | 较小 | 最小(无运行时) |
| 生态成熟度 | 发展中 | 成熟 |
| DOM 访问 | 需手动绑定 | web-sys 自动生成 |
| 调试支持 | 良好(源码映射) | 一般 |
适用场景
AssemblyScript 最适合以下场景:
- TypeScript/JavaScript 开发者:无需学习新语言即可上手 Wasm
- 快速原型:开发速度快,迭代周期短
- 中小型项目:代码量不大,注重快速交付
- Web 原生应用:与现有 JavaScript 代码库无缝集成
- 教育用途:学习 Wasm 概念的理想起点
不适合的场景:
- 性能极致场景:Rust 或 C++ 通过 Emscripten 能获得更好的性能
- 大型项目:类型系统不如 Rust 严谨,难以维护大型代码库
- 复杂内存管理:引用计数 GC 在复杂场景下性能下降明显
Wasm 应用场景
图像处理
图像处理是 Wasm 的经典应用场景。像素级别的操作天然适合并行计算,Wasm 的 SIMD 指令可以大幅加速滤镜、色彩空间转换、缩放等操作。
典型应用:
- Canvas 滤镜增强:在 WebCodecs 和 Canvas 组合中,Wasm 处理像素数据
- 批量缩略图生成:上传前端生成不同尺寸的缩略图
- RAW 格式解码:相机 RAW 文件的浏览器端解码
- 图像格式转换:HEIF、WebP、AVIF 等格式的相互转换
性能数据:在 4K 分辨率(3840×2160)的图像上应用高斯模糊滤镜,Wasm 实现比纯 JavaScript 快约 2.5 倍。
音视频编解码
视频编解码是计算密集度最高的 Web 应用之一。通过 Wasm,可以将 FFmpeg、libvpx、x264 等原生编解码库移植到浏览器。
典型应用:
- 视频格式转换:MOV 转 MP4、AVI 转 WebM
- 实时视频处理:视频水印、裁剪、调速
- 流媒体转码:HLS/DASH 片段转码
- 音频效果处理:降噪、均衡器、变调
ffmpeg.wasm 是这一领域的典型项目,支持超过 100 种编解码格式,完全在浏览器端运行。
游戏引擎
Wasm 使 Unity、Unreal Engine、Godot 等游戏引擎能够将游戏部署到 Web 平台。游戏逻辑和渲染管道以 Wasm 形式运行,通过 WebGL/WebGPU 进行图形渲染。
主要平台:
- Unity WebGL:将 C# 游戏逻辑编译为 Wasm
- Unreal Engine:通过 Emscripten 编译 C++ 游戏代码
- Godot:原生支持导出为 Wasm
- 独立引擎:如 PlayCanvas、Amethyst 等
Wasm 技术使得 AAA 级别游戏(如《DOOM》《Quake》系列)能够在浏览器中以接近本地的性能运行。
科学计算
科学计算领域对计算性能有极高的要求,Wasm 为 Web 端的数值计算提供了可行方案。
应用方向:
- 物理模拟:刚体物理、流体动力学、N 体问题
- 分子动力学:蛋白质折叠模拟、药物分子筛选
- 地质建模:地震波传播模拟
- 金融计算:蒙特卡洛模拟、风险分析
- 机器学习推理:ONNX Runtime Web、TensorFlow.js WebAssembly 后端
TensorFlow.js 的 WebAssembly 后端在 CPU 推断场景下比纯 JavaScript 后端快 3 到 10 倍。
数据库
Wasm 使得在浏览器端运行完整的数据库引擎成为可能,为离线应用和本地数据处理开辟了新途径。
典型项目:
- SQLite WASM:SQLite 的官方 WebAssembly 版本
- DuckDB WASM:分析型数据库的 Wasm 移植
- LokiJS:内存数据库的 Wasm 加速
- PolarDB、TiDB 等分布式数据库的部分组件 Wasm 化
SQLite WASM
SQLite 官方提供了完整的 WebAssembly 支持,称为 SQLite WASM/JS。这是 SQLite 维护的官方项目,将 SQLite 引擎编译为 Wasm,并提供 JavaScript API。
核心特性:
| 特性 | 描述 |
|---|---|
| 完整的 SQL 支持 | 标准 SQL 语法、窗口函数、CTE |
| 事务 | ACID 事务支持 |
| 持久化 | 通过 OPFS (Origin Private File System) 持久化 |
| 内存模式 | 完全在内存中运行 |
| 并发控制 | 支持多个连接 |
基本使用:
// 使用 ESM 模块
import sqlite3Init from '@sqlite.org/sqlite-wasm';
const sqlite3 = await sqlite3Init();
const db = new sqlite3.oo1.DB(':memory:');
db.exec('CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY, name TEXT)');
db.exec("INSERT INTO users VALUES(1, 'Alice')");
const result = db.exec('SELECT * FROM users', { returnValue: 'resultRows' });
console.log(result); // [[1, 'Alice']]SQLite WASM 的应用场景包括:
- 离线 Web 应用:浏览器端存储和查询结构化数据
- PWA 数据同步:离线数据暂存,在线时同步到服务端
- 数据分析和转换:CSV/JSON 导入数据库进行分析
- 测试和演示:无需部署数据库即可运行 SQL
Wasm 限制
垃圾收集(GC)
Wasm MVP 不包含垃圾收集器,这意味着在 Wasm 中分配的内存必须由开发者手动管理。这带来了以下挑战:
- 内存泄漏风险:忘记释放内存会导致 WASM 线性内存耗尽
- 桥接开销:对于高级语言(如 Rust),需要在 Wasm 线性内存和 JS 堆之间复制数据
- 开发复杂:需要追踪对象的生命周期
解决方案正在推进中:WasmGC 提案(2023 年标准化)为 Wasm 引入了原生的 GC 支持,使 Kotlin、Java、Dart、Python 等带有 GC 的语言可以高效编译为 Wasm。
DOM 访问
Wasm 无法直接访问 DOM API。所有 DOM 操作都必须通过 JavaScript 中间层进行,这导致了:
- 性能损失:每一次 DOM 操作都需要跨越 Wasm-JS 边界
- 编码复杂:需要显式管理 DOM 引用的生命周期
- 接口设计受限:无法直接在 Wasm 中定义事件处理器
当前的最佳实践是将 UI 层保留在 JavaScript 中,Wasm 只负责计算逻辑。未来 Reference Types 提案和 DOM Access 提案将逐渐改善这一状况。
线程
Wasm MVP 不支持真正的多线程。虽然 Web Workers 可以创建并行执行环境,但:
- 共享内存:仅通过
SharedArrayBuffer实现有限共享 - 线程安全:缺乏原生的互斥锁和条件变量
- 跨线程通信:需要序列化/反序列化数据
Wasm 多线程提案正处于积极开发阶段,包括:
| 提案 | 状态 | 内容 |
|---|---|---|
| Threads | 已实现 | 基于 SharedArrayBuffer 的共享内存 |
| Atomics | 已标准化 | 原子操作和内存屏障 |
| Wait/Notify | 已标准化 | 线程同步原语 |
| Shared Memory | 已标准化 | 多线程共享线性内存段 |
调试
Wasm 的调试体验相比原生开发仍有差距:
- 符号信息:DWARF 调试信息支持尚不完善
- 源码映射:虽然支持,但复杂场景下容易失效
- 断点设置:某些浏览器的 Wasm 调试器功能有限
- 性能分析:Wasm 代码的性能剖析工具不如原生全面
- 堆栈跟踪:优化后的 Wasm 堆栈跟踪可能不准确
工具链在持续改进:Chrome DevTools 已经可以展示 Wasm 的 DWARF 源映射信息,Firefox 也提供了专属的 Wasm 调试面板。
包体积
虽然 Wasm 二进制格式本身紧凑(通常比等效的 JS 代码小 20-40%),但实际应用中包体积仍然是一个值得关注的方面:
- 运行时依赖:Emscripten 生成的胶水代码可能增加 50-200KB
- 标准库:C++ 标准库、Rust 标准库的一部分会被包含
- 多模块:多个 Wasm 模块可能导致重复的运行时代码
优化策略:
- Tree shaking:移除未使用的函数和数据
- 压缩:
.wasm文件使用 gzip/brotli 压缩(压缩率通常为 40-60%) - 延迟加载:按需加载 Wasm 模块
- 代码瘦身:使用
-O3 -s WASM=1 -s FILESYSTEM=0等编译选项
启动开销
Wasm 的启动过程包括以下几个阶段:
- 下载:通过网络下载
.wasm文件 - 解析:二进制格式解析
- 验证:类型安全验证
- 编译:编译为机器码
- 实例化:创建模块实例并初始化
相比 JavaScript,Wasm 的编译和验证阶段通常更快(二进制格式解析效率高),但实例化阶段可能产生额外开销。在移动设备上,Wasm 的编译时间可能达到桌面端的 2 到 4 倍。
改进方向:
- 流式编译:边下载边编译(
WebAssembly.instantiateStreaming) - 缓存:Wasm 模块的编译结果可以被浏览器缓存
- 预编译:Service Worker 中提前编译 Wasm 模块
Wasm 未来
WASI
WASI(WebAssembly System Interface)是 Wasm 的系统接口标准,旨在将 Wasm 扩展到浏览器之外。它定义了 Wasm 模块与操作系统交互的标准接口,包括:
| 接口 | 功能 |
|---|---|
wasi-filesystem | 文件系统访问 |
wasi-sockets | 网络套接字 |
wasi-random | 随机数生成 |
wasi-clocks | 时钟和时间 |
wasi-http | HTTP 请求和响应 |
wasi-io | 输入输出流 |
WASI 的典型应用场景:
- 服务端计算:在容器或 serverless 环境中运行 Wasm
- 边缘计算:CDN 边缘节点执行自定义逻辑
- 插件系统:安全地加载和运行第三方代码(如 Envoy 的 Wasm 扩展)
WASI Preview 2 已于 2024 年发布,引入了组件模型和资源类型。Preview 3 正在开发中,将进一步完善 I/O 模型。
WasmGC
WasmGC 提案为 Wasm 引入了原生的垃圾收集支持,使得带有 GC 的高级语言可以高效地编译为 Wasm。这些语言包括:
- Kotlin:Kotlin/Wasm 实验性支持
- Java:通过 GraalWasm 和 CheerpJ 支持
- Dart:Flutter Web Wasm 编译目标
- Python:Pyodide、MicroPython 的 Wasm 移植
- Go:Go 编译器的 Wasm 生成(使用 TinyGo)
WasmGC 为 Wasm 增加了以下类型:
- 结构体:带字段的结构类型
- 数组:带元素的数组类型
- 函数引用:引用函数的类型
- i31ref:31 位整数引用(优化小整数存储)
多线程提案
Wasm 的线程支持正在逐步完善。除了已有的 SharedArrayBuffer 和 Atomics 支持外,未来的提案包括:
- 线程创建和销毁:在 Wasm 中直接创建线程
- 共享线性内存:线程间安全的内存共享
- NUMA 感知:非一致性内存访问优化
- 硬件加速:利用 GPU 等异构计算资源
这些提案将使得 Wasm 成为真正意义上的多线程计算平台,大幅提升并行计算能力。
组件模型
Wasm 组件模型是 Wasm 生态系统的重大进化。它将 Wasm 模块抽象为可以相互组合的组件,每个组件具有明确定义的接口。
核心概念:
- 组件:一个或多个 Wasm 模块的集合,带有类型化的接口定义
- 接口:使用 WIT(Wasm Interface Type)语言定义
- 适配器:在组件之间转换接口的代码
- 组合:将多个组件组合为更大的组件
组件模型的意义:
- 模块复用:组件可以像 npm 包一样分发和组合
- 语言无关:不同语言编写的组件可以相互调用
- 安全边界:组件之间的调用受到严格限制
- 可移植性:组件在任何支持组件模型的 Wasm 运行时中运行
云原生
Wasm 在云原生领域展现出巨大的潜力,被称为"Serverless 2.0"的基石。与容器相比,Wasm 在云原生环境中的优势:
| 维度 | Docker 容器 | Wasm 模块 |
|---|---|---|
| 启动时间 | 100-500ms | 微秒级 |
| 镜像大小 | 50-500MB | 100KB-5MB |
| 安全沙箱 | 依赖内核隔离 | 纯沙箱设计 |
| 平台依赖 | 需要操作系统支持 | 平台无关 |
主要项目和运行时:
- WasmEdge:CNCF 托管的 Wasm 运行时,专注云原生和边缘计算
- Wasmtime:Bytecode Alliance 的参考实现
- Spin:Fermyon 的云原生 Wasm 框架
- WasmCloud:分布式 Wasm 运行时
- Envoy Wasm:服务网格的扩展机制
Wasm 在云原生领域的典型场景:
- Serverless 函数:毫秒级冷启动的函数计算
- Sidecar 代理:服务网格中的数据面扩展
- 流量处理:API 网关的自定义处理逻辑
- 数据处理管道:ETL 任务的轻量级执行环境
WebAssembly 正在从一个浏览器技术演进为通用的计算平台。随着 WASI、WasmGC、组件模型等关键技术的成熟,Wasm 将在浏览器之外的应用场景中发挥越来越重要的作用,成为连接不同语言、不同平台、不同运行时的通用计算层。
在线演示
以下是一个 WebAssembly 图片滤镜性能对比的交互式 Demo,展示了 JS 与 Wasm(模拟)在多种滤镜算法下的性能差异。您可以选择图片、切换滤镜类型,直观对比执行时间和像素处理速度。