128位向量暴力美学:WASM-SIMD 重构音频核心
128条数据 lanes 并行碾压,WASM-SIMD 让 FFT 从 JS 的 1.8ms 暴跌到 0.15ms。 不是微优化,是量级跃迁。
标量思维的尽头
上一篇文章里,我们把音频处理核心从 JS 搬进了 WASM,零拷贝消除了 GC 抖动。 延迟从不可控降到了 0.15ms。但那只是稳定性的胜利,不是算力的终点。
真正的战场是 FFT——1024 点快速傅里叶变换。JS 版要 1.8ms,WASM 标量版 0.4ms, 而加上 SIMD 之后:0.15ms。12 倍碾压。
这 12 倍的差距来自哪里?来自一个物理事实:CPU 的向量寄存器宽 128 位,能同时处理 4 个 float32。标量代码一次算一个,SIMD 一次算四个。同样的循环,SIMD 的吞吐量是标量的 4 倍起步。
WASM-SIMD:128 位的暴力
WebAssembly SIMD 提案(v128)在 2023 年被所有主流浏览器支持。 它把 CPU 的 SSE/NEON 指令暴露给 WASM 模块,让你在浏览器里写出接近原生性能的向量代码。
在 Rust 里启用 SIMD 只需要两步:
# Cargo.toml
[target.wasm32-unknown-unknown]
rustflags = ["-C", "target-feature=+simd128"]
# 或者运行时检测
wasm-pack build --target web -- --cfg simd但 SIMD 不是魔法开关——它需要你亲手重写热路径。 FFT 的核心是旋转因子乘法和蝶形运算,每一步都可以向量化。
FFT 蝶形运算的向量暴力
FFT 的核心操作是蝶形运算(Butterfly): 两个复数相乘、相加、相减。每个复数占 2 个 float(实部+虚部), 4 个 float 刚好填满一个 v128 寄存器。
// Rust SIMD 蝶形运算 — WASM v128
use core::arch::wasm32::*;
fn butterfly_simd(
re0: f32, im0: f32, // 输入 A
re1: f32, im1: f32, // 输入 B
wr: f32, wi: f32, // 旋转因子
) -> (v128, v128) {
// B * W = (re1*wr - im1*wi, re1*wi + im1*wr)
let b = f32x4_splat(re1);
let w = f32x4(wr, wi, wr, wi);
let bw = i32x4_shuffle::<1, 3, 0, 2>(f32x4_mul(b, w));
// 一条指令完成 4 个乘法
let a = f32x4_splat(re0); // 实际是两个复数打包
let sum = f32x4_add(a, bw); // A + B*W
let diff = f32x4_sub(a, bw); // A - B*W
(sum, diff)
}看,一个 f32x4 就是 128 位,同时装 4 个 float。f32x4_mul 一条指令算 4 个乘法。i32x4_shuffle 一条指令完成交叉重排。 这就是暴力——不是算法层面的巧思,是硬件层面的蛮力。
性能实测:12 倍碾压
同一台机器,同一段 1024 点 FFT:
| 实现 | 单次耗时 | SIMD | vs JS |
|---|---|---|---|
| JavaScript (JIT) | ~1.8ms | 无 | 1x |
| Rust → WASM 标量 | ~0.4ms | 无 | 4.5x |
| Rust → WASM + SIMD | ~0.15ms | 128-bit | 12x |
JS 版 1.8ms 已经逼近 2.67ms 帧预算的红线,任何一次 GC 都会爆帧。 SIMD 版 0.15ms 留出 94% 的余量。12 倍不是微优化,是量级差距。
为什么不是 WebGPU?
有人会问:WebGPU 的 compute shader 不是更快吗?答案是:看场景。
WebGPU 的并行度远超 SIMD(上千个 workgroup),但它的调度延迟也高。 从 JS 提交 compute pass 到 GPU 执行,中间有 0.1-0.5ms 的调度开销。 对于 2.67ms 帧预算的实时音频,这个延迟不可接受。
WASM-SIMD 的优势是零调度开销:指令直接在 CPU 的向量寄存器上执行, 没有 GPU-CPU 同步的等待。在 128 点渲染量子的实时循环里,CPU SIMD 是唯一能在帧预算内完成 FFT 的方案。
下一站:实时频谱
SIMD 加速的 FFT 不仅是性能数字。它打开了一扇门——实时频谱分析。
0.15ms 的 FFT 意味着你可以在每个渲染量子里跑完变换, 实时输出频率域数据给可视化层。上面那个 3D 频谱瀑布图就是证明: 每一帧都是一次真实的 FFT,每一根柱子都是一个频段的能量。
这条路的尽头不是 FFT。是卷积混响、自适应滤波、实时降噪—— 所有需要重算力的 DSP 操作,在 WASM-SIMD 下都变得可行。 浏览器不再只是展示层,它是一个真正的实时信号处理器。
彩蛋:用暴力验证暴力
上面的 3D 频谱瀑布图不是静态图片。拨动引擎切换, 你会看到 JS 引擎在频谱上留下的断裂和空洞——那是 GC 停顿导致的丢帧。 切到 WASM-SIMD,频谱连续、完整、零丢失。
12 倍算力差距,不是跑分,是你眼前正在发生的事实。
2026-04-17 · diffserv.xyz