C++ OpenMP循环比串行循环运行代码慢
我正在运行这个简洁的重力模拟,在串行执行中,它需要4分钟多一点,当我并行化一个循环时,它会增加到7分钟左右,如果我尝试并行化更多的循环,它会增加到20分钟以上。我发布了一个稍微缩短的版本,没有一些初始化,但我认为它们并不重要。我发布的是7分钟的版本,不过我想在其中添加一些注释,以便将并行化添加到循环中。谢谢你帮我处理这些乱七八糟的代码C++ OpenMP循环比串行循环运行代码慢,c++,parallel-processing,openmp,C++,Parallel Processing,Openmp,我正在运行这个简洁的重力模拟,在串行执行中,它需要4分钟多一点,当我并行化一个循环时,它会增加到7分钟左右,如果我尝试并行化更多的循环,它会增加到20分钟以上。我发布了一个稍微缩短的版本,没有一些初始化,但我认为它们并不重要。我发布的是7分钟的版本,不过我想在其中添加一些注释,以便将并行化添加到循环中。谢谢你帮我处理这些乱七八糟的代码 #include <stdio.h> #include <math.h> #include <stdlib.h> #inclu
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>
#define numb 1000
int main(){
double pos[numb][3],a[numb][3],a_local[3],v[numb][3];
memset(v, 0.0, numb*3*sizeof(double));
double richtung[3];
double t,deltat=0.0,r12 = 0.0,endt=10.;
unsigned seed;
int tcount=0;
#pragma omp parallel private(seed) shared(pos)
{
seed = 25235 + 16*omp_get_thread_num();
#pragma omp for
for(int i=0;i<numb;i++){
for(int j=0;j<3;j++){
pos[i][j] = (double) (rand_r(&seed) % 100000 - 50000);
}
}
}
for(t=0.;t<endt;t+=deltat){
printf("\r%le", t);
tcount++;
#pragma omp parallel for shared(pos,v)
for(int id=0; id<numb; id++){
for(int l=0;l<3;l++){
pos[id][l] = pos[id][l]+(0.5*deltat*v[id][l]);
v[id][l] = v[id][l]+a[id][l]*(deltat);
}
}
memset(a, 0.0, numb*3*sizeof(double));
memset(a_local, 0.0, 3*sizeof(double));
#pragma omp parallel for private(r12,richtung) shared(a,pos)
for(int id=0; id <numb; ++id){
for(int id2=0; id2<id; id2++){
for(int k=0;k<3;k++){
r12 += sqrt((pos[id][k]-pos[id2][k])*(pos[id][k]-pos[id2][k]));
}
for(int k=0; k<3;k++){
richtung[k] = (-1.e10)*(pos[id][k]-pos[id2][k])/r12;
a[id][k] += richtung[k]/(((r12)*(r12)));
a_local[k] += (-1.0)*richtung[k]/(((r12)*(r12)));
#pragma omp critical
{
a[id2][k] += a_local[k];
}
}
r12=0.0;
}
}
#pragma omp parallel for shared(pos)
for(int id =0; id<numb; id++){
for(int k=0;k<3;k++){
pos[id][k] = pos[id][k]+(0.5*deltat*v[id][k]);
}
}
deltat= 0.01;
}
return 0;
}
#包括
#包括
#包括
#包括
#包括
#定义numb 1000
int main(){
双位[numb][3]、a[numb][3]、a_local[3]、v[numb][3];
memset(v,0.0,numb*3*sizeof(double));
双日东[3];
双t,deltat=0.0,r12=0.0,endt=10。;
无标记种子;
int t计数=0;
#pragma omp并行专用(种子)共享(pos)
{
seed=25235+16*omp_get_thread_num();
#pragma omp for
对于(int i=0;i),我认为临界区是问题的起因。考虑在并行化结束后把所有关键部分都带到并行化的循环上并运行它们。
试试这个:
#pragma omp parallel shared(a,pos)
{
#pragma omp for private(id2,k,r12,richtung,a_local)
for(id=0; id <numb; ++id){
for(id2=0; id2<id; id2++){
for(k=0;k<3;k++){
r12 += sqrt((pos[id][k]-pos[id2][k])*(pos[id][k]-pos[id2][k]));
}
for(k =0; k<3;k++){
richtung[k] = (-1.e10)*(pos[id][k]-pos[id2][k])/r12;
a[id][k] += richtung[k]/(((r12)*(r12))+epsilon);
a_local[k]+= richtung[k]/(((r12)*(r12))+epsilon)*(-1.0);
}
}
}
}
for(id=0; id <numb; ++id){
for(id2=0; id2<id; id2++){
for(k=0;k<3;k++){
a[id2][k] += a_local[k];
}
}
}
#pragma omp并行共享(a,pos)
{
#私人用布拉格omp(id2、k、r12、里士东、a_本地)
对于(id=0;id),临界区看起来很糟糕。难道你不能重新运行循环并将临界区保持在外部而不进行并行化吗?临界区用我的加速度a[id2][0,1,2]解决一个竞赛条件
类似于一个数组的缩减。我确实需要id2循环,所以我要写入右边的forces int a。richtung
定义在哪里?如果是数组或指针,它会有很大的区别。如果是数组,OpenMP将为每个线程生成私有数组(如您所希望的)。如果它是一个指针,那么每个线程只会得到一个私有指针。你检查过并行版本得到相同的答案了吗?你在循环结束时做了'r12=0.0;`这样每个线程的r12
的初始值就没有定义。numb
有多大。你需要做足够的工作来克服OpenMP开销。@Haemilt恩,我理解,但你可能不理解我的替代解决方案。只需在并行循环之外创建另一个循环,不要在那里使用OpenMP。这对你有效吗?如果有效,肯定会比你在那里拥有的要好得多。关键部分意味着你使用的是锁定和互斥,根据定义,这会降低d的速度拥有你的代码。是的,这将解决关键部分问题,而不是a_local[k]
我必须使用a[id][k]*(-1.0)
,这意味着我使用运行两个循环(1000^2-1000)/2次迭代而不是1次。@Haemiltoen那么这肯定更快。去做吧,看看会发生什么。记住迭代的次数其实并不重要。重要的是你在循环中做了什么。@QuantimpPhysist现在它比临界区域运行得更快,但比连续运行慢5分钟。但是我无法将第二个循环并行化,否则我将得到一个竞争条件,我必须通过某种临界区域或将其简化为一个应该相同的数组来解决。但感谢您的帮助,我想我学到了一些有关OpenMP的知识。@Haemiltoen不客气。如果这个问题已解决,请通过复选标记选择上面的答案@海米尔顿,你为什么去掉复选标记?发生了什么事?