我正在运行这个简洁的小重力模拟,并且在串行执行中需要花费4分多钟,当我将一个循环内部并行化时,它增加到大约7分钟,如果我尝试并行化更多循环,它会增加到超过20分钟。我发布了一个稍微缩短的版本而没有进行一些初始化,但我认为它们并不重要。我发布了7分钟的版本,但有一些评论,我想在循环中添加并行化。谢谢你帮我搞乱我的代码。
#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;
}
我正在使用
g++ -fopenmp -o test_grav test_grav.c
编译代码,我只是在shell中测量时间
time ./test_grav
。
我用的时候
get_numb_threads()
获取它显示的线程数4. top
还显示超过300%(有时约为380%)的CPU使用率。有趣的是,如果我在时间循环之前启动并行区域(意味着最外部的for循环)并且没有任何实际的#pragma omp for
,这相当于为每个主要区域创建一个并行区域(三分之一到最外层)循环)。所以我认为这是一个优化的事情,但我不知道如何解决它。任何人都可以帮助我吗?
编辑:我使示例可验证并降低了numb
之类的数字,以使其更易于测试,但问题仍然存在。即使我按照TheQuantumPhysicist的建议移除了关键区域,也不是那么严重。
答案 0 :(得分:1)
我认为关键部分是导致问题的原因。考虑将所有关键部分放在并行化循环之外,并在并行化结束后运行它们。
试试这个:
#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];
}
}
}
关键部分将导致锁定和阻止。如果你能将这些部分保持线性,你将在性能上获得很大的收获。
请注意,我正在谈论一种语法解决方案,我不知道它是否适用于您的情况。但需要明确的是:如果你的系列中的每个点都取决于下一个,那么并行化并不是你的解决方案;至少使用OpenMP进行简单的并行化。