WASM (WebAssembly) 本身并不完全保证确定性行为(deterministic behavior)。它是一种平台无关、沙箱安全的字节码格式,可作为多语言的编译目标。
WASM 的确定性分析 #
| 特性 | 是否 deterministic | 说明 |
|---|---|---|
| 数学运算(整数) | ✅ 是 | 行为固定、无副作用 |
| 浮点运算(默认) | ❌ 可能不是 | 因为 IEEE 754 中如 NaN 表现、舍入方式在不同平台可能略有差异 |
| 内存访问 | ✅ 是 | 线性内存、边界检查明确 |
| I/O 操作 | ❌ 不是 | 原生 Wasm 没有 I/O,依赖宿主环境(如 JS/操作系统)提供接口,因此不确定 |
| 多线程、并发执行 | ❌ 不是 | 线程调度可能非确定性,除非显式同步或禁用线程 |
| 外部函数调用(imports) | ❌ 依赖宿主 | 外部函数的行为由宿主定义,不受 Wasm 控制 |
实现确定性的方向 #
针对上述情况,可以从以下几个方向着手:
禁用浮点或使用软浮点实现 替代平台相关的硬件浮点操作,保证跨平台一致性。
不允许非确定性系统调用 比如
Date.now()、随机数、文件系统访问,这些必须从宿主侧显式注入。限制外部导入函数(imports)
Note
WARM 中已经解决。 所有导入函数必须有严格的接口定义和行为规范。
运行在特定的 deterministic runtime 中 例如,区块链中的 deterministic wasm VM(如 Polkadot 的
wasmi,EOS 的eos-vm)。编译器层面控制 比如使用 Rust 编译成 Wasm 时,可以启用
--deterministic或关闭不稳定特性。
IWASM (Interpreter for WebAssembly) #
| 特性 | 是否 deterministic | 说明 |
|---|---|---|
| 整数运算、内存访问 | ✅ 是 | 严格遵循 Wasm 规范,行为一致 |
| 浮点支持 | ⚠️ 否(部分平台可能有差异) | 虽然 iwasm 遵循 IEEE 754,但不同硬件浮点实现仍可能有微差异,尤其是 NaN 表现 |
| I/O 操作 | ❌ 否 | 通过 Host functions 提供,可能依赖主机环境 |
| Host imports | ❌ 否 | 开发者自定义,必须小心保证行为一致 |
| 多线程 | ❌ 否 | WAMR 支持多实例运行,但不是为了并发线程设计 |
| 可配置性 | ✅ 是 | 可配置以屏蔽或禁用不确定行为,比如关闭浮点支持、限制 imports |
如何让 iwasm 更 deterministic? #
- 禁止/禁用浮点操作
- 编译时用
-disable-fp或禁止浮点指令集。
- 编译时用
- 不允许使用系统随机数、时间等非确定性功能
- 不要引入如
clock_gettime,rand,gettimeofday等。
- 不要引入如
- Host functions 明确定义并强制规范化行为
- 不允许不同平台表现不一致。
- 使用 AOT 模式编译为统一平台的中间代码
- 防止解释器行为偏差。
- 使用内置的 sandbox host 环境
- 比如通过 iwasm 的 WASI 支持,仅允许有限功能。