2017-10-20 201 views
0

您好,我是MPI編程新手。我試圖將兩個矩陣相乘(一個NxN矩陣(A)和一個Nx1(B)矩陣)以得到一個結果C矩陣(Nx1)。每個進程都應該計算矩陣C中的一行(元素),但只有進程0(我的主進程)可以正確計算,因爲它似乎沒有等待其他進程完成計算。我也不確定非主過程是否正確地發回結果(或者他們甚至需要?)。這裏是我的代碼:MPI主進程不等待其他進程計算

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include "mpi.h" 

#define PRINT_VECS 1 
#define MAX_RAND 100 
#define MASTER 0 
#define COLUMNS_B 1 
#define N 4 

void init_vec(int *vec, int len); 
void print_vec(const char *label, int *vec, int len); 

void init_vec(int *vec, int len) 
{ 
    int i; 
    for (i = 0; i < len; i++) 
    { 
     vec[i] = rand() % MAX_RAND; 
    }  
} 

void print_vec(const char *label, int *vec, int len) 
{ 
#if PRINT_VECS 
    printf("%s", label);  
    int i; 
    for (i = 0; i < len; i++) 
    { 
     printf("%d ", vec[i]); 
    } 
    printf("\n\n"); 
#endif 
} 

void init_matrix(int** matrix, int rows, int cols) 
{ 
    int i,j; 
    for (i = 0; i < rows; i++) 
    { 
     for (j = 0; j < cols; j++) 
     { 
      matrix[i][j] = rand() % MAX_RAND; 
     } 
    } 
} 

void print_matrix(int** matrix, int rows, int cols) 
{ 
    int i; 
    for (i = 0; i < rows; i++) 
    { 
     printf("|"); 
     int j; 
     for (j = 0; j < cols; j++) 
     { 
      printf("%d ", matrix[i][j]); 
     } 
     printf("|\n"); 
    } 
} 


int main(int argc, char *argv[]) 
{ 
    int my_rank; 
    int num_procs; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); //grab this process's rank 
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs); //grab the total num of processes 

    int results[num_procs]; // used to store the partial sum computed 
    int rows, cols, colsB; 
    rows = N; 
    cols = N; 
    colsB = COLUMNS_B; 
    int **A; // N x N Matrix 
    int B[N]; // N x 1 Matrix 
    int **C; // N x 1 Matrix 

    double start_time; // use these for timing 
    double stop_time; 

    if (my_rank == MASTER) 
    { 
     printf("Number of processes: %d\n", num_procs); 
     printf("N: %d\n", N); 
     srand(time(NULL)); 

     // init A 
     int i; 
     A = malloc(rows * sizeof *A); 
     for (i = 0; i < rows; i++) 
     { 
      A[i] = malloc(cols * sizeof *A[i]); 
     } 
     init_matrix(A, rows, cols); 
     printf("Matrix A:\n"); 
     print_matrix(A, rows, cols); 

     // init B 
     init_vec(B, N); 
     print_vec("Matrix B:\n", B, N); 

     // init C 
     C = malloc(rows * sizeof *C); 
     for (i = 0; i < rows; i++) 
     { 
      C[i] = malloc(colsB * sizeof *C[i]); 
     } 

     start_time = MPI_Wtime(); 
    } 

    MPI_Bcast(B, N, MPI_INT, 0, MPI_COMM_WORLD); 
    //MPI_Bcast(A, N, MPI_INT, 0, MPI_COMM_WORLD); 

    int row = my_rank; 

    int my_sum = 0; 

    int i; 
    if (my_rank < N) 
    { 
     for (i = 0; i < N; i++) 
     { 
      int num = A[row][i] * B[i]; 
      my_sum = my_sum + num; 
     } 



    C[row] = &my_sum; 
    printf("HAI FROM PROCESS %d! I will calculate row %d. My calculation: %d\n", my_rank, row, my_sum); 
} 

//MPI_Gather(&C, 1, MPI_INT, results, 1, MPI_INT, 0, MPI_COMM_WORLD); 

if (my_rank == MASTER) 
{ 
    stop_time = MPI_Wtime(); 
    printf("\nMatrix C:\n"); 
    print_matrix(C, rows, colsB); 
    printf("Total time (sec): %f\n", stop_time - start_time); 
} 

MPI_Finalize(); 

return EXIT_SUCCESS;; 

}

我敢肯定,我很接近,但我只是失去了一些東西。我嘗試添加一些註釋掉的語句,同時也播放A矩陣和/或調用MPI_GATHER,但似乎沒有給出任何proc以外的任何proc的結果,所以顯然我仍然做錯了什麼。下面是一些示例輸出:

Number of processes: 28 
N: 4 
Matrix A: 
|11 30 69 24 | 
|83 38 66 71 | 
|68 71 27 33 | 
|58 5 50 10 | 
Matrix B: 
1 58 81 44 

HAI FROM PROCESS 0! I will calculate row 0. My calculation: 8396 

Matrix C: 
|8396 | 
|-2107258888 | 
|-2107258920 | 
|-2107258888 | 
Total time (sec): 0.000078 

所以PROC 0被正確地計算,但我的錯誤信息是PROC 1是得到一個賽格故障,我想不通爲什麼。我得到的錯誤是: mpirun noticed that process rank 1 with PID 0 exited on signal 11 (Segmentation fault).任何幫助將不勝感激!

+2

爲了使這個問題可以被解析,你需要提供你沒有提供的函數,比如'init_matrix'。除此之外,你使用這個常量'MASTER',但是在你的'BCAST'中,你使用0作爲根進程。堅持一個,以防他們不一樣。更重要的是,你不會在所有進程上分配內存,只是在根上。這可能是你的程序失敗的原因之一。 – atru

+0

此外,這是一個矩陣向量乘法..我有這個問題解決了靜態數組和一個typedef而不是malloc。發佈你的整個代碼,我們可以找出問題所在。在我使用malloc的問題中,我也在每個進程上分配數組。 Malloc返回一個指向內存位置的指針。 MPI不是共享內存協議。原則上,您的代碼可以運行在許多不同的獨立機器上的不同節點上。指向其中一臺機器上的內存位置的指針在其他機器上沒有任何意義。 – atru

+0

@atru謝謝你的迴應!我已經添加了我的其他代碼。作爲響應,矩陣是隨機生成的,所以只有主進程正在生成它們。而'MASTER' == 0,所以我可以互換使用它們,並且不會在地方保持一致。而且我也知道B是一個矢量而不是矩陣,但我將它當作矩陣處理。因爲我知道他們正在正常工作,所以我不覺得需要包括其他方法,但我現在已經發布了它們。 – k1234

回答

1

這是固定的問題你的程序:

#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 
#include "mpi.h" 

#define PRINT_VECS 1 
#define MAX_RAND 100 
#define MASTER 0 
#define COLUMNS_B 1 
#define N 4 

void init_vec(int *vec, int len); 
void print_vec(const char *label, int *vec, int len); 

void init_vec(int *vec, int len) 
{ 
    int i; 
    for (i = 0; i < len; i++) 
    { 
     vec[i] = rand() % MAX_RAND; 
    } 
} 

void print_vec(const char *label, int *vec, int len) 
{ 
#if PRINT_VECS 
    printf("%s", label); 
    int i; 
    for (i = 0; i < len; i++) 
    { 
     printf("%d ", vec[i]); 
    } 
    printf("\n\n"); 
#endif 
} 

void init_matrix(int** matrix, int rows, int cols) 
{ 
    int i,j; 
    for (i = 0; i < rows; i++) 
    { 
     for (j = 0; j < cols; j++) 
     { 
      matrix[i][j] = rand() % MAX_RAND; 
     } 
    } 
} 

void print_matrix(int** matrix, int rows, int cols) 
{ 
    int i; 
    for (i = 0; i < rows; i++) 
    { 
     printf("|"); 
     int j; 
     for (j = 0; j < cols; j++) 
     { 
      printf("%d ", matrix[i][j]); 
     } 
     printf("|\n"); 
    } 
} 


int main(int argc, char *argv[]) 
{ 
    int my_rank; 
    int num_procs; 
    MPI_Init(&argc, &argv); 
    MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); //grab this process's rank 
    MPI_Comm_size(MPI_COMM_WORLD, &num_procs); //grab the total num of processes 

    int results[num_procs]; // used to store the partial sum computed 
    int rows, cols, colsB, k; 
    rows = N; 
    cols = N; 
    colsB = COLUMNS_B; 
    int **A; // N x N Matrix 
    int B[N]; // N x 1 Matrix 
    int C[N]; // N x 1 Matrix 

    // Allocate memory for the NxN matrix on all processes 
    A = (int**) malloc(N * sizeof(int*)); 
    for(k=0;k<N;k++) 
     A[k]= (int*) malloc(N * sizeof(int)); 

    double start_time; // use these for timing 
    double stop_time; 

    if (my_rank == MASTER) 
    { 
     printf("Number of processes: %d\n", num_procs); 
     printf("N: %d\n", N); 
     srand(time(NULL)); 

     // Initilize arrays on root only 
     init_matrix(A, rows, cols); 
     printf("Matrix A:\n"); 
     print_matrix(A, rows, cols); 

     init_vec(B, N); 
     print_vec("Matrix B:\n", B, N); 

     start_time = MPI_Wtime(); 
    } 

    // Be consistent with names vs. values to avoid bugs 
    MPI_Bcast(B, N, MPI_INT, MASTER, MPI_COMM_WORLD); 

    for (k=0; k<N; k++) 
     MPI_Bcast(&(A[k][0]), N, MPI_INT, MASTER, MPI_COMM_WORLD); 

    int row = my_rank; 
    int my_sum = 0; 

    int i,num; 
    if (my_rank < N) 
    { 
     for (i = 0; i < N; i++) 
     { 
      num = A[row][i] * B[i]; 
      my_sum = my_sum + num; 
     } 

     C[row] = my_sum; 
     printf("HAI FROM PROCESS %d! I will calculate row %d. My calculation: %d\n", my_rank, row, my_sum); 
    } 

    MPI_Gather(&C[row], 1, MPI_INT, &C[row], 1, MPI_INT, MASTER, MPI_COMM_WORLD); 

    if (my_rank == MASTER) 
    { 
     stop_time = MPI_Wtime(); 
     print_vec("Matrix C:\n", C, N); 
     printf("Total time (sec): %f\n", stop_time - start_time); 
    } 

    // Free matrix A 
    for(k=0;k<N;k++) 
     free(A[k]); 
    free(A); 

    MPI_Finalize(); 

    return EXIT_SUCCESS; 
} 

就像在評論中說的那樣,在這種情況下,您需要爲所有進程中的所有矩陣分配內存。該過程與程序僅在根進程中進行的A和B的初始化不同。這裏A是用malloc分配的,而C是靜態分配的,並且進一步用作與B相同方式的向量。這不是必須的,但似乎是更好的選擇,因爲C是一維數組,它的本質等價於B.

B會像以前一樣廣播到所有進程,但不是0,程序使用MASTER因此,如果您有任何機會改變MASTER的值,它的所有出現次數也會改變。這通常是一種很好的編程習慣,並且在新代碼中無處不在。

A是在一個簡單的但肯定效率較低的方式廣播比@Gilles Gouailardet建議 - 程序只是簡單地廣播的單獨的每一行,

for (k=0; k<N; k++) 
    MPI_Bcast(&(A[k][0]), N, MPI_INT, MASTER, MPI_COMM_WORLD); 

這是爲行主要排序,事實上,相關A中第k行的這N個元素是連續訪問的。如果按列發送A,則這會失敗。

剩餘改變分配的my_sum值而不是指向它的指針到C[row]C[row] = my_sum;和聚集操作:

MPI_Gather(&C[row], 1, MPI_INT, &C[row], 
     1, MPI_INT, MASTER, MPI_COMM_WORLD); 

在此,每個進程發送其值C[row]C[row]根處理。 C使用print_vec函數打印在根上。

+0

Ooooh現在我明白了很多......我需要在所有進程上分配A,但只能在主線程上初始化......我明白了!非常感謝你的明確解釋。 – k1234

+0

很高興解釋一下:)你在每一個進程上都需要A的空間,所以你有空間來存儲A從根發送的空間 - 這更不用說這個想法。 – atru

1
C

,2D矩陣是指針的陣列,這是行重大的,這意味着一個二維矩陣是一個指針數組,和一個給定的指針指向的行(一列)。

這就是說,MPI預計行在連續的內存中,因此,一個指針數組不適合MPI。

靜態數組非常合適,但如果您需要動態數組,則需要一次性分配您的矩陣,然後手動構建指針數組。

所以,你可以替換

A = malloc(rows * sizeof *A); 
    for (i = 0; i < rows; i++) 
    { 
     A[i] = malloc(cols * sizeof *A[i]); 
    } 

A = (int **)malloc(cols * sizeof(int *); 
    A[0] = (int *)malloc(cols * rows * sizeof(int)); 
    for (i = 1; i < cols; i++) 
    { 
     A[i] = A[i-1] + rows; 
    } 

,然後你就可以用

MPI_Bcast(A[0], cols*rows, MPI_INT, 0, MPI_COMM_WORLD); 

請參考MPI_Bcast a dynamic 2d array一個漫長的解釋

廣播你的矩陣

fwiw,我不明白你的關於C數據的邏輯。 據我瞭解,這是一個簡單的載體,所以其聲明是這樣,不與指針玩(因爲MPI不能正確處理它們)