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 にまとめます。

私はすでに同様の質問を投稿しましたが、このコードは改善され、進歩しましたが、scatterv 呼び出しの後にまだセグメンテーション違反が発生しています。

4

2 に答える 2

3

したがって、すぐにいくつかの問題が発生します。

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

ここでは、double へのポインターではなく、double へのポインターへのポインター (B は として定義されていますdouble **B) を渡し、MPI にそのポインターをたどってそこから 1 つの double を送信するように指示しています。それはうまくいきません。

ここで達成しているのは、すべてのタスクがそこから配列を読み取ることができるマトリックスへのポインターを送信していると思うかもしれませんが、これは機能しません。プロセスは共通のメモリ空間を共有せず (MPI が分散メモリ プログラミングと呼ばれるのはそのためです)、ポインターはどこにも移動しません。実際には、マトリックスの内容を送信する必要があります。

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

また、他のプロセスが事前に B マトリックスにメモリを正しく割り当てていることを確認する必要があります。

他の場所にも同様のポインタの問題があります:

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

繰り返しますが、 A は double へのポインターへのポインター ( double **A) as islocal_Aであり、これを機能させるには、MPI を double へのポインターにポイントする必要があります。

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

そのエラーは、すべての通信ルーチンに存在するようです。

MPI でのように見えるものはすべて(buffer, count, TYPE)、MPI ルーチンがポインターをたどり、そこに型の次のデータをbuffer送信することを意味することに注意してください。MPI は、通常、送信されたバッファー内のポインターを追跡できません。これは、一般に、ポインターが存在することを認識していないためです。ポインタから次のバイトを取得し、適切な通信を行います。そのため、TYPE 型のデータ ストリームへのポインタを渡す必要があります。countTYPE(count * sizeof(TYPE))buffer

そうは言っても、物事を少し絞り込んでおけば、これについてあなたと一緒に作業するのはずっと簡単でしょう。現在、あなたが投稿したプログラムには、無関係な I/O 要素がたくさん含まれています。つまり、最初にマトリックス形式を理解してから、2 つのマトリックスを独自に生成することなく、プログラムを実行して何が起こるかを確認することはできません。ソース コードに関する質問を投稿するときは、(a) (b) 問題を再現し、(c) 完全に自己完結型の小さなソースを投稿する必要があります。

于 2012-05-20T15:51:59.187 に答える
2

Jonathan Dursi がすでにかなり精巧な回答をしているので、これは拡張されたコメントと考えてください。マトリックスは実際には奇妙な方法で表現されていますが、少なくとも他の質問に与えられたアドバイスに従い、行ごとに個別にではなく、連続したブロックとしてそれらにスペースを割り当てます。

それを考えると、次のものを置き換える必要があります。

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);
于 2012-05-20T16:24:35.210 に答える