OpenMP中的高斯消元 - 性能问题

时间:2015-07-12 21:49:22

标签: optimization parallel-processing openmp linear-algebra

我是openMP的新手,我试图并行化高斯消除,我遇到了性能问题。我正在使用以下代码编译以下代码:

gcc -o gaussian_elimination gaussian_elimination.c -lm -lgsl -lgslcblas -fopenmp -Wall

使用export OMP_NUM_THREADS

设置终端上的线程数

我的问题是这个代码的并行版本运行速度比同一版本的串行版本慢。我相信这是因为我在外部循环中声明了#pragma parallel,这会强制openMP在每次迭代时创建和销毁线程,这将是非常昂贵的,但我还没有看到任何其他明确的方法来做同样的操作,我不认为我可以用内部并行交换外部循环。

我可能遗漏了一些东西,但我还没有找到任何其他论坛帖子来评论这个特殊问题。就执行的正确性而言,我的代码似乎正常运行,问题只是在性能方面。

先谢谢

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <omp.h>
#include <stdbool.h>
#include <time.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_rng.h>

#define DEBUG_MODE false

int random_matrix(double *A, int N,long long int seed);

int print_matrix(double *A, int N);

int print_vector(float *b,int N);

int main(int argc, char **argv){
  int N=1000;
  int i,j,k,l,i_p,s,err,D=N+1;
  long long int seed=9089123498274; // just a fixed seed only not to bother
  double *A,pivot,sw,tmp,begin,end,time_spent;
  double *Aref,*bref;
  gsl_matrix_view gsl_m; 
  gsl_vector_view gsl_b;
  gsl_vector *gsl_x;
  gsl_permutation *gsl_p;

  /* Input */

  //scanf("%d",&N);

  A = (double*)malloc(N*(N+1)*sizeof(double));
  if(A==NULL){
    printf("Matrix A not allocated\n");
    return 1;
  }

  Aref = (double*)malloc(N*N*sizeof(double));
  if(Aref==NULL){
    printf("Matrix A not allocated\n");
    return 1;
  }
  bref = (double*)malloc(N*sizeof(double));
  if(bref==NULL){
    printf("Vector B not allocated\n");
    return 2;
  }

  /*
  for(i=0;i<N;i+=1)
    for(j=0;j<N;j+=1)
      scanf("%f",&(A[i*N+j]));

  for(i=0;i<N;i+=1)
    scanf("%f",&(b[i]));
  */

  /*
  for(i=0;i<N*N;i++)
    A[i]=(float) a_data[i];
  for(i=0;i<N;i+=1)
    b[i]=(float) b_data[i]; */

  err= random_matrix(A,N,seed);
  if(err!=0)
    return err;

  for(i=0;i<N;i++)
    for(j=0;j<N;j+=1)
      Aref[i*N+j]= A[i*D+j];
  for(i=0;i<N;i+=1)
    bref[i]= A[i*D+N];//b[i];

  printf("GSL reference:\n");

  gsl_m = gsl_matrix_view_array (Aref, N, N);
  gsl_b = gsl_vector_view_array (bref, N);
  gsl_x = gsl_vector_alloc (N);
  gsl_p = gsl_permutation_alloc(N);

  begin = clock();

  gsl_linalg_LU_decomp(&gsl_m.matrix, gsl_p, &s);
  gsl_linalg_LU_solve(&gsl_m.matrix, gsl_p, &gsl_b.vector, gsl_x);

  end = clock();
  time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
  printf("gsl matrix solver: %lf s\n",time_spent);

  if(DEBUG_MODE==true)
   gsl_vector_fprintf(stdout,gsl_x,"%f");

  gsl_permutation_free(gsl_p);
  gsl_vector_free(gsl_x);  

  begin = omp_get_wtime();

  for(i=0;i<N;i+=1){

    i_p = i;
    pivot = fabs(A[i*D+i]);
    for(j=i;j<N;j+=1)
      if(pivot<fabs(A[j*D+i])){ 
        pivot = fabs(A[j*D+i]);
        i_p = j;
      }

    #pragma omp parallel for shared(i,N,A,i_p) private(j,sw)
    for(j=i;j<D;j+=1){
      sw = A[i*D+j];
      A[i*D+j] = A[i_p*D+j];
      A[i_p*D+j] = sw;
    }

    pivot=A[i*D+i];
    #pragma omp parallel for shared(i,D,pivot,A) private(j)
    for(j=0;j<D;j++)
      A[i*D+j]=A[i*D+j]/pivot;

    #pragma omp parallel for shared(i,A,N,D) private(tmp,j,k,l)
    for(j=i+1;j<N+i;j++){
      k=j%N;
      tmp=A[k*D+i];
      for(l=0;l<D;l+=1)
        A[k*D+l]=A[k*D+l]-tmp*A[i*D+l];
    }   
  }

  end = omp_get_wtime();
  time_spent = (end - begin);
  printf("omp matrix solver: %lf s\n",time_spent);

  /* Output */

  if(DEBUG_MODE==true){
    printf("\nCalculated: \n");
    for(i=0;i<N;i+=1)
      printf("%.6f \n",A[i*(N+1)+N]);
    printf("\n"); 
  }

  free(A);

  return 0;
}

int random_matrix(double *A, int N,long long int seed){
  int i,j;
  const gsl_rng_type * T;
  gsl_rng *r;
  gsl_rng_env_setup();

  T = gsl_rng_default;
  r = gsl_rng_alloc (T);

  for(i=0;i<N;i++)
    for(j=0;j<=N;j++)
      A[i*(N+1)+j]= gsl_rng_uniform (r);

  gsl_rng_free (r);

  return 0;
}

int print_matrix(double *A, int N){
  int i,j;
  for(i=0;i<N;i++)
    for(j=0;j<=N+1;j++){
      if(j==0 || j==N || j==N+1)
        printf(" | ");

      printf("%.2f ",A[i*(N+1)+j]);

      if(j==N+1)
        printf("\n"); 
    }

  return 0;
}

int print_vector(float *b,int N){
  int i;

  for(i=0;i<N;i+=1)
    printf("%f\n", b[i]);

  return 0;
}

我使用omp_get_wtime()更新了上面的代码,现在它读取为因为我包含越来越多的线程而减少了wtime,因此,它确实表现得如此,尽管不像我想的那样干净。

对于1000 x 1000矩阵,GSL lib为0.25 s,串行omp运行为4.4 s,4线程运行为1.5 s。

对于3000 x 3000矩阵,我得到~9s用于GSL lib,~117 s用于串行omp运行,~44 s用于4线程运行,因此至少添加更多线程确实加速了程序! / p>

非常感谢大家

0 个答案:

没有答案