为什么OpenMP在这种情况下会变慢?

时间:2011-03-22 19:54:18

标签: c++ openmp

我试图理解为什么OpenMP在下面的示例中的工作方式。

#include <omp.h>
#include <iostream>
#include <vector>
#include <stdlib.h>

void AddVectors (std::vector< double >& v1,
                 std::vector< double >& v2) {

    size_t i;

#pragma omp parallel for private(i)
    for (i = 0; i < v1.size(); i++) v1[i] += v2[i];

}


int main (int argc, char** argv) {

    size_t N1 = atoi(argv[1]);

    std::vector< double > v1(N1,1);
    std::vector< double > v2(N1,2);

    for (size_t i = 0; i < N1; i++) AddVectors(v1,v2);

    return 0;

}

我首先在不启用OpenMP的情况下编译上面的代码(通过在编译标志上省略-fopenmp)。 N1 = 10000的执行时间为0.1秒。启用OpenMP会使执行时间超过1 分钟。我在它完成之前就已经停止了(厌倦了等待......)。

我正在编译代码如下:

g ++ -std = c ++ 0x -O3 -funroll-loops -march = core2 -fomit-frame-pointer -Wall -fno-strict-aliasing -o main.o -c main.cpp

g ++ main.o -o main

并非所有这些标志都是必需的,但我在项目中使用它们我试图并行化并在那里使用那些标志。这就是为什么我决定把它们留在这里的原因。另外,我添加-fopenmp以在编译时启用OpenMP。

有人知道出了什么问题吗?谢谢!

3 个答案:

答案 0 :(得分:2)

我在Visual Studio 2008上尝试了相同的示例。 我对您的代码示例进行了两次修改,使用OpenMP的速度比没有OpenMP快大约3倍。

无法在GCC上确认它,问题可能是在主循环中调用函数AddVectors,并且每次必须执行“fork”操作时,这将花费一些可测量的时间。因此,如果你有N1 = 10000,它必须产生10000个“fork”操作。

我已经修改了您自己的代码片段,只是为了让它在Visual Studio下工作, 我最后添加了一个print语句,以避免编译器删除所有代码。

#include <omp.h>
#include <iostream>
#include <vector>
#include <stdlib.h>

void AddVectors (std::vector< double >& v1,
                 std::vector< double >& v2) {

    #pragma omp parallel for
    for (int i = 0; i < static_cast<int>(v1.size()); i++) v1[i] += v2[i];

}


int main (int argc, char** argv) {

    size_t N1 = atoi(argv[1]);

    std::vector< double > v1(N1,1);
    std::vector< double > v2(N1,2);

    for (size_t i = 0; i < N1; i++) AddVectors(v1,v2);


    printf("%g\n",v1[0]);
    return 0;

}

答案 1 :(得分:1)

可以对整个AddVectors调用进行g ++优化吗?尝试返回最后一个v1元素并将其存储在volatile变量中。

答案 2 :(得分:1)

问题在于您使用的数组类型

矢量是一个容器。它是一种存储大小,开始,结束等信息的结构;并且有几个内置函数,其中 operator [] 是用于访问数据的其中一个。因此,倾向于加载的缓存行表示向量 V 的索引“i”,加载元素 V [i] 和一些未使用的信息在代码中。

相反,如果使用经典数组(动态/静态),则运算符[]导致仅加载数据元素。因此,高速缓存行(通常为64字节长)将加载此双数组的8个元素(大小为double = 8字节)。

请参阅_mm_malloc和malloc之间的区别以增强数据对齐。

@Mr Fooz 我不确定。让我们比较两种情况的效果结果:

  

i7处理器上的4个线程

     

阵列时间:0.122007 |重复:4 | MFlops:327.85

     

矢量时间:0.101006 |重复:2 | MFlops:188.669

我强制运行时间超过0.1秒,因此代码会自行重复。主循环:

const int N = 10000000;
timing(&wcs);
for(; runtime < 0.1; repeat*=2)
{
    for(int r = 0; r < repeat; r++)
    {
        #pragma omp parallel for
        for(int i = 0; i < N; i++)
        {
            A[i] += B[i];
        }
        if(A[0]==0) dummy(A[0]);
    }
    timing(&wce);
    runtime = wce-wcs;
}

MFLops :((N *重复)/运行时)/ 1000000