OpenMP并行版本比串行运行慢

时间:2014-04-15 21:08:07

标签: c++ parallel-processing openmp

我有这个代码,我使用OpenMP进行并行化,似乎比串行版本运行得慢。这是代码的相关片段:

Out_props ion_out;

#pragma omp parallel for firstprivate(Egx,Egy,vi_inlet,dt,xmin,xmax,ymin,ymax,qmi,dy,Nx) private(ion_out)
for (int i=0;i<Np;i++)
{
    ion_out = ApplyReflectionBC(dt,Nx,xmin,xmax,ymin,ymax,qmi,dy,vi_inlet,Egx,Egy,xi_i[2*i],xi_i[1+2*i],vi_i[2*i],vi_i[1+2*i]);

    xi_o[1-1+2*i]=ion_out.xout;
    xi_o[2-1+2*i]=ion_out.yout;
    vi_o[1-1+2*i]=ion_out.vxout;
    vi_o[2-1+2*i]=ion_out.vyout;
}

此处outprops只是一个包含4个double类型成员的结构。 ApplyReflectionBC函数(如下所示)仅对每个i应用一些操作。所有这些操作完全相互独立。 EgxEgy是在进入此循环之前定义的60x60矩阵,vi_inlet是2x1向量。我已经尝试将ion_out制作一个大小为Np的矩阵,以进一步提高独立性,但这似乎没有任何区别。 firstprivate内的其他所有内容都是在进入此循环之前定义的double类型。

我真的很感激为什么这可能比串行版本运行慢很多次。谢谢!

Out_props ApplyReflectionBC(double dt,int Nx,double xmin,double xmax,double ymin, double ymax,double qmp, double dy, double *vp_inlet,double *Egx,double *Egy, double xpx,double xpy,double vpx,double vpy)
{
    Out_props part_out;
    double Lgy=ymax-ymin;
    double xp_inp[2]={xpx,xpy};
    double vp_inp[2]={vpx,vpy};
    double xp_out[2];
    double vp_out[2];

    struct vector
    {
        double x;
        double y;
    }vnmf,Ep,xnmf;


    if((xp_inp[1-1]>xmin) && (xp_inp[1-1]<xmax) && (xp_inp[2-1]<ymin)) //ONLY below lower wall
    {
       xp_out[1-1]=xp_inp[1-1];
       xp_out[2-1]=ymin;

       vp_out[1-1]=vp_inp[1-1];
       vp_out[2-1]=-vp_inp[2-1];
    }
    else if((xp_inp[1-1]<xmin) || (xp_inp[1-1]>xmax) || (xp_inp[2-1]>ymax))
    {//Simple Boris Push
        xnmf.x=xmin;
        xnmf.y=ymin+Lgy*rand()/RAND_MAX;

        vnmf.x=vp_inlet[0];
        vnmf.y=vp_inlet[1];

        //Find E field at x,y
        double yjp=ymin+dy*floor((xnmf.y-ymin)/(1.0*dy));
        double yjp1p=yjp+dy;

        int kp=(yjp-ymin)/dy;

        int kpp1=kp+1;
        double ylg=xnmf.y-yjp;

        double wjk=1.0*(dy-ylg)/(1.0*dy);
        double wjkp1=1.0*ylg/(1.0*dy);

        Ep.x=wjk*Egx[Nx*kp]+wjkp1*Egx[Nx*kpp1];

        Ep.y=wjk*Egy[Nx*kp]+wjkp1*Egy[Nx*kpp1];

        do
        {
            double f=1.0*rand()/RAND_MAX;
            xp_out[1-1]=xnmf.x+f*dt*(vnmf.x+qmp*Ep.x*f*dt/2.0);
            xp_out[2-1]=xnmf.y+f*dt*(vnmf.y+qmp*Ep.y*f*dt/2.0);

            vp_out[1-1]=vnmf.x+qmp*Ep.x*(f-0.5)*dt;
            vp_out[2-1]=vnmf.y+qmp*Ep.y*(f-0.5)*dt;

        } while((xp_out[1-1]<xmin) || (xp_out[1-1]>xmax) || (xp_out[2-1]<ymin)  || (xp_out[2-1]>ymax));
    }
    else
    {
        xp_out[1-1]=xp_inp[1-1];
        xp_out[2-1]=xp_inp[2-1];

        vp_out[1-1]=vp_inp[1-1];
        vp_out[2-1]=vp_inp[2-1];
    }

    part_out.xout=xp_out[0];
    part_out.yout=xp_out[1];
    part_out.vxout=vp_out[0];
    part_out.vyout=vp_out[1];

    return part_out;
}

1 个答案:

答案 0 :(得分:0)

有些观点:

首先,firstprivate指令在每个线程的堆栈中创建声明变量的副本,因此需要一些时间。由于这些变量不会被更改(即只读),因此您可以将它们声明为shared

其次,但影响较小,ApplyReflectionBC函数按值获取所有内容,因此它将创建每个参数的本地副本。使用引用(例如double &dt)。

修改

正如Hristo指出的那样,rand()是你问题的根源。您必须将其替换为其他随机数生成器功能。对于更好的随机数和线程安全性,您可以使用此Mersenne Twister类(如果LGPL 2.1不是问题):http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h。只需将其声明为您的主题,例如:

MTRand rng;
#pragma omp parallel for private(rng, ...)
for (..)
{
  ApplyReflectionBC(..., rng);
}

Out_props ApplyReflectionBC(...,MTRand &rng)
{

  // .... Code ....

  xnmf.y=ymin+Lgy*rng.rand(); // MTRand::rand will return a number in the range [0; 1]

  // ........
}