_mm256_loadu_epi64、_mm256_storeu_epi64 需要 avx512vl?

_mm256_loadu_epi64, _mm256_storeu_epi64 require avx512vl?

第一次使用 avx2 内在函数(在支持 avx2 的系统上,但 avx512)。

无论是从原型还是我从 intel intrinsics 参考中获得的信息,我都认为 _mm256_loadu_epi64_mm256_storeu_epi64 是 avx512 函数。

但是如果我只用 -mavx2 编译代码,我会得到编译器错误。另一方面,如果我使用 -mavx512vl 进行编译(根据编译器错误的建议),它会编译并且 似乎 可以工作。但是,如果我选择 avx512...

,我当然会担心编译器在程序的其余部分可能会做什么

按照我认为应该为我的 avx2 机器编译的方式进行编译:

clang++ -std=c++17 -O2 -mavx2 -o storeload dummy.cpp
dummy.cpp:16:21: error: always_inline function
'_mm256_loadu_epi64' requires target feature 'avx512vl',
but would be inlined into function 'main' that is
compiled without support for 'avx512vl'
__m256i avx2reg = _mm256_loadu_epi64(&input[0]);
^
dummy.cpp:17:3: error: always_inline function
'_mm256_storeu_epi64' requires target feature 'avx512vl',
but would be inlined into function 'main' that is
compiled without support for 'avx512vl'
_mm256_storeu_epi64(&output[0],avx2reg);
^
2 errors generated.

编译但让我紧张:

clang++ -std=c++17 -O2 -mavx512vl -o storeload dummy.cpp

似乎有效:

./storeload
0x1111111111111111 == 0x1111111111111111 ?
0x2222222222222222 == 0x2222222222222222 ?
0x3333333333333333 == 0x3333333333333333 ?
0x4444444444444444 == 0x4444444444444444 ?

编译器是

clang --version
Debian clang version 11.0.1-2
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

测试代码为

#include <cstdint>
#include <array>
#include <cinttypes>
#include <iostream>
#include <immintrin.h>

int main(int argc, const char* argv[]) {
  std::array<uint64_t,4> input
    { UINT64_C(0x1111111111111111),
      UINT64_C(0x2222222222222222),
      UINT64_C(0x3333333333333333),
      UINT64_C(0x4444444444444444) };
  std::array<uint64_t,4> output;
  output.fill(UINT64_C(0));

  __m256i avx2reg = _mm256_loadu_epi64(&input[0]);
  _mm256_storeu_epi64(&output[0],avx2reg);

  std::cout << std::hex << std::showbase;
  
  for (size_t i=0; i < input.size(); i++) {
    std::cout << input[i] << " == " << output[i] << " ?" << std::endl;
  }
  
  return 0;
}

问题

使用 _mm256_loadu_si256((const __m256i*) ptr)_mm256_storeu_si256,另请参阅


那些具有更好 arg 类型(void* 而不是 __m256i*)的内在函数是与其他 AVX-512 内在函数一起引入的,但是执行 256 位加载的最有效方法是使用 AVX1 vmovdquvmovups(或任何指令的内存源操作数)。这就是为什么 clang 最终生成的代码可以 运行 在你的 CPU 上。 (用反汇编器或clang -march=native -O3 foo.cpp -S -o - | less检查asm输出)

不幸的是,clang 甚至不允许您在不启用 AVX-512VL 的情况下使用 void* 版本,因为它们不会做任何只能用 AVX-512 实现的事情;只有像 _mm256_mask_storeu_epi64 这样的 intrinsics for vmovdqu64 的屏蔽版本才真正有意义,其中 epi64 元素大小具有任何意义(屏蔽粒度)。

如果您的 CPU 不支持 ,那么使用 -mavx512vl 是安全的。 (Skylake-X、冰湖等)。 clang 可能已经决定实际使用它,例如使用 ymm15..31 避免 vzeroupper,或将一对按位布尔内在函数编译为 vpternlogd,或将 _mm256_set1_epi32 折叠为 vpaddd 的广播内存源操作数(_mm256_add_epi32 ).

或者作为错过的优化(更大的代码大小),实际使用 vmovdqu64 而不是 vmovdqu 来加载存储 ymm0..15。 GCC has/had 这个 bug 有一段时间了。

Why is the prefix _mm256 and not _mm512 on the functions I use?

AVX-512VL(VL=向量长度)的重点是 AVX-512 引入的很酷的新东西的 128 位和 256 位版本,例如屏蔽存储和屏蔽寄存器写入,矢量寄存器数量是原来的两倍,广播内存源操作数而不需要单独的 vpbroadcastd 加载等