Skip to main content

WebAssembly SIMD

SIMD

概览

本教程演示了如何使用互联网计算机 (IC) 的 WebAssembly SIMD 功能。

ICP 支持确定性的 WebAssembly SIMD,这是智能合约链上性能的一个重要里程碑,特别是对于人工智能、图像处理、游戏、科学等领域具有重要意义。

同时传统区块链操作的性能也有可能得到显著提升,例如奖励分配或加密操作都可能受益于新的 WebAssembly SIMD 指令。

什么是 WebAssembly SIMD

WebAssembly SIMD(单指令多数据)是一组定义在 WebAssembly 核心规范中的指令集,它包含 200 多条确定性矢量指令。通过并行处理,可以显著地加速某些在 ICP 容器中运行的特定任务。

note

WebAssembly SIMD 功能在所有的 ICP 节点上都可用。

带给开发者的益处

WebAssembly SIMD 的支持使得 ICP 在性能上达到了一个全新的水平。开发者可以:

  • 优化计算密集型任务
    识别容器代码中可以从 SIMD 指令中受益的部分,并针对性能进行代码优化。
  • 解锁新的可能性
    探索以前因处理能力受限而无法实现的新功能和复杂应用。
  • 构建面向未来的基础
    使得开发者处于区块链创新的前沿。

使用 WebAssembly SIMD

在智能合约中利用 WebAssembly SIMD 主要有两种方法:

  • 循环自动矢量化
    只需启用 WebAssembly SIMD 并重新编译项目,就有可能获得显著的性能提升。这种优化一般来说很简单、安全,并且可以通过一行代码来完成,但其结果在很大程度上取决于所使用的算法、库和编译器。

  • SIMD intrinsics 函数
    一些计算密集型的函数可以直接使用 SIMD 指令编写,这种方式可以充分发挥 SIMD 的潜力。但在许多情况下,一些核心容器算法可能都需要使用新指令重写。

循环自动矢量化

要利用循环自动矢量化,您可以为整个工作区全局启用 WebAssembly SIMD 功能,或为某些容器的特定函数局部启用。一旦这些指令对编译器可用,编译器就会自动将一些普通的循环转换为带有并行计算的循环。

虽然这种改动简单且不易出错,但最终结果取决于许多因素,如算法本身、编译器的优化选项和级别、项目依赖等。

示例

如果您想要为整个工作区及其所有依赖项全局启用 WebAssembly SIMD 指令,需要创建 .cargo/config.toml 文件并包含以下内容:

[build]
target = ["wasm32-unknown-unknown"]

[target.wasm32-unknown-unknown]
rustflags = ["-C", "target-feature=+simd128"]

如果您仅需要仅为容器的特定函数启用 WebAssembly SIMD 指令,请做如下修改:

#[target_feature(enable = "simd128")]
fn auto_vectorization() {
...
}

WebAssembly SIMD intrinsics 函数

WebAssembly SIMD 指令以 wasm32 平台的特定 intrinsics 函数的形式提供。如果您需要使用这些 intrinsics 函数,应按照前一节的说明启用 WebAssembly SIMD 指令。

示例

以下是一个简短的代码片段,演示了如何使用单个 SIMD 指令来相乘两个包含四个浮点元素的数组:

#[inline(always)]
#[target_feature(enable = "simd128")]
pub fn mul4(a: [f32; 4], b: [f32; 4]) -> [f32; 4] {
use core::arch::wasm32::*;

// Load the arrays `A` and `B` into the SIMD registers.
let a = unsafe { v128_load(a.as_ptr() as *const v128) };
let b = unsafe { v128_load(b.as_ptr() as *const v128) };

// Multiply elements of `A` and `B` using a single SIMD instruction.
let c = f32x4_mul(a, b);

// Store and return the result.
let mut res = [0.0; 4];
unsafe { v128_store(res.as_mut_ptr() as *mut v128, c) };
res
}

要求

note

使用 WebAssembly SIMD 功能需要 dfx 版本 0.20.2-beta.0 或更高版本。

该示例展示了利用 WebAssembly SIMD 指令的不同方法:将 Rust 循环自动矢量化和 SIMD intrinsics 函数分别用于矩阵乘法,这是一项机器学习和人工智能应用中的核心操作。该示例比较了各种 SIMD 优化技术及其潜在的性能提升效果。

按照如下命令进入包含项目文件的文件夹,并启动 IC 的本地开发环境。

cd examples/rust/simd

dfx stop
dfx start --clean

您会看到类似如下输出:

Running dfx start for version 0.22.0
Using the default configuration for the local shared network.
Initialized replica.
Dashboard: http://localhost:45111/_/dashboard

本地部署

请打开另一个命令行终端,执行以下脚本将您的容器部署到 IC 的本地开发环境:

cd examples/rust/simd

dfx deploy

您会看到类似如下输出:

Deployed canisters.
URLs:
Backend canister via Candid interface:
mat_mat_mul: http://127.0.0.1:4943/?canisterId=bd3sg-teaaa-aaaaa-qaaba-cai&id=bkyz2-fmaaa-aaaaa-qaaaq-cai

容器接口

容器 mat_mat_mul 提供以下接口:

  • naive_f32/naive_u32
    返回使用简单算法对矩阵 AB 进行 1K 次逐元素相乘的循环所使用的指令数。

  • optimized_f32/optimized_u32
    返回使用优化算法对矩阵 ABK x 4 packed slices 进行 1K 次逐元素相乘的循环所使用的指令数。

  • auto_vectorized_f32/auto_vectorized_u32
    返回使用 Rust 循环自动矢量化对矩阵 ABK x 4 packed slices 进行 1K 次逐元素相乘的循环所使用的指令数。

  • simd_f32
    返回使用 WebAssembly SIMD 指令对矩阵 ABK x 4 packed slices 进行 1K 次逐元素相乘的循环所使用的指令数。

您可以在前一节部署的 Backend canisterCandid UI URL 查看所有公开接口,如下所示:

  Backend canister via Candid interface:
mat_mat_mul: http://127.0.0.1:4943/?canisterId=...

运行

示例 1:浮点数矩阵乘法

该示例执行一个循环将矩阵 A 和 B 的 K x 4 packed slices 进行 1K 次逐元素相乘,并将不同版本的实现进行比较:

  • 使用优化算法
  • 启用了 Rust 自动矢量化的算法
  • 使用 WebAssembly SIMD 指令的算法
$ dfx canister call mat_mat_mul optimized_f32
(39_518_255 : nat64)
$ dfx canister call mat_mat_mul auto_vectorized_f32
(13_697_228 : nat64)
$ dfx canister call mat_mat_mul simd_f32
(13_697_228 : nat64)

在这个示例中,Rust 的自动矢量化在优化矩阵乘法方面表现出色,相比优化算法版本实现了 3 倍的性能提升!此外,它与手工编写的 WebAssembly SIMD 实现性能相当。

note

示例中 optimized_f32 的结果 39_518_255 是基于 dfx 0.22.0 所获得的,如果您基于 dfx 0.21.0 及之前版本运行则会得到 168_542_255 。这是因为 wasmtime 中一个已知 的 NaN 规范化问题已经得到修复,并包含在了在最新版本的 dfx 0.22.0 之中。具体的修改可以参考这里

示例 2:整型矩阵乘法

该示例执行一个循环将整数矩阵 A 和 B 的 K x 4 packed slices 进行 1K 次逐元素相乘,并将不同版本的实现进行比较:

  • 使用优化算法;
  • 启用了 Rust 自动矢量化化的算法
% dfx canister call mat_mat_mul optimized_u32
(32_342_253 : nat64)
% dfx canister call mat_mat_mul auto_vectorized_u32
(16_164_254 : nat64)

Rust 自动矢量化在这个示例中再次展示了其强大功能,自动矢量化后的整数矩阵乘法版本相比原始代码实现了约 2 倍的性能提升。

结论

WebAssembly SIMD 功能为互联网计算机(IC)解锁了新的可能性,尤其是在机器学习和人工智能领域中。

该示例展示了通过使用 Rust 的循环自动矢量化技术,在很小代价的情况下矩阵乘法的性能可以提高约 3 倍。示例也展示了整数运算的性能也能提高,尽管提升幅度只约为 2 倍。

note

在实际应用中,根据具体应用和所涉及的操作类型的不同,性能提升的效果也会有所不同。

有问题么?

欢迎大家随时向我们提出任何技术问题。我们会尽力尽快回应

  1. 上网比较方便的,在 ICP China 的 Twitter Community,用中文发帖提问

  2. 有经验的开发者在 DFINITY 基金会运营的开发者论坛上用英文提问

    info

    https://forum.dfinity.org/ ,可以 cue 我们:BenPaulVincentHerbert

  3. 深度 ICP 用户可以在 OpenChat 上的 IC123.xyz Community 里的频道 ic123.xyz 工作组 里提问题

    欢迎您申请 DFINITY 基金会的 Developer Grant 项目,加入互联网计算机的大家庭!