为什么OpenMP没有加速基本循环?

时间:2016-03-29 16:28:48

标签: c++ g++ openmp

我有一个基本循环:

int i, n=50000000;
for (i=0 ; i<n ; i++)
{
    register float val = rand()/(float)RAND_MAX;
}

我想用OpenMP加速。我之前设定了:

omp_set_dynamic(0);
omp_set_num_threads(nths);

nths=4

最后一个循环是:

int i, n=50000000;
#pragma omp parallel for firstprivate(n) default(shared)
for (i=0 ; i<n ; i++)
{
    register float val = rand()/(float)RAND_MAX;
}

非并行化循环执行需要1.12秒,并行执行需要21.04秒(根据我的linux优先级流程,它可能会有很大变化)。我在x86平台上使用Ubuntu和4个CPU,每个1个线程。我使用g++(我需要它)进行编译,并使用-fopenmp标记-lgomp

为什么OpenMP没有加速这个基本循环?

修改

关于答案,我将循环内部改为:

for (i=0 ; i<n ; i++)
{
    a[i]=i;
    b[i]=i;
    c[i]=a[i]+b[i];
}

使用n=500000和pragma:

#pragma omp parallel for firstprivate(n) default(shared) schedule(dynamic) num_threads(4)

我还将代码更改为仅使用gcc,我遇到了同样的问题:

With 1 Thread
Test        ms = 0.003000
Test Omp    ms = 19.695000
With 4 Threads
Test        ms = 0.003000
Test Omp    ms = 240.990000

EDIT2:

我改变了使用OpenMP时测量时间的方式。我使用clock()函数代替omp_get_wtime()函数,结果更好。

2 个答案:

答案 0 :(得分:2)

我在我的系统中快速运行你的代码。 首先,在阵列添加的情况下,你的50M几乎不足以显示胜利,但确实如此 - 如果OpenMP设置正确的话。

在你的情况下,schedule(dynamic)正在扼杀你 - 它告诉编译器将工作分散到运行时的团队。如果你不能预先确定你的工作量,这将是有意义的 - 但在这种情况下,它是完全可预测的,因为每次迭代的努力完全相同。

在编辑了您的示例(见下文)并在超线程CPU上运行后,我得到了以下结果,其核心都固定在最低频率上。我使用gcc 4.9.3编译:

time ./testseq && time ./testpar

real    0m0.576s
user    0m0.504s
sys     0m0.072s

real    0m0.285s
user    0m0.968s
sys     0m0.123s

正如您所看到的,real值,即&#34;挂钟时间&#34;,大致减半。由于线程启动和关闭,user时间会增加。

如果我添加schedule(dynamic)子句,并行化结果会发生很大变化:

real    0m4.181s
user    0m14.886s
sys     0m1.283s

所有额外的工作负担都花费在执行少量工作并寻找下一批的线程上。这需要锁定 - 这会杀死你的第二个例子。如果遇到负载平衡问题,请仅使用schedule(dynamic) - 每个迭代器的工作量差别很大。

为了充分披露,我使用以下完整源代码运行:

CXXFLAGS=-std=c++11 -I. -Wall -Wextra -g -pthread

all: testseq testpar

testpar: test.cpp
    ${CXX} -o $@ $^ -fopenmp ${CXXFLAGS}

testseq: test.cpp
    ${CXX} -o $@ $^ ${CXXFLAGS}

clean:
    rm -f *.o *~ test

test.cpp

#include <omp.h>

constexpr int n=50*1000*1000;
float a[n];
float b[n];
float c[n];

int main(void) {
    #pragma omp parallel for schedule(dynamic)
    for (int i=0 ; i<n ; i++) {
        a[i]=i;
        b[i]=i;
        c[i]=a[i]+b[i];
    }
}

请注意,我还将其他条款带到您的parallel for - 您不需要任何条款。

答案 1 :(得分:0)

rand()根本不是线程安全的函数。内部有一个具有状态的PRNG,因此在没有同步的情况下不能被多个线程调用。使用不同的PRNG(C ++ 11有很多它们,也是Boost),每个线程使用一个生成器,不要忘记用不同的种子值播种它们(小心{{} 1}})。

<强>更新

由于线程创建的开销,

time(NULL) 500k可能太小而无法获得任何加速。您可以测试,例如n设置为500M吗?而且,没有OpenMP的时间可疑性很低(可能不是这么低的n)。在循环之后,您对nab数组做了什么?如果没有,编译器可以在顺序代码中优化整个循环。 对这些数组执行某些操作,例如,打印出它们的总和(在测量部分之外)。