OpenMP C ++矩阵乘法

时间:2014-11-19 22:12:52

标签: c++ openmp

我想并行化以下代码。特别是这些for循环,因为它是最昂贵的操作。

      for (i = 0; i < d1; i++)
         for (j = 0; j < d3; j++)
             for (k = 0; k < d2; k++)
             C[i][j] = C[i][j] + A[i][k] * B[k][j];  

这是我第一次尝试使用OpenMP并行化代码。我尝试了几件事,但总是比使用串行版本更糟糕的运行时间。 如果你能告诉我代码或者pragma是否有问题,那将会很棒......

      #include <omp.h>
      #include <stdio.h>
      #include <stdlib.h>
      //#include <stdint.h>

      // ---------------------------------------------------------------------------
      // allocate space for empty matrix A[row][col]
      // access to matrix elements possible with:
      // - A[row][col]
      // - A[0][row*col]


      float **alloc_mat(int row, int col)
      {
          float **A1, *A2;

          A1 = (float **)calloc(row, sizeof(float *));      // pointer on rows
          A2 = (float *)calloc(row*col, sizeof(float));    // all matrix elements

          //#pragma omp parallel for
          for (int i=0; i<row; i++)
              A1[i] = A2 + i*col;

          return A1;
      }

      // ---------------------------------------------------------------------------
      // random initialisation of matrix with values [0..9]

      void init_mat(float **A, int row, int col)
      {   
          //#pragma omp parallel for
          for (int i = 0; i < row*col; i++)
              A[0][i] = (float)(rand() % 10);
      }

      // ---------------------------------------------------------------------------
      // DEBUG FUNCTION: printout of all matrix elements

      void print_mat(float **A, int row, int col, char *tag)
      {
          int i, j;

          printf("Matrix %s:\n", tag);
          for (i = 0; i < row; i++)
          {
              //#pragma omp parallel for
              for (j=0; j<col; j++) 
                  printf("%6.1f   ", A[i][j]);
              printf("\n"); 
          }
      }

      // ---------------------------------------------------------------------------

      int main(int argc, char *argv[])
      {
          float **A, **B, **C;  // matrices
          int d1, d2, d3;         // dimensions of matrices
          int i, j, k;          // loop variables


          double start, end;
          start = omp_get_wtime();

          /* print user instruction */
          if (argc != 4)
          {
              printf ("Matrix multiplication: C = A x B\n");
              printf ("Usage: %s <NumRowA>; <NumColA> <NumColB>\n",argv[0]); 
               return 0;
           }

           /* read user input */
           d1 = atoi(argv[1]);      // rows of A and C
           d2 = atoi(argv[2]);     // cols of A and rows of B
           d3 = atoi(argv[3]);     // cols of B and C

           printf("Matrix sizes C[%d][%d] = A[%d][%d] x B[%d][%d]\n", 
           d1, d3, d1, d2, d2, d3);

           /* prepare matrices */
           A = alloc_mat(d1, d2);
           init_mat(A, d1, d2); 
           B = alloc_mat(d2, d3);
           init_mat(B, d2, d3);
           C = alloc_mat(d1, d3);   // no initialisation of C, 
       //because it gets filled by matmult

           /* serial version of matmult */
           printf("Perform matrix multiplication...\n");



           int sum;
           //#pragma omp parallel
           //{
               #pragma omp parallel for collapse(3) schedule(guided)
               for (i = 0; i < d1; i++)
                   for (j = 0; j < d3; j++)
                       for (k = 0; k < d2; k++){
                       C[i][j] = C[i][j] + A[i][k] * B[k][j];
                       }
           //}


           end = omp_get_wtime();


           /* test output */
           print_mat(A, d1, d2, "A"); 
           print_mat(B, d2, d3, "B"); 
           print_mat(C, d1, d3, "C"); 

           printf("This task took %f seconds\n", end-start);
           printf ("\nDone.\n");

           return 0;
       }

1 个答案:

答案 0 :(得分:2)

正如@genisage在评论中所建议的那样,矩阵的大小可能足够小,以至于初始化附加线程的开销大于通过并行计算矩阵乘法所实现的时间节省。但是,请考虑下面的图,其中包含通过使用和不使用OpenMP运行代码而获得的数据。 Serial vs. Parallel Matrix Multiplication comparison

我使用了从n = 10到n = 1000的平方矩阵。请注意,在n = 50和n = 100之间的某个位置,并行版本变得更快。

然而,在尝试编写快速矩阵乘法时还有其他问题需要考虑,这主要与有效使用缓存有关。首先,您连续分配整个矩阵(这很好),但仍然使用两个指针重定向来访问数据,这是不必要的。此外,您的矩阵以行主格式存储,这意味着您正在连续访问A和C中的数据,但不是在B中。而不是显式存储B并将A行与B列相乘,您将得到一个通过存储B转置并将一行A元素与一行B转置相乘来实现更快的乘法运算。

这是一个仅针对A * B的优化,但是代码中可能存在其他位置,其中存储B优于B转置,在这种情况下,通常执行matrix multiplication by blocking可以提高缓存利用率