为什么存储到 AVX2 256 位向量和从中加载在调试和发布模式下有不同的结果?

Why does storing to and loading from an AVX2 256bit vector have different results in debug and release mode?

当我尝试 store and load 256 位往返于 AVX2 256 位矢量时,我在发布模式下没有收到预期的输出。

use std::arch::x86_64::*;

fn main() {
    let key = [1u64, 2, 3, 4];
    let avxreg = unsafe { _mm256_load_si256(key.as_ptr() as *const __m256i) };
    let mut back_key = [0u64; 4];
    unsafe { _mm256_storeu_si256(back_key.as_mut_ptr() as *mut __m256i, avxreg) };
    println!("back_key: {:?}", back_key);
}

playground

在调试模式下:

back_key: [1, 2, 3, 4]

在发布模式中:

back_key: [1, 2, 0, 0]

后半部分既没有加载也没有存储,我不知道是哪一个。

奇怪的是针对原生 CPU 作品。在发布模式 + RUSTFLAGS="-C target-cpu=native"

back_key: [1, 2, 3, 4]

我什至试图通过强制对齐来摆脱 Clippy 错误(我不确定下面的代码是否被认为更正确)。

use std::arch::x86_64::*;

#[repr(align(256))]
#[derive(Debug)]
struct Key([u64; 4]);

fn main() {
    let key = Key([1u64, 2, 3, 4]);
    let avxreg = unsafe { _mm256_load_si256(&key as *const _ as *const __m256i) };
    let mut back_key = Key([0u64; 4]);
    unsafe { _mm256_storeu_si256((&mut back_key) as *mut _ as *mut __m256i, avxreg) };
    println!("back_key: {:?}", back_key);
}
  1. 为什么会这样?
  2. 是否有针对此特定用例的修复程序?
  3. 此修复是否可以针对用户输入进行推广(例如:如果我想将字节切片作为用户输入并执行相同的过程)

在更彻底 reading the docs 之后,很明显我必须将主体提取到另一个函数中,并通过用

注释来强制使用 AVX2 编译该函数
#[target_feature(enable = "avx2")]

或者用

编译整个程序
RUSTFLAGS="-C target-feature=+avx2" cargo run --release

第一个选项更好,因为它保证函数中使用的 SIMD 指令被正确编译,调用者只需在使用 is_x86_feature_detected!("avx2") 调用之前检查它们的 CPU 是否具有这些功能。所有这些都记录在案,但如果编译器可以用 "hey, this function uses AVX2 instructions, but was not annotated with #[target_feature(enable = "avx2")] and the program was not compiled with AVX2 enabled globally, so calling this function is undefined behavior" 发出警告,那就太棒了。它会让我省去很多麻烦!

由于依赖未定义的行为是不好的,我们在操场上的初始程序应该写成:

use std::arch::x86_64::*;

fn main() {
    unsafe { run() }
}

#[target_feature(enable = "avx2")]
unsafe fn run() {
    let key = [1u64, 2, 3, 4];
    let avxreg = _mm256_load_si256(key.as_ptr() as *const __m256i);
    let mut back_key = [0u64; 4];
    _mm256_storeu_si256(back_key.as_mut_ptr() as *mut __m256i, avxreg);
    println!("back_key: {:?}", back_key);
}

一些注意事项:

  1. main不能不安全,因此不能用target_feature注解,所以需要抽取到另一个函数
  2. 这仍然假定 x86_64 CPU 运行 代码具有 avx 功能,因此请确保在调用
  3. 之前进行检查
  4. 不值得研究为什么调试版本给出正确的结果,因为 运行 在我的家用计算机上发布的它也给出正确的结果(在某些咒语下)。查看汇编表明 LLVM 以一种或另一种方式进行了优化,但它并不是特别有见地。