您如何才能让gcc完全矢量化此sqrt循环?

问题描述

如果我接受此代码

#include <cmath>

void compute_sqrt(const double* x,double* y,int n) {
  int i;
#pragma omp simd linear(i)
  for (i=0; i<n; ++i) {
    y[i] = std::sqrt(x[i]);
  }
}

并使用g++ -S -c -O3 -fopenmp-simd -march=cascadelake进行编译,然后在循环(compiler-explorer)中得到类似的指令

...
  vsqrtsd %xmm0,%xmm0,%xmm0
...

XMM是128位寄存器,但是层叠湖支持avx-512。有没有办法让gcc使用256(YMM)或512位(ZMM)寄存器?

通过比较,ICC认将256个寄存器用于级联:使用icc -c -S -O3 -march=cascadelake -qopenmp-simd编译会产生(compiler-explorer

...
  vsqrtpd 32(%rdi,%r9,8),%ymm1 #7.12
...

,您可以添加选项-qopt-zmm-usage=high以使用512位寄存器(compiler-explorer

...
  vrsqrt14pd %zmm4,%zmm1 #7.12
...

解决方法

XMM是128位寄存器

更糟糕的是,vsqrtsd甚至不是向量运算,如最后的sd所示(标量,双精度)。标量浮点运算也使用XMM寄存器,但是只有寄存器的低64位或32位包含有用的数据,其余的被清零。

缺少的选项是-fno-math-errno-ffast-math也暗示了此标志,它具有附加作用)和(可选)-mprefer-vector-width=512

-fno-math-errno会关闭数学运算(尤其是平方根)的设置errno,这意味着输入NaN errno设置为{{ 1}}。默认情况下,ICC显然并不关心这一点。

EDOM使自动矢量化在可行时更喜欢512位操作。默认情况下,至少对于-mprefer-vector-width=512cascadelake以及其他当前处理器而言,首选256位操作,它可能不会对所有将来的处理器保持这种状态。

,

如果添加-ffast-math标志,则gcc将使用YMM寄存器,例如:

vsqrtpd (%rdi,%rax),%ymm0
vmovupd %ymm0,(%rcx,%rax)

Demo