跳到主要内容

WebAssembly 速查表

快速查阅 WebAssembly 常用语法、API 和最佳实践。

数据类型

值类型

类型说明字节数
i3232位整数4
i6464位整数8
f3232位浮点4
f6464位浮点8
v128128位向量16
funcref函数引用-
externref外部引用-

类型转换

指令说明
i32.wrap_i64i64 → i32
i64.extend_i32_si32 → i64 (有符号)
i64.extend_i32_ui32 → i64 (无符号)
f32.convert_i32_si32 → f32
f64.convert_i64_si64 → f64
i32.trunc_f32_sf32 → i32
i32.reinterpret_f32位重解释
f32.reinterpret_i32位重解释

WAT 语法

模块结构

(module
;; 导入
(import "env" "memory" (memory 1))
(import "env" "log" (func $log (param i32)))

;; 全局变量
(global $counter (mut i32) (i32.const 0))

;; 内存
(memory 1)

;; 表
(table 2 funcref)

;; 函数
(func $add (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add)

;; 导出
(export "add" (func $add))
(export "memory" (memory 0)))

函数定义

;; 基本函数
(func $name (param $x i32) (result i32)
local.get $x
i32.const 1
i32.add)

;; 多返回值
(func $swap (param $a i32) (param $b i32) (result i32 i32)
local.get $b
local.get $a)

控制流

;; if-else
(if (result i32)
(then i32.const 1)
(else i32.const 0))

;; block
(block $label (result i32)
i32.const 1
br $label)

;; loop
(loop $continue
;; 循环体
br $continue)

;; br_if
br_if $label

内存操作

;; 加载
i32.load
i32.load offset=4
i32.load8_s
i32.load16_u
i64.load
f32.load
f64.load

;; 存储
i32.store
i32.store8
i32.store16
i64.store
f32.store
f64.store

;; 内存大小
memory.size

;; 内存增长
memory.grow

JavaScript API

加载模块

// 流式加载(推荐)
const { instance } = await WebAssembly.instantiateStreaming(
fetch('module.wasm'),
importObject
);

// 从 ArrayBuffer 加载
const { instance } = await WebAssembly.instantiate(buffer, importObject);

// 分步加载
const module = await WebAssembly.compile(bytes);
const instance = await WebAssembly.instantiate(module, importObject);

Memory

// 创建内存
const memory = new WebAssembly.Memory({
initial: 1, // 初始页数
maximum: 10, // 最大页数
shared: false // 是否共享
});

// 访问缓冲区
const buffer = memory.buffer;
const view = new Int32Array(buffer);

// 增长内存
memory.grow(1);

// 获取大小
const pages = memory.buffer.byteLength / 65536;

Table

// 创建表
const table = new WebAssembly.Table({
initial: 2,
element: 'funcref'
});

// 操作
table.get(0);
table.set(0, func);
table.grow(1);
table.length;

Global

// 创建全局变量
const global = new WebAssembly.Global(
{ value: 'i32', mutable: true },
0
);

// 访问
global.value;
global.value = 42;

错误类型

WebAssembly.CompileError
WebAssembly.LinkError
WebAssembly.RuntimeError

Rust wasm-bindgen

基本注解

use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}

#[wasm_bindgen]
pub struct Counter {
count: i32,
}

#[wasm_bindgen]
impl Counter {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Counter { count: 0 }
}

pub fn increment(&mut self) -> i32 {
self.count += 1;
self.count
}
}

导入 JavaScript

#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);

#[wasm_bindgen(js_namespace = Math)]
fn random() -> f64;

type HTMLDocument;

#[wasm_bindgen(js_namespace = window)]
fn document() -> HTMLDocument;
}

类型映射

RustJavaScript
i32, u32Number
i64, u64BigInt
f32, f64Number
boolBoolean
&str, StringString
Vec<T>Array
JsValueany

Emscripten

编译命令

# 基本编译
emcc main.c -o main.html

# 导出函数
emcc main.c -s EXPORTED_FUNCTIONS="['_add','_multiply']"

# 优化
emcc main.c -O3 -o main.html

# 减小体积
emcc main.c -Os -s WASM=1 --closure 1

# 启用 SIMD
emcc main.c -msimd128 -o main.html

# 启用多线程
emcc main.c -pthread -s PTHREAD_POOL_SIZE=4

常用选项

选项说明
-s WASM=1输出 WebAssembly
-s EXPORTED_FUNCTIONS导出函数列表
-s EXPORTED_RUNTIME_METHODS导出运行时方法
-s ALLOW_MEMORY_GROWTH=1允许内存增长
-s NO_FILESYSTEM=1禁用文件系统
-s MINIMAL_RUNTIME=1最小运行时
--preload-file预加载文件
--bind启用 Embind

C/C++ 导出

#include <emscripten.h>

EMSCRIPTEN_KEEPALIVE
int add(int a, int b) {
return a + b;
}

Embind (C++)

#include <emscripten/bind.h>

class Counter {
public:
Counter() : count(0) {}
int increment() { return ++count; }
private:
int count;
};

EMSCRIPTEN_BINDINGS(module) {
emscripten::class_<Counter>("Counter")
.constructor()
.function("increment", &Counter::increment);
}

性能优化

编译优化

[profile.release]
opt-level = 3 # 速度优先
opt-level = "s" # 体积优先
lto = true # 链接时优化
codegen-units = 1 # 更好的优化
strip = true # 移除符号

减小体积

  • 使用 wee_alloc 替代默认分配器
  • 禁用不需要的功能
  • 使用 Closure Compiler
  • 压缩输出文件

运行时优化

  • 减少跨边界调用
  • 批量处理数据
  • 复用内存缓冲区
  • 使用 SIMD
  • 使用多线程

调试技巧

启用调试

# Rust
wasm-pack build --dev

# Emscripten
emcc main.c -g -o main.html

日志输出

// Rust
use web_sys::console;
console::log_1(&"Debug message".into());

// 或使用宏
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
// C
#include <emscripten.h>
emscripten_log(EM_LOG_CONSOLE, "Debug: %d", value);

浏览器调试

  • Chrome DevTools 支持 Wasm 调试
  • 可以设置断点
  • 可以查看 WAT 格式
  • Performance 面板分析性能

常见问题

内存增长后视图失效

// 错误
const view = new Int32Array(memory.buffer);
memory.grow(1);
view[0] = 42; // 可能出错

// 正确
let view = new Int32Array(memory.buffer);
memory.grow(1);
view = new Int32Array(memory.buffer); // 重新创建

字符串传递

// 写入字符串
function writeString(memory, str, offset) {
const encoder = new TextEncoder();
const bytes = encoder.encode(str + '\0');
new Uint8Array(memory.buffer).set(bytes, offset);
return offset;
}

// 读取字符串
function readString(memory, offset) {
const view = new Uint8Array(memory.buffer);
let end = offset;
while (view[end] !== 0) end++;
return new TextDecoder().decode(view.slice(offset, end));
}

避免内存泄漏

// 使用后释放
const ptr = Module._malloc(size);
try {
// 使用内存
} finally {
Module._free(ptr);
}

参考链接