为什么 OpenMP 不加速基本循环?

Why a basic loop isn't accelerated by 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];
}

with 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

编辑2:

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

rand() 根本不是线程安全函数。里面有一个 PRNG,它有一个状态,因此在没有同步的情况下不能被多个线程调用。使用不同的 PRNG(C++11 有很多,Boost 也是),每个线程使用一个生成器并且不要忘记用 不同的种子值 为它们播种(提防 time(NULL)).

更新

由于线程创建的开销,

n 为 500k 可能太小而无法获得任何加速。你能测试一下,例如,n 设置为 500M 吗?此外,没有 OpenMP 的时间低得令人怀疑(可能不会这么低 n)。循环后的 abc 数组在做什么?如果没有,编译器可以优化顺序代码中的整个循环。 对这些数组做一些事情,例如,打印出它们的总和(在测量部分之外)。

我运行你的代码很快就通过了我的系统。 首先,在数组加法的情况下,您的 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 值(即 "wallclock time")大致减半。 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 的其他条款 - 您需要 none 个。