如何让gcc完全向量化这个sqrt循环?

5

如果我使用这段代码

#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位寄存器,但Cascade Lake支持AVX-512。有没有办法让GCC使用256位(YMM)或512位(ZMM)寄存器?

与之相比,ICC默认使用256个寄存器用于Cascade Lake:使用icc -c -S -O3 -march=cascadelake -qopenmp-simd编译会产生(Compiler Explorer)。

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

你可以添加选项-qopt-zmm-usage=high以使用512位寄存器(编译器浏览器

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

2
请注意,vrsqrt14pd是一种快速的近似倒数,是sqrt的近似值的一部分,如果您在循环中只执行这个操作(就像您的代码一样),那么它会更快。在实际应用中,请将sqrt作为其他计算的一部分来执行,以便它可以与其他活动的ALU重叠。 - Peter Cordes
2个回答

5

XMM寄存器是128位的

更糟糕的是,vsqrtsd甚至不是向量操作,这由末尾的sd(表示标量、双精度)所示。XMM寄存器也被标量浮点运算使用,但只有寄存器的低64或32位包含有用的数据,其余部分被清零。

缺失的选项是-fno-math-errno(该标志也隐含了-ffast-math,具有额外影响),以及(可选)-mprefer-vector-width=512

-fno-math-errno关闭数学运算中的errno设置,特别是对于平方根,这意味着负输入会导致NaN, 而不涉及errno设置为EDOM。ICC显然默认不关心这一点。

-mprefer-vector-width=512使自动向量化在合适时候偏好512位操作。默认情况下,256位操作被偏好,至少对于cascadelakeskylake-avx512和其他当前处理器而言,它可能不会在所有未来处理器上保持这种方式。


2
ICC 默认使用 -fp-model fast=1,有点类似于 gcc / clang 的 -ffast-math,包括将 FP 数学视为可结合的。所以,ICC 默认情况下不会关心设置 errno!(https://scicomp.stackexchange.com/q/20815),这就是为什么 OP 观察到它使用 vrsqrt14pd,这是一种快速的近似倒数。 - Peter Cordes

1
如果您添加 -ffast-math 标志,gcc 将使用 YMM 寄存器,例如:
vsqrtpd (%rdi,%rax), %ymm0
vmovupd %ymm0, (%rcx,%rax)

演示


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接