使用OpenMP停止GCC自动矢量化

使用OpenMP停止GCC自动矢量化,c,gcc,openmp,vectorization,C,Gcc,Openmp,Vectorization,我一直在努力使我的代码能够通过GCC自动矢量化,但是,当我包含-fopenmp标志时,它似乎停止了所有自动矢量化的尝试。我正在使用ftree vectorize-ftree vectorizer verbose=5对其进行矢量化和监控 如果我不包括标志,它开始给我很多关于每个循环的信息,如果它是矢量化的,为什么不。当我尝试使用omp\u get\u wtime()函数时,编译器停止运行,因为它无法链接。一旦包含该标志,它就会简单地列出每个函数,并告诉我它在其中矢量化了0个循环 我读过其他一些地方

我一直在努力使我的代码能够通过GCC自动矢量化,但是,当我包含
-fopenmp
标志时,它似乎停止了所有自动矢量化的尝试。我正在使用
ftree vectorize-ftree vectorizer verbose=5
对其进行矢量化和监控

如果我不包括标志,它开始给我很多关于每个循环的信息,如果它是矢量化的,为什么不。当我尝试使用
omp\u get\u wtime()
函数时,编译器停止运行,因为它无法链接。一旦包含该标志,它就会简单地列出每个函数,并告诉我它在其中矢量化了0个循环


我读过其他一些地方提到这个问题,但它们并没有找到任何解决方案:。OpenMP是否有自己处理矢量化的方法?我需要明确地告诉你吗?

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

  • OpenMP是否有自己处理矢量化的方法
  • 是的。。。但从即将到来的OpenMP 4.0开始。上面的文章提供了对这个结构的一个很好的了解。另一方面,当前的OpenMP 3.1并不“了解”SIMD概念。因此,在实践中(或者至少在我的经验中),每当在循环上使用openmp工作共享构造时,自动矢量化机制就会被禁用。无论如何,这两个概念是正交的,您仍然可以从中受益(参见另一个)

  • 我需要明确地告诉它吗
  • 恐怕是的,至少目前是这样。我将开始以明确矢量化的方式重写考虑中的循环(即,我将在Intel平台上使用Intrinsic,在IBM上使用Altivec等等)。

    您会问“为什么启用OpenMP时GCC不能进行矢量化?”

    似乎这可能是GCC的一个bug:)

    否则,OpenMP API可能会引入阻止自动矢量化的依赖项(控制或数据)。要自动垂直化,给定代码必须没有数据/控件依赖关系。使用OpenMP可能会导致一些虚假的依赖性


    注:OpenMP(4.0之前)使用线程级并行,这与SIMD/矢量化正交。程序可以同时使用OpenMP和SIMD并行。

    GCC矢量器中存在一个缺陷,最近的GCC版本似乎已经解决了这个问题。在我的测试用例GCC 4.7.2中,成功地实现了以下简单循环:

    #pragma omp parallel for schedule(static)
    for (int i = 0; i < N; i++)
       a[i] = b[i] + c[i] * d;
    
    GCC4.7之前版本中的矢量器无法对该循环进行矢量化。这不是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;
    }
    

    我在搜索有关gcc 4.9选项openmp simd的评论时看到了这篇文章,该选项应该在不激活omp并行(线程)的情况下激活openmp 4#pragma omp simd。gcc bugzilla pr60117(已确认)显示了pragma omp阻止在没有pragma的情况下自动矢量化的情况

    即使使用simd子句,gcc也不会矢量化omp parallel for(并行区域只能自动矢量化嵌套在parallel for下的内部循环)。除了icc 14.0.2之外,我不知道有哪种编译器可以被推荐用于实现#pragma omp parallel for simd;对于其他编译器,需要使用SSE intrinsics编码才能获得这种效果

    在我的测试中,Microsoft编译器没有在并行区域内执行任何自动矢量化,这显示了gcc在此类情况下的明显优势


    单个循环的并行化和矢量化相结合有几个困难,即使实现得最好。我很少看到通过向并行循环中添加矢量化来实现超过2倍或3倍的加速。例如,使用AVX double数据类型的矢量化有效地将块大小减少了4倍。典型的实现只能在整个数组对齐的情况下实现对齐的数据块,并且数据块也是向量宽度的精确倍数。当数据块未全部对齐时,由于对齐方式的不同,会存在固有的工作不平衡。

    我认为您可以在这个问题的答案中找到合理的信息。谢谢,这描述了如何将SIMD与OpenMP一起使用,但似乎并没有解释为什么我使用OpenMP时,已经在工作的SIMD实现停止工作。这也意味着我只能对相同数量的位进行操作,它们只是在数字之间进行分割。在使用GCC时,我并没有被问到我想要在一个寄存器上拆分多少个。因为我使用的是一台大学的“超级计算机”,所以我假设硬件对SIMD有额外的空间。我怎么才能知道这是否正确?硬件是AMD处理器,它将使用3Dnow!最后,我的问题是,由于硬件确实有特定的寄存器,可以容纳更多的寄存器来帮助矢量化,考虑到该链接中给出的函数表示它会将正常大小的寄存器拆分为块,我如何使用GCC实现这一点。非常感谢。第一个链接提供函数
    VECTOR\u ADD
    。我已经读到,它使用一个正常大小的寄存器来完成,因此只允许对小的数字进行矢量化。我知道我的硬件有特定的寄存器来处理SIMD,这样就不会发生这种情况。有没有办法让OpenMP使用此寄存器?考虑到GCC在为我做这一切之前,我是否需要使用这些函数?我不明白为什么OpenMP会停止此表单的工作。你的第二个链接说他们可以一起工作,但不是我如何做到的。再次非常感谢。主要的想法是OpenMP不能意识到SIMDization,因为您在VECTOR_ADD中处理它。我从未使用过3Dnow,但在英特尔平台上,您可以使用3Dnow显式矢量化代码。主要的缺点是要么失去可移植性(因为intrinsic不能在其他平台上工作),要么失去可读性/可维护性(因为有条件编译)
    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;
    }
    
    #pragma omp parallel for schedule(static)
    for (int i = 0; i < N; i++)
       a[i] = b[i] + c[i];