2012-05-20 148 views
2
 /** 
    * BLOCK_LOW 
    * Returns the offset of a local array 
    * with regards to block decomposition 
    * of a global array. 
    * 
    * @param (int) process rank 
    * @param (int) total number of processes 
    * @param (int) size of global array 
    * @return (int) offset of local array in global array 
    */ 
    #define BLOCK_LOW(id, p, n) ((id)*(n)/(p)) 

    /** 
    * BLOCK_HIGH 
    * Returns the index immediately after the 
    * end of a local array with regards to 
    * block decomposition of a global array. 
    * 
    * @param (int) process rank 
    * @param (int) total number of processes 
    * @param (int) size of global array 
    * @return (int) offset after end of local array 
    */ 
    #define BLOCK_HIGH(id, p, n) (BLOCK_LOW((id)+1, (p), (n))) 

    /** 
    * BLOCK_SIZE 
    * Returns the size of a local array 
    * with regards to block decomposition 
    * of a global array. 
    * 
    * @param (int) process rank 
    * @param (int) total number of processes 
    * @param (int) size of global array 
    * @return (int) size of local array 
    */ 
    #define BLOCK_SIZE(id, p, n) ((BLOCK_HIGH((id), (p), (n))) - (BLOCK_LOW((id), (p), (n)))) 

    /** 
    * BLOCK_OWNER 
    * Returns the rank of the process that 
    * handles a certain local array with 
    * regards to block decomposition of a 
    * global array. 
    * 
    * @param (int) index in global array 
    * @param (int) total number of processes 
    * @param (int) size of global array 
    * @return (int) rank of process that handles index 
    */ 
    #define BLOCK_OWNER(i, p, n) (((p)*((i)+1)-1)/(n)) 



    /*Matricefilenames: 
     small matrix A.bin of dimension 100 × 50 
     small matrix B.bin of dimension 50 × 100 
     large matrix A.bin of dimension 1000 × 500 
     large matrix B.bin of dimension 500 × 1000 

    An MPI program should be implemented such that it can 
    • accept two file names at run-time, 
    • let process 0 read the A and B matrices from the two data files, 
    • let process 0 distribute the pieces of A and B to all the other processes, 
    • involve all the processes to carry out the the chosen parallel algorithm 
    for matrix multiplication C = A * B , 
    • let process 0 gather, from all the other processes, the different pieces 
    of C , 
    • let process 0 write out the entire C matrix to a data file. 
    */ 


    #include <stdio.h> 
    #include <stdlib.h> 
    #include <mpi.h> 
    #include "mpi-utils.c" 
    void read_matrix_binaryformat (char*, double***, int*, int*); 
    void write_matrix_binaryformat (char*, double**, int, int); 
    void create_matrix (double***,int,int); 
    void matrix_multiplication (double ***, double ***, double ***,int,int, int); 

    int main(int argc, char *argv[]) { 
     int id,p; // Process rank and total amount of processes 
     int rowsA, colsA, rowsB, colsB; // Matrix dimensions 
     double **A; // Matrix A 
     double **B; // Matrix B 
     double **C; // Result matrix C : AB 
     int local_rows; // Local row dimension of the matrix A 
     double **local_A; // The local A matrix 
     double **local_C; // The local C matrix 

     MPI_Init (&argc, &argv); 
     MPI_Comm_rank (MPI_COMM_WORLD, &id); 
     MPI_Comm_size (MPI_COMM_WORLD, &p); 

     if(argc != 3) { 
      if(id == 0) { 
       printf("Usage:\n>> %s matrix_A matrix_B\n",argv[0]); 
      }  
      MPI_Finalize(); 
      exit(1); 
     } 

     if (id == 0) { 
      read_matrix_binaryformat (argv[1], &A, &rowsA, &colsA); 
      read_matrix_binaryformat (argv[2], &B, &rowsB, &colsB); 
     } 

     if (p == 1) { 
      create_matrix(&C,rowsA,colsB); 
      matrix_multiplication (&A,&B,&C,rowsA,colsB,colsA); 

      char* filename = "matrix_C.bin"; 
      write_matrix_binaryformat (filename, C, rowsA, colsB); 
      free(A); 
      free(B); 
      free(C); 
      MPI_Finalize(); 
      return 0; 
     } 


     // For this assignment we have chosen to bcast the whole matrix B: 
     MPI_Bcast (&B, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); 
     MPI_Bcast (&colsA, 1, MPI_INT, 0, MPI_COMM_WORLD); 
     MPI_Bcast (&colsB, 1, MPI_INT, 0, MPI_COMM_WORLD); 
     MPI_Bcast (&rowsA, 1, MPI_INT, 0, MPI_COMM_WORLD); 
     MPI_Bcast (&rowsB, 1, MPI_INT, 0, MPI_COMM_WORLD); 

     local_rows = BLOCK_SIZE(id, p, rowsA); 


     /* SCATTER VALUES */ 

     int *proc_elements = (int*)malloc(p*sizeof(int)); // amount of elements for each processor 
     int *displace = (int*)malloc(p*sizeof(int)); // displacement of elements for each processor 
     int i; 
     for (i = 0; i<p; i++) { 
      proc_elements[i] = BLOCK_SIZE(i, p, rowsA)*colsA; 
      displace[i] = BLOCK_LOW(i, p, rowsA)*colsA; 
     } 

     create_matrix(&local_A,local_rows,colsA); 

     MPI_Scatterv(&A[0],&proc_elements[0],&displace[0],MPI_DOUBLE,&local_A[0], 
        local_rows*colsA,MPI_DOUBLE,0,MPI_COMM_WORLD); 

     /* END SCATTER VALUES */ 

     create_matrix (&local_C,local_rows,colsB); 
     matrix_multiplication (&local_A,&B,&local_C,local_rows,colsB,colsA); 

     /* GATHER VALUES */ 

     MPI_Gatherv(&local_C[0], rowsA*colsB, MPI_DOUBLE,&C[0], 
       &proc_elements[0],&displace[0],MPI_DOUBLE,0, MPI_COMM_WORLD); 

     /* END GATHER VALUES */ 

     char* filename = "matrix_C.bin"; 
     write_matrix_binaryformat (filename, C, rowsA, colsB); 

     free (proc_elements); 
     free (displace);  
     free (local_A); 
     free (local_C); 
     free (A); 
     free (B); 
     free (C); 
     MPI_Finalize(); 
     return 0; 
    } 

    void create_matrix (double ***C,int rows,int cols) { 
     *C = (double**)malloc(rows*sizeof(double*)); 
     (*C)[0] = (double*)malloc(rows*cols*sizeof(double)); 
     int i; 
     for (i=1; i<rows; i++) 
      (*C)[i] = (*C)[i-1] + cols; 
    } 

    void matrix_multiplication (double ***A, double ***B, double ***C, int rowsC,int colsC,int colsA) { 
     double sum; 
     int i,j,k; 
     for (i = 0; i < rowsC; i++) { 
      for (j = 0; j < colsC; j++) { 
       sum = 0.0; 
       for (k = 0; k < colsA; k++) { 
        sum = sum + (*A)[i][k]*(*B)[k][j]; 
       } 
       (*C)[i][j] = sum; 
      } 
     } 
    } 

    /* Reads a 2D array from a binary file*/ 
    void read_matrix_binaryformat (char* filename, double*** matrix, int* num_rows, int* num_cols) { 
     int i; 
     FILE* fp = fopen (filename,"rb"); 
     fread (num_rows, sizeof(int), 1, fp); 
     fread (num_cols, sizeof(int), 1, fp); 
     /* storage allocation of the matrix */ 
     *matrix = (double**)malloc((*num_rows)*sizeof(double*)); 
     (*matrix)[0] = (double*)malloc((*num_rows)*(*num_cols)*sizeof(double)); 
     for (i=1; i<(*num_rows); i++) 
      (*matrix)[i] = (*matrix)[i-1]+(*num_cols); 
     /* read in the entire matrix */ 
     fread ((*matrix)[0], sizeof(double), (*num_rows)*(*num_cols), fp); 
     fclose (fp); 
    } 

    /* Writes a 2D array in a binary file */ 
    void write_matrix_binaryformat (char* filename, double** matrix, int num_rows, int num_cols) { 
     FILE *fp = fopen (filename,"wb"); 
     fwrite (&num_rows, sizeof(int), 1, fp); 
     fwrite (&num_cols, sizeof(int), 1, fp); 
     fwrite (matrix[0], sizeof(double), num_rows*num_cols, fp); 
     fclose (fp); 
    } 

我的任务是做矩阵A和B的并行矩阵乘法和聚集在矩阵的结果C.分段故障

我通过把矩阵A的横行片,每个进程这样做将使用它的块乘以矩阵B,并从乘法中取回它的一块。然后我将收集所有流程中的部分,并将它们放在一起到矩阵C.

我已经发布了一个类似的问题,但是此代码已得到改进,并且我已经取得了进展,但仍然在分散呼叫。

+1

请使用调试器来缩小确切位置的故障发生。 SO不是你的个人调试器。 – Mat

+0

我改变了从代码中为文件读取矩阵的代码,只为它们分配空间,留下随机值。运行程序...'程序正常退出。 (gdb)quit' ...我们甚至无法重现关于该代码的错误....您确定您的数据是否正确解码(代码忽略了如何处理数据的特定字节顺序,甚至与正确的大小的数据...例如'sizeof(int)'不需要是生成矩阵二进制文件的机器相同... – ShinTakezou

回答

3

所以我看到了一些问题的时候了:

MPI_Bcast (&B, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); 

在这里,你传递不是一个指针双打,而是一个指针的指针的指针双(B定义为double **B ),并且你告诉MPI遵循那个指针并从那里发送一个双精度值。这是行不通的。

你可能会认为你正在努力完成这里发送指向矩阵,使所有任务都可以读取阵列 - 这是行不通的。这些进程不共享一个共同的内存空间(这就是为什么MPI被称为分布式内存编程),而且指针不会去任何地方。实际上,你将不得不派矩阵的内容,

MPI_Bcast (&(B[0][0]), rowsB*colsB, MPI_DOUBLE, 0, MPI_COMM_WORLD); 

和你将不得不确保其他进程都正确分配内存的时间提前了矩阵B。

有类似指针的问题在其他地方:

MPI_Scatterv(&A[0], ..., &local_A[0] 

再次,是一个指针的指针双打(double **A)如local_A,你需要将指向MPI的指针的双打这个工作,类似于

MPI_Scatterv(&(A[0][0]), ..., &(local_A[0][0]) 

该错误似乎存在于所有通信例程中。

请记住,任何看起来像MPI (buffer, count, TYPE)意味着,MPI程序遵循指针buffer和发送下一个countTYPE类型存在的数据。 MPI无法遵循您发送的缓冲区内的指针,因为通常它不知道它们在那里。它只需要指针buffer的下一个(count * sizeof(TYPE))字节,并执行任何适合于它们的通信。所以你必须传递一个指向类型为TYPE的数据流的指针。

话虽如此,这将是一个更容易在此与您合作,如果你已经缩小东西下来了一点;现在你发布的程序中包含很多不相关的I/O内容,这意味着如果不先计算矩阵格式,然后自行生成两个矩阵,那么没有人可以运行程序来查看发生了什么。在发布有关源代码的问题时,您确实需要发布一小段源代码(b)来重现问题,并且(c)是完全独立的。

+0

1+为您的完整答案。 – peaceman

2

认为这是一个扩展的批评是乔纳森·德西已经给予了相当详尽的答案。你的矩阵实际上是以一种奇怪的方式表示的,但至少你遵循了给你的另一个问题的建议,并为它们分配空间作为连续的块,而不是为每行分开。

鉴于这种情况,你应该更换:

MPI_Scatterv(&A[0],&proc_elements[0],&displace[0],MPI_DOUBLE,&local_A[0], 
      local_rows*colsA,MPI_DOUBLE,0,MPI_COMM_WORLD); 

MPI_Scatterv(A[0],&proc_elements[0],&displace[0],MPI_DOUBLE,local_A[0], 
      local_rows*colsA,MPI_DOUBLE,0,MPI_COMM_WORLD); 

A[0]已经指向矩阵数据的开始,也没有必要做一个指针。对于local_A[0]以及MPI_Gatherv()调用的参数也是如此。

已经说过很多次了 - MPI不做指针追踪,只能用于平缓冲区。

我也注意到你的代码中的另一个错误 - 你的矩阵的内存没有正确释放。你只释放指针数组,而不是矩阵数据本身:

free(A); 

应该真正成为

free(A[0]); free(A);