我有一个基本循环:
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()
函数,结果更好。
答案 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
)。在循环之后,您对n
,a
和b
数组做了什么?如果没有,编译器可以在顺序代码中优化整个循环。 对这些数组执行某些操作,例如,打印出它们的总和(在测量部分之外)。