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

时间:2018-09-20 21:11:32

标签: rust compiler-optimization simd avx2

当我尝试storeload 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. 此修复程序是否可以推广到用户输入(例如:如果我想将字节片用作用户输入并执行相同的过程)

1 个答案:

答案 0 :(得分:3)

经过更深入的reading the docs处理后,很明显,我必须将主体提取到另一个函数中,并通过用AVX2对其进行注释来迫使该函数使用AVX2进行编译。

#[target_feature(enable = "avx2")]

或使用编译整个程序

RUSTFLAGS="-C target-feature=+avx2" cargo run --release

第一个选项更好,因为它可以保证函数中使用的SIMD指令被正确编译,只是在调用方使用is_x86_feature_detected!("avx2")之前,调用方要检查其CPU是否具有这些功能。所有这些都已记录在案,但是如果编译器可以警告“嘿,此函数使用AVX2指令,但未使用#[target_feature(enable = "avx2")]进行注释,并且该程序未在全局启用AVX2的情况下进行编译,那么这将是令人惊讶的,因此调用此函数是未定义的行为”。那样会让我头疼!

由于依靠不确定的行为是不好的,我们在操场上的初始程序应写为:

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. 不值得研究为什么调试版本会给出正确的结果,因为在家用计算机上以发行版运行它也会给出正确的结果(在某些情况下)。查看汇编程序可以发现LLVM一种或多种方法都进行了优化,但这并不是特别有见地。