【问题标题】:How can you get gcc to fully vectorize this sqrt loop?如何让 gcc 完全矢量化这个 sqrt 循环?
【发布时间】:2020-12-11 23:34:29
【问题描述】:

如果我接受此代码

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

相比之下,ICC 默认为 cascadelake 使用 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
...

【问题讨论】:

  • 请注意,vrsqrt14pd 是一个快速近似倒数,是 sqrt 近似的一部分,如果这就是你在循环中所做的所有事情(就像你的代码),它会更快。在现实生活中,将 sqrt 作为其他计算的一部分,这样它就可以与其他处于活动状态的 ALU 重叠。

标签: c++ gcc x86 icc auto-vectorization


【解决方案1】:

XMM 是 128 位寄存器

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

缺少的选项是-fno-math-errno(此标志也由-ffast-math 隐含,具有附加效果)和(可选)-mprefer-vector-width=512

-fno-math-errno 关闭设置 errno 用于数学运算,尤其是平方根,这意味着负输入导致 NaN 没有设置 errnoEDOM。默认情况下,ICC 显然不关心这一点。

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

【讨论】:

  • ICC 默认为 -fp-model fast=1,有点像 gcc / clang -ffast-math,包括我认为将 FP 数学视为关联。所以,是的,ICC 不会默认设置errnoperformance of icc main.cpp == g++ -ffast-math main.cpp。这就是为什么 OP 使用 vrsqrt14pd 来看待它,这是一个快速的近似倒数。
【解决方案2】:

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

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

Demo

【讨论】:

    猜你喜欢
    • 2013-08-27
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 2011-06-28
    • 2016-07-06
    • 2018-12-08
    • 2016-06-03
    • 2019-03-21
    相关资源
    最近更新 更多