这是从 SIMD 数组转换为常规数字类型的未定义行为吗?

Is this transmute from array of SIMD to regular numeric types undefined behaviour?

我有一个浮点值数组,我需要执行一些易于向量化的操作,例如对所有这些值求和,然后将它们全部除以该总和。我还需要访问(主要是读取)数组的各个元素。我想我可以使用 SIMD 类型来启用数组的矢量化。每当我需要对数组的各个元素做很多事情时,我会将数组转换为对常规浮点值数组的引用,然后访问该引用,如下所示:

extern crate simd;

use simd::x86::avx::f32x8;

fn main() {
    let values8: [f32x8; 100] = [f32x8::splat(1.1); 100];
    let values: &[f32; 800] = unsafe { std::mem::transmute(&values8) };

    println!("{}", values[333]);
}

这编译并且似乎工作得很好。但我担心这是未定义的行为,因为 I read 那:

Transmuting between non-repr(C) types is UB

我认为 SIMD 类型(如 simd::x86::avx::f32x8)是 repr(simd),我认为 [f32; 800] 也不是 repr(C)

我知道我可以在 SIMD 类型上使用 extract 方法来获取那些单独的浮点值,但使用上述转换为常规数组的方案会使代码更简单。

没有任何具体的保证(我找不到),我认为你不能断定它是安全的。

事实上,如上所述,由于略有不同的原因,它绝对是不安全的;您洗了对 values8 的引用,借用检查器未跟踪该引用;它让我这样做:

extern crate simd;

use simd::f32x4;

fn main() {
    let mut values8: [f32x4; 100] = [f32x4::splat(1.1); 100];
    let values: &[f32; 400] = unsafe { std::mem::transmute(&values8) };

    let t = &mut values8[4];
    println!("{}", values[333]); // but there's a mutable reference!
}

this Pre-RFC 中的早期定义表明

It is illegal to take an internal reference to the fields of a repr(simd) type, because the representation of booleans may require to change, so that booleans are bit-packed. The official external library providing SIMD support will have private fields so this will not be generally observable.

这显然会禁止将 simd 类型转换为数组。

实际的 RFC changed,显然你可以引用内部的东西。 RFC 还声明布局和对齐方式取决于平台。

由于我所知道的所有平台都不会向像 f32x8 这样的普通 simd 类型添加填充,您可以假设 f32x8 的布局是 "the same" 作为 [f32; 8] 因为它包含 8 f32s 紧密地打包在一个 32 字节的内存中。不过顺序可能是任意的。


"Transmuting between non-repr(C) types is UB" I think simd types (like simd::x86::avx::f32x8) are repr(simd) and I don't think [f32; 800] is repr(C) either.

从技术上讲,您既不是转换 repr(simd) 类型也不是 [f32; 800],您是将引用转换为另一个引用,但结果是相同的。


正如@Chris Emerson 所指出的,您示例中的不安全性来自生命周期链的中断。要恢复它,请创建一个抽象边界:

fn simd_slice(simd: &[f32x8; 100]) -> &[f32; 800] {
    unsafe { &*(simd as *const [f32x8; 100] as *const [f32; 800]) }
}