我正在为我的 opengl 项目制作自己的 linalg 库,并且正在考虑使用 simd 加速 matmul。
最小可重现示例:
use std::arch::x86_64::*;
#[derive(Debug, Clone, Copy)]
struct Mat4f {pub data: [[f32; 4]; 4]}
#[repr(align(16))]
struct AlignedF32([f32; 4]);
impl Mat4f {
pub fn new() -> Self {
Self {data: [[0_f32; 4]; 4]}
}
}
#[cfg(all(
any(target_arch = "x86", target_arch = "x86_64"),
target_feature = "sse"
))]
impl Mat4f {
pub fn mm_simd(&self, other: &Self) -> Self {
let a = &self.data; // transposing is omitted here
let b = &other.data;
let mut buffer = AlignedF32([0_f32; 4]);
let mut product: Mat4f = Mat4f::new();
unsafe {
for i in 0..4 {
for j in 0..4 {
let m1 = _mm_mul_ps(_mm_load_ps(b[i].as_ptr()), _mm_load_ps(a[j].as_ptr()));
let m2 = _mm_hadd_ps(m1, m1);
let m3 = _mm_hadd_ps(m2, m2);
_mm_store_ps(buffer.0.as_mut_ptr(), m3);
product.data[i][j] = buffer.0[0];
}
}
}
product
}
}
fn main() {
let a: Mat4f = Mat4f::new();
let b: Mat4f = a;
println!("{:?}", a.mm_simd(&b));
// crashes if you run the following line
// for i in 0..10{}
}
在循环内、循环之前或循环之后调用 a.mm_simd(&b)
时,会导致访问冲突,抛出 (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
。不确定 simd 对于 mat4x4 产品是否真的有必要。但令我困扰的是,一条不执行任何操作的附加行实际上会导致整个系统崩溃。
Mat4f
上没有强制对齐,因此使用指向其中包含的切片的参数来调用
_mm_load_ps(b[i].as_ptr())
是无效的,并且会调用 UB。这意味着虽然它可能按预期工作,但它也可能会出错或损坏你的记忆。 令我困扰的是,一条不执行任何操作的附加行实际上会导致整个系统崩溃
是的,这就是UB的本质,它可能会受到完全不相关的代码的影响。