主题切换
WebAssembly 深度专题
预计阅读 25 分钟
版本信息
最后更新:2026-07-09 · 适用基础库:≥ 3.9.0(最低兼容 ≥ 2.30.0)· WASM ≥ 2.15.0 · 微信开发者工具:稳定版 1.06+
WebAssembly (WASM) 是现代浏览器的第四种标准语言(与 HTML/CSS/JS 并列)。在微信小游戏中,WASM 是 Unity WebGL 导出方案的核心技术,也能直接用于计算密集型任务。本章从前端视角深入讲解 WASM 在微信小游戏中的实用场景。
前端 WASM 经验 vs 游戏 WASM 应用
前端对比:Web WASM vs 小游戏 WASM
| 维度 | 前端 WASM 应用 | 微信小游戏 WASM |
|---|---|---|
| 典型场景 | 图片压缩、加密、SQLite | Unity 引擎运行时、物理计算、自定义渲染 |
| 加载方式 | WebAssembly.instantiateStreaming | 同标准 Web API |
| 内存限制 | 浏览器 Tab 内存(~2GB) | iOS 高性能 ~400MB / 普通 ~200MB |
| 编译工具链 | Emscripten / wasm-pack / AssemblyScript | 同左 |
| 调试 | Chrome DevTools WASM 调试 | 微信开发者工具支持有限 |
关键认知: 微信小游戏环境本质是一个精简的浏览器(基于 WebKit/Chromium),标准 WebAssembly API 基本都可用。特别在 iOS 高性能模式下,WASM 通过 JIT 编译可以达到接近原生的性能。
WASM 运行原理
与 JavaScript 引擎的关系
┌─────────────────────────────────────────────────┐
│ JavaScript 引擎 (V8/JSCore) │
│ │
│ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ JS 代码 │ │ WASM 模块 │ │
│ │ ↓ 解析 │ │ ↓ 解码(比 JS 解析更快) │ │
│ │ ↓ 编译 (JIT) │ │ ↓ 编译(AOT 风格优化) │ │
│ │ ↓ 优化 │ │ ↓ 执行 │ │
│ └──────────────┘ └─────────────────────────┘ │
│ ↕ 共享内存 (SharedArrayBuffer) │
└─────────────────────────────────────────────────┘WASM 相对于 JavaScript 的关键优势:
| 特性 | JavaScript | WebAssembly |
|---|---|---|
| 类型系统 | 动态类型(运行时检查) | 静态类型(编译时确定) |
| 解析速度 | 需解析 + AST + 字节码 | 直接解码为机器码(快 10-20x) |
| 优化预测 | 需 JIT 预热(类型收集) | 接近 AOT 编译(可预测) |
| 内存管理 | GC 自动管理 | 手动管理(线性内存) |
| 每帧性能 | 适合 UI 和轻量逻辑 | 适合计算密集型任务 |
微信小游戏中 WASM 的使用方式
方式一:Unity WebGL 导出(最常用)
无需手写 WASM 代码——Unity 的 WX-WASM-SDK 自动将 C# 游戏代码编译为 WASM。
typescript
// 微信侧适配代码
// 1. 将 Unity 构建产物放入小游戏分包
// 2. 初始化加载 WASM
// 3. WX-WASM-SDK 会自动创建并管理渲染 Canvas,无需使用 document.getElementById
// 注意:以下为 WX-WASM-SDK v1.x 的 API 示例
// v2.x 中 API 结构有所调整,请以官方文档为准:
// https://github.com/wechat-miniprogram/minigame-unity-webgl-transform
const unityContext = wx.createUnityInstance({
// WASM 文件路径
codeUrl: 'subpackages/unity/webgl.wasm.framework.unityweb',
dataUrl: 'subpackages/unity/webgl.data.unityweb',
// 框架 JS
frameworkUrl: 'subpackages/unity/webgl.wasm.framework.js',
})方式二:手动编译 C/C++/Rust → WASM
当你只需要一个计算密集的模块(如物理引擎、寻路算法),而非整个游戏引擎时:
Emscripten(C/C++)
c
// physics.c — 用 C 实现碰撞检测
#include <math.h>
#include <stdbool.h>
// 暴露给 JS 的函数
bool check_collision(float ax, float ay, float ar,
float bx, float by, float br) {
float dx = ax - bx;
float dy = ay - by;
float dist = sqrtf(dx * dx + dy * dy);
return dist < (ar + br);
}
// 批量碰撞检测(更体现 WASM 性能优势)
int batch_check(float* positions, int count, float radius) {
int collisions = 0;
for (int i = 0; i < count; i++) {
for (int j = i + 1; j < count; j++) {
float dx = positions[i*2] - positions[j*2];
float dy = positions[i*2+1] - positions[j*2+1];
if (sqrtf(dx*dx + dy*dy) < radius * 2) {
collisions++;
}
}
}
return collisions;
}编译命令:
bash
# 安装 Emscripten
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk && ./emsdk install latest && ./emsdk activate latest
# 编译 C 代码为 WASM
emcc physics.c -Os \
-s WASM=1 \
-s EXPORTED_FUNCTIONS='["_check_collision","_batch_check"]' \
-s EXPORTED_RUNTIME_METHODS='["cwrap","setValue","getValue"]' \
-o physics.jsJavaScript 侧加载:
typescript
// loadWasm.ts — 加载手动编译的 WASM 模块
async function loadPhysicsModule(): Promise<void> {
// ⚠️ 分包内的文件使用相对路径读取,不要用 USER_DATA_PATH
// 假设 game.json 中配置了分包: { "name": "wasm", "root": "subpackages/wasm/" }
const wasmPath = 'subpackages/wasm/physics.wasm'
const fs = wx.getFileSystemManager()
const bytes = await new Promise<ArrayBuffer>((resolve, reject) => {
fs.readFile({
filePath: wasmPath,
// 注意:分包内的 .wasm 文件使用代码包相对路径即可
// 如果需要从 CDN 加载,改用: wx.request({ url, responseType: 'arraybuffer' })
success: (res) => resolve(res.data as ArrayBuffer),
fail: reject,
})
})
const { instance } = await WebAssembly.instantiate(bytes, {
env: {
memory: new WebAssembly.Memory({ initial: 256, maximum: 512 }),
// 提供 C 标准库函数(如 malloc)
abort: () => console.error('WASM abort'),
},
})
// 调用 WASM 函数
const checkCollision = instance.exports.check_collision as Function
const result = checkCollision(0, 0, 10, 15, 0, 10)
console.log('碰撞检测结果:', result) // 1 = true
}方式三:Rust + wasm-bindgen
rust
// physics.rs — Rust 实现(比 C 更安全)
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub struct PhysicsWorld {
bodies: Vec<Body>,
}
#[wasm_bindgen]
impl PhysicsWorld {
pub fn new() -> PhysicsWorld {
PhysicsWorld { bodies: Vec::new() }
}
pub fn add_body(&mut self, x: f32, y: f32, radius: f32) {
self.bodies.push(Body { x, y, radius });
}
/// 检测所有碰撞对
pub fn detect_collisions(&self) -> Vec<usize> {
let mut result = Vec::new();
for i in 0..self.bodies.len() {
for j in (i + 1)..self.bodies.len() {
let a = &self.bodies[i];
let b = &self.bodies[j];
let dx = a.x - b.x;
let dy = a.y - b.y;
if (dx * dx + dy * dy) < (a.radius + b.radius).powi(2) {
result.push(i);
result.push(j);
}
}
}
result
}
}
struct Body {
x: f32, y: f32, radius: f32,
}编译:
bash
# 安装 wasm-pack
cargo install wasm-pack
# 编译为 WASM + JS 胶水代码
wasm-pack build --target webWASM 内存管理
WASM 的线性内存是一个连续的 ArrayBuffer,由 WASM 模块手动管理。
typescript
// 与 WASM 共享数据
const memory = new WebAssembly.Memory({ initial: 256 }) // 256 页 = 16MB
const wasmBuffer = new Float32Array(memory.buffer)
// JS → WASM:写入数据
for (let i = 0; i < 1000; i++) {
wasmBuffer[i] = Math.random() * 800 // 模拟位置数据
}
// 调用 WASM 函数处理数据
const result = (instance.exports.process_positions as Function)(0, 1000)
// WASM → JS:读取结果
const processedData = wasmBuffer.slice(result, result + 1000)内存注意事项
- 不要用 JS GC 管理 WASM 内存 — WASM 模块内分配的内存不受 JS GC 追踪,需手动释放
- 内存泄漏更难排查 — 每次调用 C 的
malloc必须有对应的free - SharedArrayBuffer — 微信小游戏中
SharedArrayBuffer需要特殊安全头(Cross-Origin-Opener-Policy),可能不可用
微信小游戏 WASM 性能对比
| 场景 | 纯 JS | WASM (C/Rust) | 性能提升 |
|---|---|---|---|
| 1000 个对象碰撞检测 | ~3.2ms | ~0.8ms | 4x |
| 10000 次三角函数运算 | ~1.5ms | ~0.3ms | 5x |
| A* 寻路 (100×100 网格) | ~12ms | ~2.5ms | 4.8x |
| 简单游戏逻辑(非计算密集) | ~0.5ms | ~0.5ms | 持平(WASM ↔ JS 调用有开销) |
何时使用 WASM
值得使用 WASM 的场景:
- 批量物理碰撞检测(数百个实体)
- 复杂的寻路/导航网格计算
- 自定义 Shader 的数学预处理
- 加密/压缩/哈希等算法
不值得使用 WASM 的场景:
- 简单的 UI 交互逻辑
- 需要频繁调用 Web API 的操作(WASM ↔ JS 边界开销高)
- 数据量较小的统计计算
- 已有性能良好的 JS 实现
结论: 微信小游戏大部分场景不需要手写 WASM。除非你使用 Unity(自动 WASM 导出)或者有明确的性能瓶颈(Profile 确认 JS 计算耗时 > 5ms),否则 JS 已经足够。
WASM SIMD 向量加速
SIMD(Single Instruction Multiple Data,单指令多数据) 允许一条指令同时操作多个数据——对游戏中的向量运算(碰撞检测、粒子更新、矩阵变换)有显著的加速效果。
原理
普通运算逐分量处理:
// 两个 4D 向量相加,需要 4 条加法指令
result.x = a.x + b.x
result.y = a.y + b.y
result.z = a.z + b.z
result.w = a.w + b.wSIMD 同时处理:
// 一条 SIMD 指令同时完成 4 个加法
result = SIMD_ADD(a, b) // 一次处理 128 bits (4 × f32)在 WASM 中启用 SIMD
bash
# Emscripten 编译时启用 SIMD
emcc physics.c -O3 \
-sWASM=1 \
-msimd128 \ # 启用 128-bit SIMD
-sEXPORTED_FUNCTIONS='["_batch_check_collision"]' \
-o physics-simd.wasmrust
// Rust 中使用 SIMD(通过 std::arch::wasm32)
use std::arch::wasm32::*;
#[target_feature(enable = "simd128")]
unsafe fn vec4_add_simd(a: v128, b: v128) -> v128 {
f32x4_add(a, b) // 单个指令完成 4 个 f32 加法
}性能提升
| 场景 | 无 SIMD | SIMD 加速 | 提升倍率 |
|---|---|---|---|
| 碰撞检测(1000 实体) | ~45ms | ~18ms | 2.5× |
| 粒子位置更新(10000 粒子) | ~80ms | ~25ms | 3.2× |
| 矩阵乘法(4×4) | ~0.3μs | ~0.08μs | 3.8× |
微信小游戏的 V8 引擎(Chrome 91+ 内核)已支持 WASM SIMD。大多数中高端设备(iPhone 8+、主流 Android)均可使用。
WASM 线程与 SharedArrayBuffer
基本原理
WebAssembly.Threads 允许 WASM 模块配合 Web Workers 进行多线程计算。然而,微信小游戏对线程支持有限:
| 能力 | 浏览器 | 微信小游戏 |
|---|---|---|
SharedArrayBuffer | ✅(需 COOP/COEP 头) | ⚠️ 受限,不一定可用 |
| Web Worker | ✅ | ✅ wx.createWorker() |
| WASM Threads | ✅ | ❌ 大多不支持 |
| Atomics(同步原语) | ✅ | ⚠️ 依赖 SharedArrayBuffer |
微信小游戏替代方案:Web Worker + 消息传递
对于计算密集型任务(如地图生成、AI 寻路预计算),可以使用 wx.createWorker() 在后台线程中执行,避免阻塞主线程的游戏循环:
typescript
// main.js — 主域
const worker = wx.createWorker('workers/ai-worker.js')
// 发送寻路请求到 Worker
function requestPathAsync(start: Point, end: Point): Promise<Point[]> {
return new Promise((resolve) => {
worker.onMessage((msg: any) => {
if (msg.type === 'pathResult') {
resolve(msg.path)
}
})
worker.postMessage({ type: 'findPath', start, end })
})
}
// ai-worker.js — Worker 线程
// 加载 WASM 寻路模块
let wasmInstance: any = null
// 在 Worker 中初始化 WASM
WebAssembly.instantiate(wasmBuffer, imports).then((result) => {
wasmInstance = result.instance
})
worker.onMessage((msg: any) => {
if (msg.type === 'findPath') {
// 在 Worker 线程中执行 WASM 计算(不阻塞主线程渲染)
const path = wasmInstance.exports.find_path(msg.start, msg.end)
worker.postMessage({ type: 'pathResult', path })
}
})Worker 的局限
- Worker 内无法直接调用
wxAPI(需要消息中转) - Worker 有独立的内存空间,WASM 模块需要分别在主域和 Worker 中加载
wx.createWorker()创建的 Worker 有最大并发数限制(通常 1-3 个)
wasm-opt 优化工具
Binaryen 是 WebAssembly 的编译器基础设施,其核心工具 wasm-opt 可以对已编译的 .wasm 文件进行后编译优化(post-compilation optimization),进一步减小体积和提升性能。
安装
bash
# macOS
brew install binaryen
# Linux
apt install binaryen
# 或通过 npm(Node.js 环境)
npm install -g binaryen基本用法
bash
# 基础优化(建议级别 -O3)
wasm-opt -O3 input.wasm -o output.wasm
# 激进优化(包括代码重排和内联)
wasm-opt -O4 input.wasm -o output.wasm
# 仅优化体积(牺牲少量性能)
wasm-opt -Oz input.wasm -o output.wasm
# 查看优化前后对比
echo "优化前: $(wc -c < input.wasm) bytes"
wasm-opt -O3 input.wasm -o output.wasm
echo "优化后: $(wc -c < output.wasm) bytes"
# 通常可缩减 15-30%常用优化 Pass
bash
# 针对特定场景的精细优化
wasm-opt input.wasm -o output.wasm \
--simplify-locals \ # 简化局部变量
--vacuum \ # 删除无用代码
--inline-main \ # 内联 main 函数
--duplicate-function-elimination \ # 合并重复函数
--converge # 多次迭代直到收敛集成到构建流程
json
// package.json — 构建脚本
{
"scripts": {
"build:wasm": "emcc src/physics.c -O3 -sWASM=1 -o build/physics.wasm",
"optimize:wasm": "wasm-opt -O3 build/physics.wasm -o dist/physics.wasm",
"build": "npm run build:wasm && npm run optimize:wasm"
}
}对于微信小游戏,wasm 文件通过 CDN 下发或放入分包。每缩减 100KB wasm 体积,用户下载等待时间减少约 0.5-1 秒(3G 网络环境),对转化率有显著影响。
📚 相关阅读
- 05 引擎对比与选型 — Unity WX-WASM-SDK 详解
- 08 性能优化与包体控制 — 计算密集型场景优化
- iOS 高性能模式 — iOS 下的 WASM 性能表现
WASM 流式编译
WebAssembly.instantiateStreaming 可以在下载 WASM 文件的同时进行编译——下载完即编译完,节省等待时间。
流式 vs 传统编译
typescript
// ===== 传统方式:下载 → 编译(串行,总耗时 = 下载 + 编译)=====
async function compileWasmTraditional(url: string): Promise<WebAssembly.Instance> {
const response = await fetch(url) // 步骤 1: 下载
const bytes = await response.arrayBuffer() // 步骤 2: 读为 ArrayBuffer
const module = await WebAssembly.compile(bytes) // 步骤 3: 编译
return await WebAssembly.instantiate(module, imports) // 步骤 4: 实例化
}
// ===== 流式方式:边下载边编译(并行,总耗时 ≈ max(下载, 编译))=====
async function compileWasmStreaming(url: string): Promise<WebAssembly.Instance> {
const { instance } = await WebAssembly.instantiateStreaming(fetch(url), imports)
return instance
// 原理:浏览器/运行时在接收 WASM 数据块的同时就把它们喂给编译引擎,
// 而不是等全部下载完才编译。
}微信小游戏中的流式编译
typescript
/**
* 微信小游戏 WASM 流式编译
*
* 微信环境的 wx.request + WebAssembly.instantiateStreaming 需要特殊处理,
* 因为 wx.request 返回的不是标准的 Response 对象。
*
* 方案:使用 ReadableStream 模拟 fetch response,传递给 instantiateStreaming
*/
async function wxWasmStreamingCompile(
url: string,
imports: WebAssembly.Imports
): Promise<WebAssembly.Instance> {
// 微信小游戏中,推荐方案:
// 1. 小 WASM(< 4MB):直接使用 WebAssembly.instantiate(编译很快,流式优势不明显)
// 2. 大 WASM(> 4MB):将 WASM 文件放入分包,使用文件系统读取 + WebAssembly.compile
// 对于 Unity/引擎导出的 WASM(通常 > 10MB),建议使用分包 + 本地文件读取:
const fs = wx.getFileSystemManager()
const arrayBuffer = fs.readFileSync(url) // 返回 ArrayBuffer
// 注意:WebAssembly.instantiate 在微信环境的 V8 引擎中原生支持
const module = await WebAssembly.compile(arrayBuffer)
const instance = await WebAssembly.instantiate(module, imports)
return instance
}流式编译适用性
- 小 WASM(< 2MB):流式编译的优势不明显,传统方式多耗的时间可以忽略不计。
- 大 WASM(> 10MB):如果通过 CDN 加载,流式编译可以显著减少等待时间。但如果放本地分包,文件系统读取速度远快于网络,传统编译也足够快。
- 微信限制:
wx.request的responseType: 'arraybuffer'模式下不存在原生ReadableStream,因此微信小游戏中instantiateStreaming的使用场景有限。
WASM GC 提案
WASM GC(Garbage Collection)是 WebAssembly 的一项重要提案,允许 WASM 模块直接持有和操作 JavaScript 对象的引用,无需手动序列化/反序列化。
为什么 WASM GC 重要
传统 WASM ↔ JS 交互:
WASM 模块 (i32/f64)
↕ 需要序列化/反序列化
JavaScript 对象 (Object, Array, String)
WASM GC 交互:
WASM 模块 (struct, array, anyref)
↕ 直接引用传递(零拷贝)
JavaScript 对象 (Object, Array, String)当前支持状态(2026 年)
| 环境 | WASM GC 支持 | 备注 |
|---|---|---|
| Chrome 119+ | ✅ 默认启用 | 2023 年 11 月起支持 |
| Firefox 120+ | ✅ 默认启用 | 2023 年底起支持 |
| Safari 17+ | ✅ 默认启用 | 2023 年起支持 |
| Node.js 22+ | ✅ 实验性支持 | 需 --experimental-wasm-gc 标志 |
| 微信小游戏 | ⚠️ 视 V8 内核版本而定 | 取决于微信内置 V8 版本 |
对小游戏开发者的意义
目前阶段:如果使用 C/C++(Emscripten)编译 WASM,GC 提案的影响有限——Emscripten 仍然使用传统的线性内存模型。WASM GC 主要对直接从高级语言(如 Kotlin/Wasm、Dart/WasmGC)编译到 WASM 的场景有重大影响。
建议:关注 WASM GC 的进展,但在微信小游戏的 WASM 开发中,目前仍以传统线性内存 + 手动内存管理为主。
调试 WASM 在微信开发者工具中
WASM 代码调试比普通 JS 代码更困难——编译后的二进制代码几乎不可读。以下是在微信环境下调试 WASM 的实用技巧。
1. 生成 Source Map
使用 Emscripten 编译时启用调试符号:
bash
# 编译时添加 -g 标志生成调试信息和 source map
emcc physics.c \
-g \ # 生成 DWARF 调试信息
-gseparate-dwarf \ # 将调试信息分离为 .debug.wasm 文件
-O2 \ # 优化级别(-g 与 -O2 可共存)
-s EXPORTED_FUNCTIONS='["_malloc","_free","_collisionCheck"]' \
-o physics.wasm2. 防御性调试(日志注入)
在 WASM 代码中加入诊断输出:
c
// physics.c — 通过 Emscripten 的 emscripten_log 输出到 JS 控制台
#include <emscripten/emscripten.h>
int collisionCheck(float x1, float y1, float r1, float x2, float y2, float r2) {
// 诊断日志(仅在调试构建时启用)
#ifdef DEBUG
EM_ASM({
console.log('[WASM Debug] collisionCheck called:',
'circle1=(', $0, ',', $1, ',', $2, ')',
'circle2=(', $3, ',', $4, ',', $5, ')'
);
}, x1, y1, r1, x2, y2, r2);
#endif
float dx = x2 - x1;
float dy = y2 - y1;
float dist = sqrtf(dx * dx + dy * dy);
return dist < (r1 + r2);
}3. 内存快照
typescript
// JS 端 — WASM 内存快照工具
function dumpWasmMemory(memory: WebAssembly.Memory, offset: number, length: number): void {
if (length > 1024) {
console.warn('内存快照需限制长度,仅显示前 1024 字节')
length = 1024
}
const buffer = new Uint8Array(memory.buffer, offset, length)
const hexLines: string[] = []
for (let i = 0; i < buffer.length; i += 16) {
const slice = Array.from(buffer.slice(i, i + 16))
const hex = slice.map((b) => b.toString(16).padStart(2, '0')).join(' ')
const ascii = slice.map((b) => (b >= 32 && b <= 126 ? String.fromCharCode(b) : '.')).join('')
hexLines.push(`${(offset + i).toString(16).padStart(8, '0')} ${hex.padEnd(48)} ${ascii}`)
}
console.log(
`WASM 内存快照 (offset: 0x${offset.toString(16)}, 长度: ${length}):\n${hexLines.join('\n')}`
)
}
// 使用示例 — 查看 WASM 线性内存中特定区域的数据
// dumpWasmMemory(wasmMemory, 1024, 256)4. 常见 WASM 调试陷阱
| 陷阱 | 表现 | 排查方法 |
|---|---|---|
| 内存越界 | 数据错乱或静默崩溃 | 在 C 代码中加入边界检查断言 |
| 类型不匹配 | NaN 或异常值 | 检查 JS→WASM 传参的类型(i32≠f64) |
| 内存泄漏 | JS Heap 持续增长 | 确认每次 malloc 都有对应的 free |
| 栈溢出 | "stack overflow" 错误 | 减少递归深度或调整 Emscripten 栈大小 |
WASM 调试原则
- 先 JS 后 WASM:算法先在纯 JS 中实现并验证,确认正确后再移植到 WASM。
- 最小化接口:JS 和 WASM 之间传递的数据尽量简单(数字数组而非复杂对象),减少接口层的 bug。
- 开关式调试:使用预处理宏
#ifdef DEBUG控制调试代码,避免影响发布版本性能。
📝 课后练习
练习 1:评估 WASM 是否适合你的游戏
题目: 你正在开发一款包含以下功能的休闲游戏。请判断每个功能是否适合用 WASM 实现,并说明理由:
- 排行榜排序(1000 条数据,按分数降序)
- 物理碰撞(每帧 200 个道具掉落,需检测与地面的碰撞)
- 每日任务重置逻辑(检查时间戳、更新任务状态)
- 礼包码校验(SHA256 哈希对比)
参考答案:
| 功能 | 是否用 WASM | 理由 |
|---|---|---|
| 排行榜排序 | ❌ 不需要 | 1000 条数据 JS 排序 < 1ms,WASM 边界开销反而更大 |
| 物理碰撞 | ⚠️ 视情况 | 200 个道具不算多,JS 实现通常够用。> 500 个时考虑 WASM |
| 每日任务 | ❌ 不需要 | 纯逻辑判断,非计算密集,JS 完全胜任 |
| 礼包码 | ✅ 建议 | SHA256 是标准算法,C/Rust 的 WASM 实现比纯 JS 快 5-10x |
练习 2:尝试编译一个简单的 WASM 模块
题目: 使用 AssemblyScript(TypeScript 的子集)编写一个简单的 WASM 模块,实现斐波那契数列计算,并在浏览器中测试 WASM 与 JS 版本的性能对比。
参考答案:
typescript
// fib.ts — AssemblyScript 代码
export function fib(n: i32): i32 {
if (n <= 1) return n
return fib(n - 1) + fib(n - 2)
}bash
# 安装 AssemblyScript
npm init && npm install --save-dev assemblyscript
npx asinit .
# 编译
npm run asbuildtypescript
// 在 JS 中加载并对比性能
const jsFib = (n: number): number => (n <= 1 ? n : jsFib(n - 1) + jsFib(n - 2))
async function benchmark() {
const wasmFib = (await import('./build/release')).fib
const n = 40
const jsStart = performance.now()
const jsResult = jsFib(n)
const jsTime = performance.now() - jsStart
const wasmStart = performance.now()
const wasmResult = wasmFib(n)
const wasmTime = performance.now() - wasmStart
console.log(`fib(${n}) — JS: ${jsTime.toFixed(1)}ms, WASM: ${wasmTime.toFixed(1)}ms`)
// 典型结果:JS ~1200ms, WASM ~200ms (6x 提升)
}