这个问题在这里已经有了答案:





SIMD code works in Debug, but does not in Release

(1 个回答)


2年前关闭。




当我尝试 storeload 256 位与 AVX2 256 位向量之间的往来时,我在 Release模式下没有收到预期的输出。

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

在 Debug模式下:

back_key: [1, 2, 3, 4]

在 Release模式下:

back_key: [1, 2, 0, 0]

后半部分没有被加载或存储,我无法弄清楚是哪个。

奇怪的是针对 native CPU 工作。在 Release模式下 + 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);
}
  • 为什么会这样?
  • 是否有针对此特定用例的修复程序?
  • 能否将此修复程序推广用于用户输入(例如:如果我想将字节切片作为用户输入并执行相同的过程)
  • 最佳答案

    在更彻底的 reading the docs 之后,很明显我必须将主体提取到另一个函数中,并通过注释来强制使用 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);
    }
    

    一些注意事项:
  • main 不可能是不安全的,因此不能用 target_feature 注释,所以需要提取到另一个函数
  • 这仍然假设运行代码的 x86_64 CPU 具有 avx 功能,因此请确保在调用
  • 之前检查
  • 不值得研究为什么调试版本会给出正确的结果,因为在我的家用电脑上运行它也会给出正确的结果(在某些咒语下)。查看程序集表明 LLVM 以一种或另一种方式进行了优化,但它并不是特别有见地。
  • 关于rust - 为什么在调试和 Release模式下存储和加载 AVX2 256 位向量会产生不同的结果?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52433389/

    10-12 22:54