c++ - 为什么 gcc 会用 _mm512_dpbusds_epi32 添加额外的 vmovdqa64 指令?

标签 c++ gcc intel avx512

g++正在生成额外的移动指令,我不知道为什么。 Godbolt link

这发生在 _mm512_dpbusds_epi32 附近固有的。该指令计算 8 位点积,然后将它们添加到打包的 32 位累加器(在本例中为饱和加法)。该指令有点不寻常,因为它同时读取和写入累加器。

当使用 gcc 编译时,编译器会在累加器上发出额外的移动指令 ( vmovdqa64 )。

这是一个累积一些点积的测试程序:

#include <immintrin.h>
#include <cstddef>
__m512i Slow(const __m512i *a, const __m512i b0, const __m512i b1, std::size_t count) {
  __m512i c0 = _mm512_setzero_epi32();
  __m512i c1 = _mm512_setzero_epi32();
  for (std::size_t i = 0; i < count; ++i) {
    c0 = _mm512_dpbusds_epi32(c0, a[i], b0);
    c1 = _mm512_dpbusds_epi32(c1, a[i], b1);
  }
  // Do not optimize away
  return _mm512_sub_epi32(c0, c1);
}

使用 g++ -O3 -mavx512vnni example.cc -S 编译时,这是主循环:
.L3:
  vmovdqa64 (%rdi), %zmm6
  vmovdqa64 %zmm3, %zmm0
  vmovdqa64 %zmm4, %zmm2
  addq  $64, %rdi
  vpdpbusds %zmm5, %zmm6, %zmm0
  vpdpbusds %zmm1, %zmm6, %zmm2
  vmovdqa64 %zmm0, %zmm3
  vmovdqa64 %zmm2, %zmm4
  cmpq  %rdi, %rax
  jne .L3


上面的程序集正在从 zmm3 复制一个累加器至 zmm0 , 更新 zmm0 ,并将其复制回 zmm3 .这是不必要的;它应该只使用 zmm0 之一或 zmm3作为蓄能器。

问题在 g++ (Gentoo 9.2.0-r2 p3) 9.2.0 上是一样的和 g++ (Ubuntu 8.4.0-1ubuntu1~18.04) 8.4.0 .
clang++ 9.0.1 避免了不必要的复制(它还展开了循环,但这里是最紧凑的版本。)
.LBB0_6:                                # =>This Inner Loop Header: Depth=1
  vmovaps (%rdi), %zmm4
  vpdpbusds %zmm0, %zmm4, %zmm3
  vpdpbusds %zmm1, %zmm4, %zmm2
  addq  $64, %rdi
  addq  $-1, %rax
  jne .LBB0_6

我能够解决 g++ 中的问题通过使用内联汇编。
#include <immintrin.h>
#include <cstddef>
__m512i Fast(const __m512i *a, const __m512i b0, const __m512i b1, std::size_t count) {
  __m512i c0 = _mm512_setzero_epi32();
  __m512i c1 = _mm512_setzero_epi32();
  for (std::size_t i = 0; i < count; ++i) {
    asm ("vpdpbusds %2, %1, %0" : "+x"(c0) : "x"(a[i]), "mx"(b0));
    asm ("vpdpbusds %2, %1, %0" : "+x"(c1) : "x"(a[i]), "mx"(b1));
  }
  // Do not optimize away
  return _mm512_sub_epi32(c0, c1);
}

循环 g++Fast 生成好多了:
.L3:
#APP
# 7 "asm.cc" 1
  vpdpbusds (%rdi), %zmm3, %zmm0
# 0 "" 2
# 8 "asm.cc" 1
  vpdpbusds (%rdi), %zmm1, %zmm2
# 0 "" 2
#NO_APP
  addq  $64, %rdi
  cmpq  %rax, %rdi
  jne .L3

最佳答案

回答我自己的问题。这是一个错误 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94663可能在集成的寄存器分配器中。

关于c++ - 为什么 gcc 会用 _mm512_dpbusds_epi32 添加额外的 vmovdqa64 指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61308501/

相关文章:

c++ - 矩阵和 vector 乘法

c++ - 创建全局变量导致链接器错误

c++ - 使用 glut 和 std::string 时 main() 之前的段错误?

c++ - 转换重载函数不明确

c++ - Intel Core i7-3770 上的 ACML 5.3.1 段错误

c++ - cppcheck 警告列表初始化中指向局部变量的指针

c - outb 宏中的 GCC 错误消息

c++ - 如何优化数组的重新排序?

opencv - 使用 TBB 并行化 OpenCV 代码

linux - 什么是英特尔微码?