WebAssembly做矩阵运算为什么比JavaScript还慢?
我用Rust编译了一个简单的矩阵乘法函数到WASM,本以为会比纯JS快,结果实测反而慢了将近一倍。是不是我哪里配置错了?
数据是100×100的浮点矩阵,JS版本用的是普通的for循环,WASM那边是通过wasm-bindgen传入Float64Array。加载和调用方式应该没问题,但性能就是上不去。
#[wasm_bindgen]
pub fn matmul(a: &[f64], b: &[f64], n: usize) -> Vec<f64> {
let mut c = vec![0.0; n * n];
for i in 0..n {
for j in 0..n {
for k in 0..n {
c[i * n + j] += a[i * n + k] * b[k * n + j];
}
}
}
c
}
核心问题在于你返回了
Vec,这意味着每次调用都要把WASM内存里的数据完整拷贝到JS侧。100x100的矩阵就是8万字节的数据复制,这个拷贝开销比实际计算还大。正确的做法是预先在JS侧分配好输出数组,传给WASM函数直接写入:
JS那边这样调用:
另外还有几个优化点:
第一,你的Rust代码是最朴素的三重循环,内存访问模式很差。矩阵乘法的标准优化是考虑缓存局部性,但这个属于进阶优化,先把数据拷贝问题解决了再考虑。
第二,如果追求极致性能,可以考虑用crate做循环展开或者SIMD优化,不过100x100的规模可能犯不上。
第三,wasm-bindgen默认的优化级别可能不够,编译时加
opt-level = 3和lto = true能提升明显。你先试试改成传参写入的方式,性能应该能上去。