为什么GCC无法向量化此函数并循环?

时间:2019-01-11 12:27:52

标签: c++ openmp vectorization simd

我正在尝试使功能启用SIMD并通过函数调用向量化循环。

#include <cmath>

#pragma omp declare simd
double BlackBoxFunction(const double x) {
    return 1.0/sqrt(x);
}

double ComputeIntegral(const int n, const double a, const double b) {
    const double dx = (b - a)/n;
    double I = 0.0;
    #pragma omp simd reduction(+: I)

    for (int i = 0; i < n; i++) {
      const double xip12 = a + dx*(double(i) + 0.5);
      const double yip12 = BlackBoxFunction(xip12);
      const double dI = yip12*dx;
      I += dI; 
  }
  return I;
}

对于上面的代码,如果我使用icpc进行编译:

icpc worker.cc -qopenmp -qopt-report=5 -c

opt-report报告显示函数和循环都已向量化。 但是,如果我尝试使用g++ 6.5进行编译:

g++ worker.cc -O3 -fopenmp -fopt-info-vec-missed -funsafe-math-optimizations -c

输出显示note:not vectorized: control flow in loop.note: bad loop form,并且无法对循环进行矢量化。

如何使用GCC对循环进行矢量化处理?

编辑:

如果我将函数写入单独的文件中,

worker.cc

#include "library.h"

double ComputeIntegral(const int n, const double a, const double b) {
    const double dx = (b - a)/n;
    double I = 0.0;
    #pragma omp simd reduction(+: I)

    for (int i = 0; i < n; i++) {
      const double xip12 = a + dx*(double(i) + 0.5);
      const double yip12 = BlackBoxFunction(xip12);
      const double dI = yip12*dx;
      I += dI; 
  }
  return I;
}

library.h

#ifndef __INCLUDED_LIBRARY_H__
#define __INCLUDED_LIBRARY_H__

#pragma omp declare simd
double BlackBoxFunction(const double x); 

#endif

library.cc

#include <cmath>

#pragma omp declare simd
double BlackBoxFunction(const double x) {
  return 1.0/sqrt(x);
}

然后我用GCC编译它:

g++ worker.cc library.cc -O3 -fopenmp -fopt-info-vec-missed -funsafe-math-optimizations -c

它显示:

worker.cc:9:31: note: loop vectorized

但是

library.cc:5:18: note:not vectorized: control flow in loop.
library.cc:5:18: note:bad loop form.

这让我感到困惑。我想知道它是否已经矢量化了。

1 个答案:

答案 0 :(得分:7)

在稍微修改一下代码之后,可以使用gcc进行矢量化:

#include <cmath>

double BlackBoxFunction(const double x) {
    return 1.0/sqrt(x);
}

double ComputeIntegral(const int n, const double a, const double b) {
    const double dx = (b - a)/n;
    double I = 0.0;
    double d_i = 0.0;
    for (int i = 0; i < n; i++) {
      const double xip12 = a + dx*(d_i + 0.5);
      d_i = d_i + 1.0;
      const double yip12 = BlackBoxFunction(xip12);
      const double dI = yip12*dx;
      I += dI; 
  }
  return I;
}

这是使用编译器选项-Ofast -march=haswell -fopt-info-vec-missed -funsafe-math-optimizations进行编译的。主循环编译为

.L7:
    vaddpd  ymm2, ymm4, ymm7
    inc     eax
    vaddpd  ymm4, ymm4, ymm8
    vfmadd132pd     ymm2, ymm9, ymm5
    vsqrtpd ymm2, ymm2
    vdivpd  ymm2, ymm6, ymm2
    vfmadd231pd     ymm3, ymm5, ymm2
    cmp     eax, edx
    jne     .L7

请参阅以下Godbolt link

我删除了#pragma omp ...,因为它们没有改善向量化,但也没有使向量化恶化。

请注意,仅将编译器选项从-O3更改为-Ofast是 足以实现矢量化。但是,使用double计数器比int计数器要有效,后者每次转换都会翻倍。

还请注意,矢量化报告具有很大的误导性。检查生成的汇编代码,以查看矢量化是否成功。