WebAssembly 速查表
快速查阅 WebAssembly 常用语法、API 和最佳实践。
数据类型
值类型
| 类型 | 说明 | 字节数 |
|---|---|---|
i32 | 32位整数 | 4 |
i64 | 64位整数 | 8 |
f32 | 32位浮点 | 4 |
f64 | 64位浮点 | 8 |
v128 | 128位向量 | 16 |
funcref | 函数引用 | - |
externref | 外部引用 | - |
类型转换
| 指令 | 说明 |
|---|---|
i32.wrap_i64 | i64 → i32 |
i64.extend_i32_s | i32 → i64 (有符号) |
i64.extend_i32_u | i32 → i64 (无符号) |
f32.convert_i32_s | i32 → f32 |
f64.convert_i64_s | i64 → f64 |
i32.trunc_f32_s | f32 → 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;
}
类型映射
| Rust | JavaScript |
|---|---|
i32, u32 | Number |
i64, u64 | BigInt |
f32, f64 | Number |
bool | Boolean |
&str, String | String |
Vec<T> | Array |
JsValue | any |
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);
}