第7章 JavaScript 交互
WebAssembly 与 JavaScript 的紧密集成是其强大之处。本章将深入探讨两种环境之间的数据传递、函数调用、内存共享等核心技术,以及性能优化的最佳实践。
WebAssembly JavaScript API
7.1.1 模块加载与实例化
WebAssembly 模块的加载和实例化是使用的第一步:
基础加载模式:
// 方法1:使用 WebAssembly.instantiate(推荐)
async function loadWasm() {
const wasmModule = await WebAssembly.instantiateStreaming(
fetch('module.wasm')
);
return wasmModule.instance.exports;
}
// 方法2:分步加载
async function loadWasmStep() {
const wasmBytes = await fetch('module.wasm').then(r => r.arrayBuffer());
const wasmModule = await WebAssembly.instantiate(wasmBytes);
return wasmModule.instance.exports;
}
// 方法3:预编译模块(适合重复使用)
async function loadWasmPrecompiled() {
const wasmBytes = await fetch('module.wasm').then(r => r.arrayBuffer());
const module = await WebAssembly.compile(wasmBytes);
const instance = await WebAssembly.instantiate(module);
return instance.exports;
}
带导入的模块加载:
// JavaScript 函数供 WebAssembly 调用
const importObject = {
env: {
// 数学函数
js_sin: Math.sin,
js_cos: Math.cos,
js_log: Math.log,
// 控制台输出
console_log: (value) => console.log('WASM:', value),
console_error: (ptr, len) => {
const memory = wasmInstance.exports.memory;
const message = new TextDecoder().decode(
new Uint8Array(memory.buffer, ptr, len)
);
console.error('WASM Error:', message);
},
// 内存分配辅助
js_malloc: (size) => {
// 简化的内存分配记录
console.log(`Allocating ${size} bytes`);
return 0; // 实际应该返回内存地址
},
// 时间函数
current_time_ms: () => Date.now(),
// 随机数生成
random: () => Math.random()
},
// 内存导入(如果需要)
memory: new WebAssembly.Memory({ initial: 256, maximum: 512 })
};
// 对应的 WAT 模块
const watSource = `
(module
;; 导入 JavaScript 函数
(import "env" "js_sin" (func $js_sin (param f64) (result f64)))
(import "env" "console_log" (func $console_log (param i32)))
(import "env" "current_time_ms" (func $current_time_ms (result f64)))
;; 导入内存
(import "env" "memory" (memory 256 512))
;; 使用导入函数的 WebAssembly 函数
(func $math_demo (param $angle f64) (result f64)
;; 记录计算开始
(call $console_log (i32.const 1))
;; 计算 sin(angle)
(call $js_sin (local.get $angle)))
(func $benchmark_demo (result f64)
(local $start_time f64)
(local $end_time f64)
(local $i i32)
(local $sum f64)
;; 记录开始时间
(local.set $start_time (call $current_time_ms))
;; 执行一些计算
(loop $calc_loop
(if (i32.ge_s (local.get $i) (i32.const 1000000))
(then (br $calc_loop)))
(local.set $sum
(f64.add
(local.get $sum)
(call $js_sin (f64.convert_i32_s (local.get $i)))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $calc_loop))
;; 记录结束时间
(local.set $end_time (call $current_time_ms))
;; 返回耗时
(f64.sub (local.get $end_time) (local.get $start_time)))
(export "math_demo" (func $math_demo))
(export "benchmark_demo" (func $benchmark_demo))
(export "memory" (memory 0)))
`;
// 加载并实例化
async function loadWasmWithImports() {
const wasmBytes = await WebAssembly.wat2wasm(watSource);
const wasmModule = await WebAssembly.instantiate(wasmBytes, importObject);
return wasmModule.instance.exports;
}
7.1.2 异步加载和错误处理
class WasmLoader {
constructor() {
this.modules = new Map();
this.loading = new Map();
}
// 单例加载,避免重复请求
async loadModule(name, wasmPath, importObject = {}) {
// 如果已经加载,直接返回
if (this.modules.has(name)) {
return this.modules.get(name);
}
// 如果正在加载,等待完成
if (this.loading.has(name)) {
return await this.loading.get(name);
}
// 开始加载
const loadPromise = this._loadModuleInternal(name, wasmPath, importObject);
this.loading.set(name, loadPromise);
try {
const exports = await loadPromise;
this.modules.set(name, exports);
this.loading.delete(name);
return exports;
} catch (error) {
this.loading.delete(name);
throw error;
}
}
async _loadModuleInternal(name, wasmPath, importObject) {
try {
console.log(`Loading WASM module: ${name}`);
// 检查 WebAssembly 支持
if (!WebAssembly) {
throw new Error('WebAssembly not supported in this environment');
}
// 加载并实例化
const response = await fetch(wasmPath);
if (!response.ok) {
throw new Error(`Failed to fetch ${wasmPath}: ${response.statusText}`);
}
const wasmModule = await WebAssembly.instantiateStreaming(response, importObject);
console.log(`Successfully loaded WASM module: ${name}`);
return wasmModule.instance.exports;
} catch (error) {
// 如果 instantiateStreaming 失败,尝试传统方法
if (error.name === 'TypeError' && error.message.includes('streaming')) {
console.warn('Falling back to non-streaming WASM loading');
return await this._loadModuleFallback(wasmPath, importObject);
}
console.error(`Failed to load WASM module ${name}:`, error);
throw new Error(`WASM loading failed: ${error.message}`);
}
}
async _loadModuleFallback(wasmPath, importObject) {
const response = await fetch(wasmPath);
const wasmBytes = await response.arrayBuffer();
const wasmModule = await WebAssembly.instantiate(wasmBytes, importObject);
return wasmModule.instance.exports;
}
// 预加载多个模块
async preloadModules(modules) {
const loadPromises = modules.map(({ name, path, imports }) =>
this.loadModule(name, path, imports)
);
return await Promise.all(loadPromises);
}
// 获取已加载的模块
getModule(name) {
return this.modules.get(name);
}
// 检查模块是否已加载
hasModule(name) {
return this.modules.has(name);
}
// 卸载模块
unloadModule(name) {
this.modules.delete(name);
}
}
// 使用示例
const wasmLoader = new WasmLoader();
async function initializeApp() {
try {
// 定义要加载的模块
const modules = [
{
name: 'math',
path: './math.wasm',
imports: {
env: {
sin: Math.sin,
cos: Math.cos,
sqrt: Math.sqrt
}
}
},
{
name: 'image',
path: './image.wasm',
imports: {
env: {
memory: new WebAssembly.Memory({ initial: 64 })
}
}
}
];
// 预加载所有模块
await wasmLoader.preloadModules(modules);
console.log('All WASM modules loaded successfully');
// 使用模块
const mathModule = wasmLoader.getModule('math');
const result = mathModule.calculate(3.14159);
console.log('Calculation result:', result);
} catch (error) {
console.error('Failed to initialize app:', error);
// 实现降级方案
fallbackToJavaScript();
}
}
function fallbackToJavaScript() {
console.log('Using JavaScript fallback implementation');
// 纯 JavaScript 实现
}
数据类型转换
7.2.1 基本类型传递
WebAssembly 和 JavaScript 之间的数据类型转换:
// 对应的 WAT 模块
const basicTypesWat = `
(module
;; 基本数据类型函数
(func $add_i32 (param $a i32) (param $b i32) (result i32)
(i32.add (local.get $a) (local.get $b)))
(func $add_i64 (param $a i64) (param $b i64) (result i64)
(i64.add (local.get $a) (local.get $b)))
(func $add_f32 (param $a f32) (param $b f32) (result f32)
(f32.add (local.get $a) (local.get $b)))
(func $add_f64 (param $a f64) (param $b f64) (result f64)
(f64.add (local.get $a) (local.get $b)))
;; 类型转换示例
(func $int_to_float (param $x i32) (result f32)
(f32.convert_i32_s (local.get $x)))
(func $float_to_int (param $x f32) (result i32)
(i32.trunc_f32_s (local.get $x)))
;; 布尔逻辑(使用 i32)
(func $logical_and (param $a i32) (param $b i32) (result i32)
(i32.and (local.get $a) (local.get $b)))
(func $is_even (param $x i32) (result i32)
(i32.eqz (i32.rem_u (local.get $x) (i32.const 2))))
;; 多返回值(需要较新的 WebAssembly 版本)
(func $divmod (param $a i32) (param $b i32) (result i32 i32)
(i32.div_s (local.get $a) (local.get $b))
(i32.rem_s (local.get $a) (local.get $b)))
(export "add_i32" (func $add_i32))
(export "add_i64" (func $add_i64))
(export "add_f32" (func $add_f32))
(export "add_f64" (func $add_f64))
(export "int_to_float" (func $int_to_float))
(export "float_to_int" (func $float_to_int))
(export "logical_and" (func $logical_and))
(export "is_even" (func $is_even))
(export "divmod" (func $divmod)))
`;
// JavaScript 类型转换辅助工具
class TypeConverter {
constructor(wasmExports) {
this.wasm = wasmExports;
}
// 安全的整数转换
toWasmI32(jsValue) {
if (typeof jsValue === 'boolean') {
return jsValue ? 1 : 0;
}
const num = Number(jsValue);
if (!Number.isFinite(num)) {
throw new Error(`Cannot convert ${jsValue} to i32`);
}
// 确保在 32 位有符号整数范围内
return Math.trunc(num) | 0;
}
// BigInt 到 i64 转换
toWasmI64(jsValue) {
if (typeof jsValue === 'bigint') {
return jsValue;
}
if (typeof jsValue === 'number') {
if (!Number.isFinite(jsValue)) {
throw new Error(`Cannot convert ${jsValue} to i64`);
}
return BigInt(Math.trunc(jsValue));
}
return BigInt(jsValue);
}
// 浮点数转换
toWasmF32(jsValue) {
const num = Number(jsValue);
if (!Number.isFinite(num) && !Number.isNaN(num)) {
throw new Error(`Cannot convert ${jsValue} to f32`);
}
return Math.fround(num); // 强制转换为 32 位浮点
}
toWasmF64(jsValue) {
const num = Number(jsValue);
if (!Number.isFinite(num) && !Number.isNaN(num)) {
throw new Error(`Cannot convert ${jsValue} to f64`);
}
return num;
}
// 从 WASM 返回值转换
fromWasmI32(wasmValue) {
return wasmValue | 0; // 确保是 32 位整数
}
fromWasmI64(wasmValue) {
return BigInt.asIntN(64, wasmValue);
}
fromWasmF32(wasmValue) {
return Math.fround(wasmValue);
}
fromWasmF64(wasmValue) {
return wasmValue;
}
// 布尔值转换
toWasmBool(jsValue) {
return jsValue ? 1 : 0;
}
fromWasmBool(wasmValue) {
return wasmValue !== 0;
}
}
// 使用示例
async function demonstrateTypeConversion() {
const wasmModule = await WebAssembly.instantiate(
await WebAssembly.wat2wasm(basicTypesWat)
);
const converter = new TypeConverter(wasmModule.instance.exports);
const { exports } = wasmModule.instance;
// 基本运算
console.log('32-bit addition:', exports.add_i32(10, 20)); // 30
console.log('64-bit addition:', exports.add_i64(10n, 20n)); // 30n
console.log('32-bit float addition:', exports.add_f32(3.14, 2.86)); // 6.0
console.log('64-bit float addition:', exports.add_f64(3.14159, 2.71828)); // 5.85987
// 类型转换
console.log('Int to float:', exports.int_to_float(42)); // 42.0
console.log('Float to int:', exports.float_to_int(3.99)); // 3
// 布尔逻辑
console.log('Logical AND:', converter.fromWasmBool(exports.logical_and(
converter.toWasmBool(true),
converter.toWasmBool(false)
))); // false
console.log('Is even:', converter.fromWasmBool(exports.is_even(42))); // true
// 边界情况处理
try {
console.log('Large number conversion:', converter.toWasmI32(2**50));
} catch (error) {
console.warn('Conversion error:', error.message);
}
// 多返回值(如果支持)
if (exports.divmod) {
const [quotient, remainder] = exports.divmod(17, 5);
console.log(`17 ÷ 5 = ${quotient} remainder ${remainder}`); // 3 remainder 2
}
}
7.2.2 复杂数据结构
// 复杂数据结构的 WAT 模块
const structuresWat = `
(module
(memory (export "memory") 1)
;; 结构体操作:Point { x: f32, y: f32 }
(func $create_point (param $x f32) (param $y f32) (param $ptr i32)
(f32.store (local.get $ptr) (local.get $x))
(f32.store (i32.add (local.get $ptr) (i32.const 4)) (local.get $y)))
(func $get_point_x (param $ptr i32) (result f32)
(f32.load (local.get $ptr)))
(func $get_point_y (param $ptr i32) (result f32)
(f32.load (i32.add (local.get $ptr) (i32.const 4))))
(func $point_distance (param $p1 i32) (param $p2 i32) (result f32)
(local $dx f32)
(local $dy f32)
;; dx = p2.x - p1.x
(local.set $dx
(f32.sub
(f32.load (local.get $p2))
(f32.load (local.get $p1))))
;; dy = p2.y - p1.y
(local.set $dy
(f32.sub
(f32.load (i32.add (local.get $p2) (i32.const 4)))
(f32.load (i32.add (local.get $p1) (i32.const 4)))))
;; sqrt(dx*dx + dy*dy)
(f32.sqrt
(f32.add
(f32.mul (local.get $dx) (local.get $dx))
(f32.mul (local.get $dy) (local.get $dy)))))
;; 数组操作
(func $sum_array (param $ptr i32) (param $length i32) (result f32)
(local $sum f32)
(local $i i32)
(loop $sum_loop
(if (i32.ge_u (local.get $i) (local.get $length))
(then (br $sum_loop)))
(local.set $sum
(f32.add
(local.get $sum)
(f32.load
(i32.add
(local.get $ptr)
(i32.mul (local.get $i) (i32.const 4))))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $sum_loop))
local.get $sum)
(func $reverse_array (param $ptr i32) (param $length i32)
(local $i i32)
(local $j i32)
(local $temp f32)
(local.set $j (i32.sub (local.get $length) (i32.const 1)))
(loop $reverse_loop
(if (i32.ge_u (local.get $i) (local.get $j))
(then (br $reverse_loop)))
;; 交换 arr[i] 和 arr[j]
(local.set $temp
(f32.load
(i32.add (local.get $ptr) (i32.mul (local.get $i) (i32.const 4)))))
(f32.store
(i32.add (local.get $ptr) (i32.mul (local.get $i) (i32.const 4)))
(f32.load
(i32.add (local.get $ptr) (i32.mul (local.get $j) (i32.const 4)))))
(f32.store
(i32.add (local.get $ptr) (i32.mul (local.get $j) (i32.const 4)))
(local.get $temp))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(local.set $j (i32.sub (local.get $j) (i32.const 1)))
(br $reverse_loop)))
;; 字符串操作
(func $string_length (param $ptr i32) (result i32)
(local $len i32)
(loop $strlen_loop
(if (i32.eqz (i32.load8_u (i32.add (local.get $ptr) (local.get $len))))
(then (br $strlen_loop)))
(local.set $len (i32.add (local.get $len) (i32.const 1)))
(br $strlen_loop))
local.get $len)
(func $string_compare (param $str1 i32) (param $str2 i32) (result i32)
(local $i i32)
(local $char1 i32)
(local $char2 i32)
(loop $strcmp_loop
(local.set $char1 (i32.load8_u (i32.add (local.get $str1) (local.get $i))))
(local.set $char2 (i32.load8_u (i32.add (local.get $str2) (local.get $i))))
;; 如果字符不同,返回差值
(if (i32.ne (local.get $char1) (local.get $char2))
(then (return (i32.sub (local.get $char1) (local.get $char2)))))
;; 如果到达字符串末尾,返回 0
(if (i32.eqz (local.get $char1))
(then (return (i32.const 0))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $strcmp_loop))
i32.const 0)
(export "create_point" (func $create_point))
(export "get_point_x" (func $get_point_x))
(export "get_point_y" (func $get_point_y))
(export "point_distance" (func $point_distance))
(export "sum_array" (func $sum_array))
(export "reverse_array" (func $reverse_array))
(export "string_length" (func $string_length))
(export "string_compare" (func $string_compare)))
`;
// JavaScript 数据结构包装器
class StructureManager {
constructor(wasmExports, memory) {
this.wasm = wasmExports;
this.memory = memory;
this.heap = new Uint8Array(memory.buffer);
this.heapF32 = new Float32Array(memory.buffer);
this.heapI32 = new Int32Array(memory.buffer);
this.allocatedPointers = new Set();
this.nextPointer = 1024; // 从 1KB 开始分配
}
// 简单的内存分配器
allocate(bytes) {
const ptr = this.nextPointer;
this.nextPointer += Math.ceil(bytes / 4) * 4; // 4字节对齐
this.allocatedPointers.add(ptr);
// 检查内存溢出
if (this.nextPointer >= this.memory.buffer.byteLength) {
throw new Error('Out of WebAssembly memory');
}
return ptr;
}
// 释放内存
free(ptr) {
this.allocatedPointers.delete(ptr);
}
// Point 结构体包装
createPoint(x, y) {
const ptr = this.allocate(8); // 2 个 f32 = 8 字节
this.wasm.create_point(x, y, ptr);
return new PointWrapper(this, ptr);
}
// 数组包装
createFloatArray(jsArray) {
const ptr = this.allocate(jsArray.length * 4);
const wasmArray = new Float32Array(this.memory.buffer, ptr, jsArray.length);
wasmArray.set(jsArray);
return new FloatArrayWrapper(this, ptr, jsArray.length);
}
// 字符串包装
createString(jsString) {
const encoder = new TextEncoder();
const bytes = encoder.encode(jsString + '\0'); // 添加 null 终止符
const ptr = this.allocate(bytes.length);
const wasmBytes = new Uint8Array(this.memory.buffer, ptr, bytes.length);
wasmBytes.set(bytes);
return new StringWrapper(this, ptr);
}
// 从内存读取字符串
readString(ptr) {
const length = this.wasm.string_length(ptr);
const bytes = new Uint8Array(this.memory.buffer, ptr, length);
return new TextDecoder().decode(bytes);
}
// 清理所有分配的内存
cleanup() {
this.allocatedPointers.clear();
this.nextPointer = 1024;
}
}
// Point 包装器
class PointWrapper {
constructor(manager, ptr) {
this.manager = manager;
this.ptr = ptr;
}
get x() {
return this.manager.wasm.get_point_x(this.ptr);
}
get y() {
return this.manager.wasm.get_point_y(this.ptr);
}
distanceTo(other) {
return this.manager.wasm.point_distance(this.ptr, other.ptr);
}
toObject() {
return { x: this.x, y: this.y };
}
free() {
this.manager.free(this.ptr);
}
}
// 浮点数组包装器
class FloatArrayWrapper {
constructor(manager, ptr, length) {
this.manager = manager;
this.ptr = ptr;
this.length = length;
this.view = new Float32Array(manager.memory.buffer, ptr, length);
}
get(index) {
if (index < 0 || index >= this.length) {
throw new Error('Array index out of bounds');
}
return this.view[index];
}
set(index, value) {
if (index < 0 || index >= this.length) {
throw new Error('Array index out of bounds');
}
this.view[index] = value;
}
sum() {
return this.manager.wasm.sum_array(this.ptr, this.length);
}
reverse() {
this.manager.wasm.reverse_array(this.ptr, this.length);
return this;
}
toArray() {
return Array.from(this.view);
}
free() {
this.manager.free(this.ptr);
}
}
// 字符串包装器
class StringWrapper {
constructor(manager, ptr) {
this.manager = manager;
this.ptr = ptr;
}
length() {
return this.manager.wasm.string_length(this.ptr);
}
compareTo(other) {
return this.manager.wasm.string_compare(this.ptr, other.ptr);
}
toString() {
return this.manager.readString(this.ptr);
}
free() {
this.manager.free(this.ptr);
}
}
// 使用示例
async function demonstrateStructures() {
const wasmModule = await WebAssembly.instantiate(
await WebAssembly.wat2wasm(structuresWat)
);
const manager = new StructureManager(
wasmModule.instance.exports,
wasmModule.instance.exports.memory
);
try {
// Point 结构体
const point1 = manager.createPoint(3.0, 4.0);
const point2 = manager.createPoint(0.0, 0.0);
console.log('Point 1:', point1.toObject()); // { x: 3, y: 4 }
console.log('Point 2:', point2.toObject()); // { x: 0, y: 0 }
console.log('Distance:', point1.distanceTo(point2)); // 5.0
// 数组操作
const array = manager.createFloatArray([1.5, 2.5, 3.5, 4.5, 5.5]);
console.log('Original array:', array.toArray()); // [1.5, 2.5, 3.5, 4.5, 5.5]
console.log('Sum:', array.sum()); // 17.5
array.reverse();
console.log('Reversed array:', array.toArray()); // [5.5, 4.5, 3.5, 2.5, 1.5]
// 字符串操作
const str1 = manager.createString('Hello');
const str2 = manager.createString('World');
const str3 = manager.createString('Hello');
console.log('String 1 length:', str1.length()); // 5
console.log('String 1 content:', str1.toString()); // "Hello"
console.log('Compare Hello vs World:', str1.compareTo(str2)); // < 0
console.log('Compare Hello vs Hello:', str1.compareTo(str3)); // 0
// 清理内存
point1.free();
point2.free();
array.free();
str1.free();
str2.free();
str3.free();
} finally {
manager.cleanup();
}
}
内存共享
7.3.1 共享内存操作
// 高级内存管理的 WAT 模块
const memoryManagementWat = `
(module
(memory (export "memory") 16 256) ;; 1MB 初始,16MB 最大
;; 全局内存状态
(global $heap_base (mut i32) (i32.const 65536)) ;; 64KB 后开始堆
(global $heap_top (mut i32) (i32.const 65536))
(global $memory_size (mut i32) (i32.const 16)) ;; 当前页数
;; 内存统计
(func $get_heap_base (result i32)
(global.get $heap_base))
(func $get_heap_top (result i32)
(global.get $heap_top))
(func $get_heap_size (result i32)
(i32.sub (global.get $heap_top) (global.get $heap_base)))
(func $get_memory_pages (result i32)
(memory.size))
;; 简单的 bump 分配器
(func $malloc (param $size i32) (result i32)
(local $ptr i32)
(local $new_top i32)
;; 对齐到 8 字节边界
(local.set $size
(i32.and
(i32.add (local.get $size) (i32.const 7))
(i32.const 0xFFFFFFF8)))
;; 获取当前指针
(local.set $ptr (global.get $heap_top))
(local.set $new_top (i32.add (local.get $ptr) (local.get $size)))
;; 检查是否需要增长内存
(if (i32.gt_u (local.get $new_top) (i32.mul (memory.size) (i32.const 65536)))
(then
;; 计算需要的页数
(local.set $size
(i32.div_u
(i32.add (local.get $new_top) (i32.const 65535))
(i32.const 65536)))
;; 尝试增长内存
(if (i32.eq (memory.grow (i32.sub (local.get $size) (memory.size))) (i32.const -1))
(then (return (i32.const 0)))))) ;; 分配失败
;; 更新堆顶
(global.set $heap_top (local.get $new_top))
local.get $ptr)
;; 批量数据操作
(func $memcpy (param $dest i32) (param $src i32) (param $size i32)
(memory.copy (local.get $dest) (local.get $src) (local.get $size)))
(func $memset (param $dest i32) (param $value i32) (param $size i32)
(memory.fill (local.get $dest) (local.get $value) (local.get $size)))
(func $memcmp (param $ptr1 i32) (param $ptr2 i32) (param $size i32) (result i32)
(local $i i32)
(local $byte1 i32)
(local $byte2 i32)
(loop $cmp_loop
(if (i32.ge_u (local.get $i) (local.get $size))
(then (br $cmp_loop)))
(local.set $byte1 (i32.load8_u (i32.add (local.get $ptr1) (local.get $i))))
(local.set $byte2 (i32.load8_u (i32.add (local.get $ptr2) (local.get $i))))
(if (i32.ne (local.get $byte1) (local.get $byte2))
(then (return (i32.sub (local.get $byte1) (local.get $byte2)))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $cmp_loop))
i32.const 0)
;; 内存模式填充
(func $pattern_fill (param $dest i32) (param $pattern i32) (param $pattern_size i32) (param $total_size i32)
(local $written i32)
(local $copy_size i32)
(loop $pattern_loop
(if (i32.ge_u (local.get $written) (local.get $total_size))
(then (br $pattern_loop)))
;; 计算这次要复制的大小
(local.set $copy_size
(select
(local.get $pattern_size)
(i32.sub (local.get $total_size) (local.get $written))
(i32.le_u
(i32.add (local.get $written) (local.get $pattern_size))
(local.get $total_size))))
;; 复制模式
(memory.copy
(i32.add (local.get $dest) (local.get $written))
(local.get $pattern)
(local.get $copy_size))
(local.set $written (i32.add (local.get $written) (local.get $copy_size)))
(br $pattern_loop)))
;; 内存检查和验证
(func $memory_checksum (param $ptr i32) (param $size i32) (result i32)
(local $checksum i32)
(local $i i32)
(loop $checksum_loop
(if (i32.ge_u (local.get $i) (local.get $size))
(then (br $checksum_loop)))
(local.set $checksum
(i32.add
(local.get $checksum)
(i32.load8_u (i32.add (local.get $ptr) (local.get $i)))))
(local.set $i (i32.add (local.get $i) (i32.const 1)))
(br $checksum_loop))
local.get $checksum)
(export "malloc" (func $malloc))
(export "get_heap_base" (func $get_heap_base))
(export "get_heap_top" (func $get_heap_top))
(export "get_heap_size" (func $get_heap_size))
(export "get_memory_pages" (func $get_memory_pages))
(export "memcpy" (func $memcpy))
(export "memset" (func $memset))
(export "memcmp" (func $memcmp))
(export "pattern_fill" (func $pattern_fill))
(export "memory_checksum" (func $memory_checksum)))
`;
// 高级内存管理器
class AdvancedMemoryManager {
constructor(wasmExports, memory) {
this.wasm = wasmExports;
this.memory = memory;
this.memoryViews = new Map();
this.allocations = new Map();
this.nextId = 1;
// 创建不同类型的视图
this.updateMemoryViews();
}
updateMemoryViews() {
const buffer = this.memory.buffer;
this.memoryViews.set('uint8', new Uint8Array(buffer));
this.memoryViews.set('uint16', new Uint16Array(buffer));
this.memoryViews.set('uint32', new Uint32Array(buffer));
this.memoryViews.set('int8', new Int8Array(buffer));
this.memoryViews.set('int16', new Int16Array(buffer));
this.memoryViews.set('int32', new Int32Array(buffer));
this.memoryViews.set('float32', new Float32Array(buffer));
this.memoryViews.set('float64', new Float64Array(buffer));
}
// 检查内存是否发生了增长
checkMemoryGrowth() {
const currentPages = this.wasm.get_memory_pages();
const currentSize = currentPages * 65536;
if (currentSize !== this.memory.buffer.byteLength) {
console.log(`Memory grew from ${this.memory.buffer.byteLength} to ${currentSize} bytes`);
this.updateMemoryViews();
}
}
// 分配内存并跟踪
allocate(size, description = '') {
this.checkMemoryGrowth();
const ptr = this.wasm.malloc(size);
if (ptr === 0) {
throw new Error(`Failed to allocate ${size} bytes`);
}
const id = this.nextId++;
this.allocations.set(id, {
ptr,
size,
description,
timestamp: Date.now()
});
return { id, ptr, size };
}
// 获取内存视图
getView(type, ptr, length) {
this.checkMemoryGrowth();
const view = this.memoryViews.get(type);
if (!view) {
throw new Error(`Unknown view type: ${type}`);
}
const elementSize = view.BYTES_PER_ELEMENT;
const start = Math.floor(ptr / elementSize);
return view.subarray(start, start + length);
}
// 写入 JavaScript 数组到 WebAssembly 内存
writeArray(jsArray, type = 'uint8') {
const allocation = this.allocate(jsArray.length * this.getTypeSize(type));
const view = this.getView(type, allocation.ptr, jsArray.length);
view.set(jsArray);
return allocation;
}
// 从 WebAssembly 内存读取到 JavaScript 数组
readArray(ptr, length, type = 'uint8') {
const view = this.getView(type, ptr, length);
return Array.from(view);
}
// 获取类型大小
getTypeSize(type) {
const sizes = {
'uint8': 1, 'int8': 1,
'uint16': 2, 'int16': 2,
'uint32': 4, 'int32': 4,
'float32': 4, 'float64': 8
};
return sizes[type] || 1;
}
// 内存操作包装
copy(destPtr, srcPtr, size) {
this.wasm.memcpy(destPtr, srcPtr, size);
}
fill(ptr, value, size) {
this.wasm.memset(ptr, value, size);
}
compare(ptr1, ptr2, size) {
return this.wasm.memcmp(ptr1, ptr2, size);
}
// 模式填充
fillPattern(destPtr, pattern, totalSize) {
// 先分配模式数据
const patternAlloc = this.writeArray(pattern);
this.wasm.pattern_fill(destPtr, patternAlloc.ptr, pattern.length, totalSize);
return patternAlloc;
}
// 计算校验和
checksum(ptr, size) {
return this.wasm.memory_checksum(ptr, size);
}
// 内存状态信息
getMemoryStatus() {
this.checkMemoryGrowth();
return {
heapBase: this.wasm.get_heap_base(),
heapTop: this.wasm.get_heap_top(),
heapSize: this.wasm.get_heap_size(),
memoryPages: this.wasm.get_memory_pages(),
totalMemory: this.memory.buffer.byteLength,
allocations: this.allocations.size,
allocatedBytes: Array.from(this.allocations.values())
.reduce((sum, alloc) => sum + alloc.size, 0)
};
}
// 分配统计
getAllocationStats() {
const stats = {
total: this.allocations.size,
totalBytes: 0,
byDescription: new Map()
};
for (const alloc of this.allocations.values()) {
stats.totalBytes += alloc.size;
const desc = alloc.description || 'unknown';
if (!stats.byDescription.has(desc)) {
stats.byDescription.set(desc, { count: 0, bytes: 0 });
}
const descStats = stats.byDescription.get(desc);
descStats.count++;
descStats.bytes += alloc.size;
}
return stats;
}
// 内存泄漏检测
detectLeaks(maxAge = 60000) { // 1分钟
const now = Date.now();
const leaks = [];
for (const [id, alloc] of this.allocations) {
if (now - alloc.timestamp > maxAge) {
leaks.push({ id, ...alloc, age: now - alloc.timestamp });
}
}
return leaks;
}
// 内存碎片分析
analyzeFragmentation() {
// 简化的碎片分析
const status = this.getMemoryStatus();
const usedRatio = status.heapSize / status.totalMemory;
const allocationCount = this.allocations.size;
return {
usedRatio,
allocationCount,
averageAllocationSize: allocationCount > 0 ? status.heapSize / allocationCount : 0,
fragmentation: allocationCount > 10 ? 'high' : allocationCount > 5 ? 'medium' : 'low'
};
}
}
// 使用示例
async function demonstrateAdvancedMemory() {
const wasmModule = await WebAssembly.instantiate(
await WebAssembly.wat2wasm(memoryManagementWat)
);
const memManager = new AdvancedMemoryManager(
wasmModule.instance.exports,
wasmModule.instance.exports.memory
);
console.log('Initial memory status:', memManager.getMemoryStatus());
// 分配一些数据
const dataAlloc = memManager.writeArray([1, 2, 3, 4, 5], 'uint32');
console.log('Allocated array:', dataAlloc);
// 读取数据
const readData = memManager.readArray(dataAlloc.ptr, 5, 'uint32');
console.log('Read back:', readData); // [1, 2, 3, 4, 5]
// 复制数据
const copyAlloc = memManager.allocate(20, 'copy of array');
memManager.copy(copyAlloc.ptr, dataAlloc.ptr, 20);
const copiedData = memManager.readArray(copyAlloc.ptr, 5, 'uint32');
console.log('Copied data:', copiedData); // [1, 2, 3, 4, 5]
// 填充模式
const patternAlloc = memManager.allocate(40, 'pattern filled');
memManager.fillPattern(patternAlloc.ptr, [0xAA, 0xBB], 40);
const patternData = memManager.readArray(patternAlloc.ptr, 40, 'uint8');
console.log('Pattern data:', patternData); // [0xAA, 0xBB, 0xAA, 0xBB, ...]
// 计算校验和
const checksum1 = memManager.checksum(dataAlloc.ptr, 20);
const checksum2 = memManager.checksum(copyAlloc.ptr, 20);
console.log('Checksums match:', checksum1 === checksum2); // true
// 内存状态
console.log('Final memory status:', memManager.getMemoryStatus());
console.log('Allocation stats:', memManager.getAllocationStats());
console.log('Fragmentation analysis:', memManager.analyzeFragmentation());
// 泄漏检测(这里应该没有泄漏)
setTimeout(() => {
const leaks = memManager.detectLeaks(1000); // 1秒
console.log('Memory leaks detected:', leaks);
}, 2000);
}
7.3.2 高性能数据传输
// 高性能数据传输示例
class HighPerformanceTransfer {
constructor(wasmExports, memory) {
this.wasm = wasmExports;
this.memory = memory;
this.transferBuffers = new Map();
this.compressionEnabled = false;
}
// 创建传输缓冲区
createTransferBuffer(name, size) {
const buffer = {
ptr: this.wasm.malloc(size),
size: size,
view: new Uint8Array(this.memory.buffer, this.wasm.malloc(size), size),
position: 0
};
if (buffer.ptr === 0) {
throw new Error(`Failed to allocate transfer buffer: ${size} bytes`);
}
this.transferBuffers.set(name, buffer);
return buffer;
}
// 批量传输数据
transferBatch(bufferName, dataChunks) {
const buffer = this.transferBuffers.get(bufferName);
if (!buffer) {
throw new Error(`Transfer buffer not found: ${bufferName}`);
}
const results = [];
let totalBytes = 0;
const startTime = performance.now();
for (const chunk of dataChunks) {
if (buffer.position + chunk.byteLength > buffer.size) {
// 缓冲区满了,处理当前内容
results.push(this.processBuffer(bufferName));
buffer.position = 0;
}
// 复制数据到缓冲区
buffer.view.set(new Uint8Array(chunk), buffer.position);
buffer.position += chunk.byteLength;
totalBytes += chunk.byteLength;
}
// 处理剩余数据
if (buffer.position > 0) {
results.push(this.processBuffer(bufferName));
}
const endTime = performance.now();
return {
results,
totalBytes,
transferTime: endTime - startTime,
throughput: totalBytes / (endTime - startTime) * 1000 // bytes/second
};
}
// 处理缓冲区数据
processBuffer(bufferName) {
const buffer = this.transferBuffers.get(bufferName);
const checksum = this.wasm.memory_checksum(buffer.ptr, buffer.position);
// 重置缓冲区位置
const processedBytes = buffer.position;
buffer.position = 0;
return {
bytes: processedBytes,
checksum: checksum
};
}
// 零拷贝数据访问
getZeroCopyView(type, ptr, length) {
const TypedArray = {
'uint8': Uint8Array,
'uint16': Uint16Array,
'uint32': Uint32Array,
'int8': Int8Array,
'int16': Int16Array,
'int32': Int32Array,
'float32': Float32Array,
'float64': Float64Array
}[type];
if (!TypedArray) {
throw new Error(`Unsupported type: ${type}`);
}
return new TypedArray(this.memory.buffer, ptr, length);
}
// 流式数据处理
async processStream(dataSource, chunkSize = 8192) {
const buffer = this.createTransferBuffer('stream', chunkSize * 2);
const results = [];
try {
const reader = dataSource.getReader();
let totalProcessed = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 确保缓冲区足够大
if (value.length > buffer.size) {
throw new Error('Chunk size exceeds buffer capacity');
}
// 如果缓冲区空间不足,先处理现有数据
if (buffer.position + value.length > buffer.size) {
results.push(this.processBuffer('stream'));
}
// 复制新数据
buffer.view.set(value, buffer.position);
buffer.position += value.length;
totalProcessed += value.length;
// 可选:定期处理缓冲区以避免内存压力
if (buffer.position >= chunkSize) {
results.push(this.processBuffer('stream'));
}
}
// 处理剩余数据
if (buffer.position > 0) {
results.push(this.processBuffer('stream'));
}
return {
success: true,
totalProcessed,
results
};
} catch (error) {
return {
success: false,
error: error.message,
results
};
}
}
// 并行数据处理
async processParallel(dataArrays, workerCount = 4) {
const chunkSize = Math.ceil(dataArrays.length / workerCount);
const promises = [];
for (let i = 0; i < workerCount; i++) {
const start = i * chunkSize;
const end = Math.min(start + chunkSize, dataArrays.length);
const chunk = dataArrays.slice(start, end);
if (chunk.length > 0) {
promises.push(this.processChunk(chunk, i));
}
}
const results = await Promise.all(promises);
return {
workerResults: results,
totalResults: results.reduce((sum, r) => sum + r.processedCount, 0),
totalTime: Math.max(...results.map(r => r.processingTime))
};
}
// 处理数据块
async processChunk(dataChunk, workerId) {
const bufferName = `worker_${workerId}`;
// 为这个工作器创建专用缓冲区
if (!this.transferBuffers.has(bufferName)) {
this.createTransferBuffer(bufferName, 64 * 1024); // 64KB
}
const startTime = performance.now();
const result = this.transferBatch(bufferName, dataChunk);
const endTime = performance.now();
return {
workerId,
processedCount: dataChunk.length,
processingTime: endTime - startTime,
throughput: result.throughput,
results: result.results
};
}
// 内存映射文件访问(模拟)
mapFile(fileBuffer, readonly = true) {
const size = fileBuffer.byteLength;
const ptr = this.wasm.malloc(size);
if (ptr === 0) {
throw new Error(`Failed to map file: ${size} bytes`);
}
// 复制文件数据到 WebAssembly 内存
const wasmView = new Uint8Array(this.memory.buffer, ptr, size);
wasmView.set(new Uint8Array(fileBuffer));
return {
ptr,
size,
view: readonly ? wasmView : new Uint8Array(this.memory.buffer, ptr, size),
readonly,
// 同步更改回原文件缓冲区(如果可写)
sync: () => {
if (!readonly && fileBuffer instanceof ArrayBuffer) {
new Uint8Array(fileBuffer).set(wasmView);
}
},
// 取消映射
unmap: () => {
// 在实际实现中这里应该调用 free
// this.wasm.free(ptr);
}
};
}
// 性能基准测试
benchmark() {
const testSizes = [1024, 8192, 65536, 524288]; // 1KB to 512KB
const results = [];
for (const size of testSizes) {
const testData = new Uint8Array(size);
// 填充测试数据
for (let i = 0; i < size; i++) {
testData[i] = i % 256;
}
// 测试传输性能
const startTime = performance.now();
const allocation = {
ptr: this.wasm.malloc(size),
size: size
};
const view = new Uint8Array(this.memory.buffer, allocation.ptr, size);
view.set(testData);
const checksum = this.wasm.memory_checksum(allocation.ptr, size);
const endTime = performance.now();
results.push({
size,
transferTime: endTime - startTime,
throughput: size / (endTime - startTime) * 1000,
checksum
});
}
return results;
}
}
// 使用示例
async function demonstrateHighPerformanceTransfer() {
// 假设已经加载了 WASM 模块
const wasmModule = await WebAssembly.instantiate(
await WebAssembly.wat2wasm(memoryManagementWat)
);
const transfer = new HighPerformanceTransfer(
wasmModule.instance.exports,
wasmModule.instance.exports.memory
);
// 创建测试数据
const testData = Array.from({ length: 10 }, (_, i) => {
const chunk = new Uint8Array(1024);
chunk.fill(i);
return chunk.buffer;
});
console.log('=== 批量传输测试 ===');
transfer.createTransferBuffer('test', 4096);
const batchResult = transfer.transferBatch('test', testData);
console.log('Batch transfer result:', batchResult);
console.log('=== 并行处理测试 ===');
const parallelResult = await transfer.processParallel(testData, 3);
console.log('Parallel processing result:', parallelResult);
console.log('=== 性能基准测试 ===');
const benchmarkResults = transfer.benchmark();
console.log('Benchmark results:', benchmarkResults);
// 性能分析
console.log('=== 性能分析 ===');
benchmarkResults.forEach(result => {
console.log(`Size: ${result.size} bytes`);
console.log(`Transfer time: ${result.transferTime.toFixed(2)} ms`);
console.log(`Throughput: ${(result.throughput / 1024 / 1024).toFixed(2)} MB/s`);
console.log(`Checksum: ${result.checksum}`);
console.log('---');
});
}
本章小结
通过本章学习,你已经全面掌握了:
- WebAssembly JavaScript API:模块加载、实例化和错误处理
- 数据类型转换:基本类型和复杂结构的双向转换
- 内存共享:高效的内存管理和数据传输技术
- 性能优化:零拷贝访问、批量处理和并行计算
这些技能为构建高性能的 WebAssembly 应用程序提供了强大的基础。
📝 进入下一步:第8章 从 C/C++ 编译
🎯 重点技能:
- ✅ JavaScript API 熟练运用
- ✅ 数据转换机制
- ✅ 内存管理策略
- ✅ 高性能数据传输
- ✅ 跨语言集成技巧