OpenMP - 求解线性方程组时性能不佳

时间:2015-02-26 14:51:52

标签: c++ multithreading openmp

我正在尝试使用OpenMP来并行化一个简单的c ++代码,该代码通过高斯消除来解决线性方程组。

我的代码的相关部分是:

#include <iostream>
#include <time.h>

using namespace std;

#define nl "\n"

void LinearSolve(double **& M, double *& V, const int N, bool parallel, int threads){
   //...  
   for (int i=0;i<N;i++){  
      #pragma omp parallel for num_threads(threads) if(parallel)
      for (int j=i+1;j<N;j++){
         double aux, * Mi=M[i], * Mj=M[j];
         aux=Mj[i]/Mi[i];
         Mj[i]=0; 
         for (int k=i+1;k<N;k++) {
            Mj[k]-=Mi[k]*aux;
         };
         V[j]-=V[i]*aux;
      };
   };
   //...    
};  

class Time {
   clock_t startC, endC;
   time_t startT, endT;
   public:
      void start() {startC=clock(); time (&startT);};
      void end() {endC=clock();  time (&endT);};          
      double timedifCPU() {return(double(endC-startC)/CLOCKS_PER_SEC);};
      int timedif() {return(int(difftime (endT,startT)));};
};

int main (){
  Time t;
  double ** M, * V;
  int N=5000;
  cout<<"number of equations "<<N<<nl<<nl;

  M= new double * [N];
  V=new double [N]; 
  for (int i=0;i<N;i++){
     M[i]=new double [N];
  };

  for (int m=1;m<=16;m=2*m){
     cout<<m<<" threads"<<nl;

     for (int i=0;i<N;i++){
        V[i]=i+1.5*i*i;
        for (int j=0;j<N;j++){
           M[i][j]=(j+2.3)/(i-0.2)+(i+2)/(j+3); //some function to get regular matrix
        };
     };

     t.start();
     LinearSolve(M,V,N,m!=1,m);
     t.end();

     cout<<"time "<<t.timedif()<<", CPU time "<<t.timedifCPU()<<nl<<nl;
  };
}

由于代码非常简单,我希望时间会如此 与线程数成反比。然而,我得到的典型结果是(代码在Linux上用gcc编译)

number of equations 5000
1 threads
time 217, CPU time 215.89

2 threads
time 125, CPU time 245.18

4 threads
time 80, CPU time 302.72

8 threads
time 67, CPU time 458.55

16 threads
time 55, CPU time 634.41

时间有所减少,但我想要的却少得多,CPU时间神秘地增长。

我怀疑问题在于内存共享,但我无法识别它。对行M [j]的访问应该不是问题,因为每个线程写入矩阵的不同行。从行M [i]读取时可能存在问题,因此我还尝试通过替换并行循环来为每个线程创建此行的单独副本

  #pragma omp parallel num_threads(threads) if(parallel)
  {
     double Mi[N];
     for (int j=i;j<N;j++) Mi[j]=M[i][j];
     #pragma omp for 
     for (int j=i+1;j<N;j++){
        double aux, * Mj=M[j];
        aux=Mj[i]/Mi[i];
        Mj[i]=0; 
        for (int k=i+1;k<N;k++) {
           Mj[k]-=Mi[k]*aux;
        };
        V[j]-=V[i]*aux;
     };
  };

不幸的是它根本没用。

我非常感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

您的问题是OpenMP同步过多。

将#omp并行放在第一个循环中意味着外循环的每次迭代都带有整个同步开销。

OpenMP profile from inside Allinea MAP

在这里查看图片中最顶层的图表(更多细节可以在Allinea MAP OpenMP profiler介绍中找到)。顶行是应用程序活动 - 深灰色表示“OpenMP同步”,绿色表示“执行计算”。

您可以在该顶部图表/图表的右侧看到大量深灰色 - 即16个线程正在运行时。你花了很多时间同步。

我也看到花了很多时间在内存访问上(比在计算中更多) - 所以这可能是因为平衡工作负载实际上是高度不平衡并且给出了同步延迟。

正如其他受访者所建议的那样 - 值得在此处阅读其他文献。

答案 1 :(得分:1)

我认为潜在的问题可能是传统的高斯消除可能不适合并行化。

高斯消元是一个过程,其中每个后续步骤依赖于前一步骤的结果,即线性求解循环的每次迭代都取决于前一次迭代的结果,即它必须连续完成。尝试在文献中搜索&#34;并行行减少算法&#34;。

同样瞥了一眼你的代码看起来你会有竞争条件。