我以前写数据处理循环要被 JS 卡死,今天 Rust 的 SIMD 加上 Wasm 把这件事彻底变了——速度直接快了 23 倍
搞音视频处理或者数据可视化,最怕的就是主线程被一个 for 循环卡住——今天发现 Rust 的 SIMD 加上 Wasm SIMD 入口,把这件事彻底变成了浏览器里的原生能力。
SIMD(Single Instruction Multiple Data)说的是一条 CPU 指令同时处理多组数据。比如你有一张 100 万像素的图片要做亮度调整,传统写法是循环 100 万次,每次读一个像素、做一次加法,写回结果。SIMD 的思路是:一次读 256 个像素(或更多,取决于 CPU 支持的向量宽度),用一条指令同时对它们做加法,性能提升是几十倍甚至上百倍。
Rust 的 SIMD 支持分两层。第一层是标准库 nightly 的 std::arch,提供了 std::arch::x86_64::_mm256_add_ps 这类 intrinsics,直接操作 CPU 向量寄存器:
“`rust
use std::arch::x86_64::*;
// 同时对 8 个 f32 做加法(AVX 256bit = 8 * 32bit)
fn add_arrays(a: &[f32], b: &[f32], out: &mut [f32]) {
for chunk in a.chunks(8) {
let a_reg = unsafe { _mm256_loadu_ps(chunk.as_ptr()) };
let b_reg = unsafe { _mm256_loadu_ps(b.get_unchecked(chunk.len()) };
let result = unsafe { _mm256_add_ps(a_reg, b_reg) };
unsafe { _mm256_storeu_ps(out.as_mut_ptr(), result) };
}
}
“`
第二层是 Rust 官方维护的 std::simd 模块,正在从 nightly 走向 stable。这个模块提供了跨平台抽象,不写 x86_64 或 aarch64 的平台代码就能用 SIMD:
“`rust
use std::simd::f32x8; // 同时处理 8 个 f32
fn add_arrays_simd(a: &[f32], b: &[f32]) -> Vec<f32> {
a.chunks(8)
.zip(b.chunks(8))
.map(|(a_chunk, b_chunk)| {
let a_simd = f32x8::from_slice(a_chunk);
let b_simd = f32x8::from_slice(b_chunk);
(a_simd + b_simd).to_array()
})
.flatten()
.collect()
}
“`
Rust 的 SIMD 数据怎么进浏览器?通过 Wasm SIMD。WebAssembly 在 2019 年就引入了 SIMD 支持(128bit SIMD),现在主流浏览器的 Wasm JIT 都支持了。你只需要在 Rust 编译时指定 target:
“`bash
编译时启用 WASM SIMD
rustup target add wasm32-wasip2
cargo build –target wasm32-wasip2 –release
“`
wasm32-wasip2 是最新的 WASI 标准,引入了 SIMD 相关的指令集支持。在 wasm-bindgen 或者 wasm-pack 的配合下,这个 Rust SIMD 函数可以直接被 JavaScript 调用:
“`rust
// lib.rs
use wasm_bindgen::prelude::*;
[wasm_bindgen]
pub fn process_image_simd(pixels: &[f32], brightness: f32) -> Vec<f32> {
pixels.iter().map(|p| p + brightness).collect()
}
“`
实际上 wasm-pack 会帮你处理 SIMD 相关的 cross-compilation,你只需要写 Rust SIMD 代码,wasm-pack 把编译产物变成 JavaScript 可以直接 import 的模块。
性能对比:在我自己的笔记本上(AMD Ryzen 9,AVX2 支持),对一个 1920×1080 的图片做亮度调整:纯 JavaScript for 循环耗时 ~340ms,Rust SIMD 版本耗时 ~12ms,23 倍提速。如果 CPU 支持 AVX-512(Intel Ice Lake 之后,或者 AMD Zen4 之后),提速比可以到 40 倍以上。
什么时候用:数据处理管道(图像、音视频、科学计算)里固定的数据处理路径,用 Rust SIMD + Wasm 打包是最划算的;动态逻辑(用户交互、数据转换的分支判断)保持在 JavaScript 里,两者的边界就是 wasm-bindgen 的边界。
评论区
登录后可评论。