我有一个
function
以将大小为MxK和KxN的两个16x16矩阵相乘在一起。教科书矩阵乘法。编译时使用
--release -C target-cpu=native
在支持AVX512的CPU上。
其中一根轴已展开。看起来,自动矢量化应该很容易看到一个在内存中连续的浮点乘法,将其打包到
ymm
/
zmm
寄存器并使用进行压缩乘法
vmulps
,但它反而生成
vmulss
因此,它明显变慢了。
相关代码片段:
for h in 0..16 {
let aval = a[(v * n) + h];
let bvals = &b[(h * n)..(h * n + 16)];
g0 += aval * bvals[0];
g1 += aval * bvals[1];
g2 += aval * bvals[2];
g3 += aval * bvals[3];
g4 += aval * bvals[4];
g5 += aval * bvals[5];
g6 += aval * bvals[6];
g7 += aval * bvals[7];
g8 += aval * bvals[8];
g9 += aval * bvals[9];
g10 += aval * bvals[10];
g11 += aval * bvals[11];
g12 += aval * bvals[12];
g13 += aval * bvals[13];
g14 += aval * bvals[14];
g15 += aval * bvals[15];
}
虽然我知道这有点
命中和未命中
优化器是否会抓住机会生成更好的代码,我很困惑为什么优化器不会做上面预期的事情。
有趣的是,下面的替代方案,似乎是未优化的代码,导致优化器生成4倍更快的代码,使用
vmulps
,
vaddps
,等等。优化器在这段代码中看到了上面代码中没有看到的内容???
pub fn matmul(a: &[f32], b: &[f32], r: &mut [f32], m: usize, k: usize, n: usize) {
let a = &a[0..(m * k)];
let b = &b[0..(k * n)];
for t in 0..k {
for y in 0..m {
for x in 0..n {
r[(y * n) + x] += a[(y * n) + t] * b[(t * n) + x];
}
}
}
}
这是在不使用unsafe或显式使用intrinsic的情况下生成自己的快速矩阵多重函数的努力的一部分。不幸的是,上面的内容让我觉得很难用rustc进行逐步优化,而且这将是徒劳的。