C++ 在这种情况下,为什么OpenMP速度慢?

C++ 在这种情况下,为什么OpenMP速度慢?,c++,openmp,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

我试图理解为什么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;

}
#包括
#包括
#包括
#包括
void AddVectors(标准::vector&v1,
标准::向量&v2){
尺寸i;
#pragma omp并行专用(i)
对于(i=0;iv1(N1,1);
std::vectorv2(N1,2);
对于(size_t i=0;i
我首先编译了上面的代码,但没有启用OpenMP(通过在编译标志上省略-fopenmp)。N1=10000的执行时间为0.1s。启用OpenMP使执行时间超过1分钟。我在完成之前就停止了(厌倦了等待…)

我将代码编译如下:

g++-std=c++0x-O3-funroll循环-march=core2-fomit帧指针-Wall-fno严格别名-o main.o-c main.cpp

g++main.o-o main

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


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

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

我在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;

}
#包括
#包括
#包括
#包括
void AddVectors(标准::vector&v1,
标准::向量&v2){
#pragma-omp并行
对于(inti=0;iv1(N1,1);
std::vectorv2(N1,2);
对于(size_t i=0;i
问题在于您使用的数组类型

向量是一个容器。它是一个存储多个信息的结构,如大小、开始、结束等;并且具有多个内置功能,其中操作符[]是用于访问数据的其中一个。因此,倾向于加载向量V的索引“i”的缓存线加载元素V[i]和代码中未使用的一些信息

#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;

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

请参见_mm_malloc和malloc之间的差异,以增强数据对齐

@福兹先生 我对此不确定。让我们比较两种情况下的性能结果:

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;
}
const int N=10000000;
定时(wcs和wcs);
对于(;运行时<0.1;重复*=2)
{
for(int r=0;r

MFLops:((N*repeat)/runtime)/1000000

只是猜测,但是:您是否尝试过在循环之前将循环最大值(
v1.size()
)存储在临时变量中,并在for循环子句中使用该变量?也许编译器看不到size()的返回值由于某种原因没有改变。您的代码现在看起来很奇怪。N1表示向量的长度和循环的运行次数?这是故意的吗?无法复制,在这里工作正常(N1=100000,没有openmp时为17秒,使用Core2 Quad时为4.7秒)。你有什么版本的GCC?什么类型的CPURonny:我以前有一个bug——我将N1和N2设置为atoi(argv[1]),所以我放弃了N2。看起来很奇怪,但并没有改变我的结果。不相关:C++有一个<代码> <代码>标题,我认为它比 <代码>更“合适”。- 1:相对于大多数纯C数组实现,向量在大多数情况下是一个0开销容器。额外的元数据(数组的长度和保留大小)与向量实例一起存储,而不是存储在每个元素中。-1=>+1:您似乎是正确的。有趣。你知道为什么它会加载额外的元数据吗?运算符[]不进行边界检查。@MrFooz: