使用OpenMP停止GCC自动向量化(Using OpenMP stops GCC auto vec

2019-07-19 19:58发布

我一直在做我的代码能,是通过自动GCC矢量化,但是当我包括了-fopenmp标志似乎停止在自动向量化的所有尝试。 我现在用的是ftree-vectorize -ftree-vectorizer-verbose=5至vectorise和监控。

如果我不包括标志,它开始给我很多的信息,关于每个循环,如果是矢量化,为什么不。 编译器停止时,我尝试使用omp_get_wtime()函数,因为它不能被链接。 一旦标志被包括在内,它只是列出了每一个功能,并告诉我,它在它矢量化0环。

我读过这个问题已经提到了一些其他地方,但他们真的不来任何的解决方案: http://software.intel.com/en-us/forums/topic/295858 HTTP:// GCC。 gnu.org/bugzilla/show_bug.cgi?id=46032 。 OpenMP的是否有它自己的矢量化处理方式? 请问我需要明确地告诉它?

Answer 1:

有在这似乎已经在最近版本的GCC得到解决GCC vectoriser一个缺点。 在我的测试情况,GCC 4.7.2 vectorises成功以下简单的循环:

#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
   a[i] = b[i] + c[i] * d;

在同一时间GCC 4.6.1不和它抱怨,环路包含函数调用或无法分析的数据的引用。 在vectoriser的错误是由触发方式parallel for循环由GCC实现。 当OpenMP构造的处理和扩展,简单的循环代码转换成这种类似的东西:

struct omp_fn_0_s
{
    int N;
    double *a;
    double *b;
    double *c;
    double d;
};

void omp_fn_0(struct omp_fn_0_s *data)
{
    int start, end;
    int nthreads = omp_get_num_threads();
    int threadid = omp_get_thread_num();

    // This is just to illustrate the case - GCC uses a bit different formulas
    start = (data->N * threadid) / nthreads;
    end = (data->N * (threadid+1)) / nthreads;

    for (int i = start; i < end; i++)
       data->a[i] = data->b[i] + data->c[i] * data->d;
}

...

struct omp_fn_0_s omp_data_o;

omp_data_o.N = N;
omp_data_o.a = a;
omp_data_o.b = b;
omp_data_o.c = c;
omp_data_o.d = d;

GOMP_parallel_start(omp_fn_0, &omp_data_o, 0);
omp_fn_0(&omp_data_o);
GOMP_parallel_end();

N = omp_data_o.N;
a = omp_data_o.a;
b = omp_data_o.b;
c = omp_data_o.c;
d = omp_data_o.d;

4.7前在GCC的vectoriser未能vectorise该循环。 这不是OpenMP的特定问题。 人们可以很容易,没有OpenMP的代码复制它。 为了证实这一点,我写了下面的简单的测试:

struct fun_s
{
   double *restrict a;
   double *restrict b;
   double *restrict c;
   double d;
   int n;
};

void fun1(double *restrict a,
          double *restrict b,
          double *restrict c,
          double d,
          int n)
{
   int i;
   for (i = 0; i < n; i++)
      a[i] = b[i] + c[i] * d;
}

void fun2(struct fun_s *par)
{
   int i;
   for (i = 0; i < par->n; i++)
      par->a[i] = par->b[i] + par->c[i] * par->d;
}

人们预计,这两个代码(注意-在这里没有的OpenMP)应vectorise因以同样restrict用于指定不走样可能发生的关键字。 不幸的是,这不是用GCC <4.7的情况下-它成功地vectorises循环中fun1但未能vectorise在fun2引用同样的道理,当它编译OpenMP的代码。

这样做的原因是,vectoriser是无法证明par->d不位于存储器内par->apar->b ,和par->c点。 这并不总是与本案fun1 ,其中有两种可能情况:

  • d是如在寄存器中的值参数传递;
  • d作为堆栈上的值参数传递。

在x64系统System V的ABI责成第一几个浮点参数得到的XMM寄存器(YMM上启用AVX-CPU)的通过。 那怎么d得到在这种情况下通过,因此没有指针都不能指向它-循环被矢量化。 在x86系统上该参数传递到栈中,ABI任务,因此d可能被任何三个指针的别名。 事实上,GCC拒绝vectorise循环中fun1如果指令来产生与32位x86代码-m32选项。

GCC 4.7通过插入运行时检查来确保没有得到解决此d也不par->d得到走样。

摆脱d去除不可证明的非混叠和下面的OpenMP代码得到由GCC 4.6.1矢量化:

#pragma omp parallel for schedule(static)
for (int i = 0; i < N; i++)
   a[i] = b[i] + c[i];


Answer 2:

我会尽量简短地回答你的问题。

  1. OpenMP的是否有它自己的矢量化处理方式?

是的......但是从传入的OpenMP 4.0开始。 该链接贴上面提供了有关该构建一个很好的洞察力。 目前的OpenMP 3.1,在另一方面,是不是SIMD概念的“感知”。 什么在实践中发生的,因此(或者,至少,在我的经验)是每当OpenMP工作结构是在一个循环中使用自动向量化的机制被抑制。 总之这两个概念是正交的,你仍然可以从两个受益(见其他答案 )。

  1. 我是否需要明确地告诉它?

恐怕是的,至少在目前。 我开始重写所考虑的循环的方式,使得量化明确的(即我将使用在Intel平台,AltiVec技术在IBM等内部函数)。



Answer 3:

你问:“为什么启用OpenMP的时候GCC不能做量化?”。

看来,这可能是GCC的一个bug :) http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46032

否则,将OpenMP API可以引入依赖(控制或数据),以防止自动矢量。 要自动vertorize,给定的代码必须是数据/控制依赖免费的。 这有可能是使用OpenMP可能导致一些虚假的依赖。

注意:OpenMP的(之前4.0)是使用线程级并行,正交于SIMD /量化。 程序可以在同一时间同时使用OpenMP和SIMD并行。



Answer 4:

我碰到这个帖子跑,同时寻找有关GCC 4.9选项的OpenMP-SIMD,这应该激活的OpenMP 4的#pragma OMP SIMD不激活OMP并行(线程)的意见。 GCC的bugzilla pr60117(确认)示出了编译OMP防止发生不编译指示自动矢量的情况。

GCC不为即使在SIMD条款(平行区域可自动矢量化仅内部循环嵌套在并行下)向量化并行OMP。 我不知道这是可以建议实施的#pragma OMP并行的SIMD比ICC 14.0.2其他的编译器; 与其他的编译器,编码将被要求上证所内部函数来获得这种效果。

微软编译器不会在我的测试中,它显示了这种情况的gcc明显的优势进行并行区域内的任何自动矢量。

单回路联合并行化和矢量有一些困难,即使有最好的执行。 我很少看到2倍以上3倍或加速通过添加矢量,以并行循环。 矢量与AVX双数据类型,例如,有效地通过的4.典型实施因子切割块大小可以实现对准的数据块只对其中整个阵列被对准的情况下,和所述组块也都是矢量宽度的精确的倍数。 当块不都是对齐的,还有是由于不同路线的内在工作不平衡。



文章来源: Using OpenMP stops GCC auto vectorising