2012-09-17 54 views
0

我想在C中使用MPI進行矩陣乘法,我們必須做一個順序版本和一個並行版本。我的平行版本沒有給出正確的答案,我不知道爲什麼。我想我沒有給流程發送正確的溝通信息,但我無法確定。教授剛剛瀏覽了不同的發送/接收/收集等信息,但沒有詳細說明......我見過很多不同的例子,但沒有完成,也沒有使用分散/收集。如果任何人都可以看看我的代碼,並告訴我是否有任何東西出現在他們身上,我會很感激。我很確定我的問題出現在分散/收集消息或c矩陣的實際計算中。帶分散聚集的MPI矩陣乘法

#define N 512 

#include <stdio.h> 
#include <math.h> 
#include <sys/time.h> 
#include <stdlib.h> 
#include <stddef.h> 
#include "mpi.h" 


print_results(char *prompt, float a[N][N]); 

int main(int argc, char *argv[]) 
{ 
    int i, j, k, rank, size, tag = 99, blksz, sum = 0; 
    float a[N][N], b[N][N], c[N][N]; 
    char *usage = "Usage: %s file\n"; 
    FILE *fd; 
    double elapsed_time, start_time, end_time; 
    struct timeval tv1, tv2; 

    MPI_Init(&argc, &argv); 
    MPI_Comm_size(MPI_COMM_WORLD, &size); 
    MPI_Comm_rank(MPI_COMM_WORLD, &rank); 

    if (argc < 2) { 
      fprintf (stderr, usage, argv[0]); 
      return -1; 
    } 

    if ((fd = fopen (argv[1], "r")) == NULL) { 
      fprintf (stderr, "%s: Cannot open file %s for reading.\n", 
            argv[0], argv[1]); 
      fprintf (stderr, usage, argv[0]); 
      return -1; 
    } 

    for (i = 0; i < N; i++) 
      for (j = 0; j < N; j++) 
        fscanf (fd, "%f", &a[i][j]); 

    for (i = 0; i < N; i++) 
      for (j = 0; j < N; j++) 
        fscanf (fd, "%f", &b[i][j]); 

    MPI_Barrier(MPI_COMM_WORLD); 

    gettimeofday(&tv1, NULL); 

    MPI_Scatter(a, N*N/size, MPI_INT, a, N*N/size, MPI_INT, 0, 
                MPI_COMM_WORLD); 
    MPI_Bcast(b, N*N, MPI_INT, 0, MPI_COMM_WORLD); 


    if (rank != 0) { 
      for (i = 0; i < N; i++) 
      { 
        for (j = 0; j < N; j++) 
        { 
          for (k = 0; k < N; k++) 
          { 
            sum = sum + a[i][k] * b[k][j]; 
          } 
          c[i][j] = sum; 
          sum = 0; 
        } 
      } 
    } 

    MPI_Gather(c, N*N/size, MPI_INT, c, N*N/size, MPI_INT, 0, 
                MPI_COMM_WORLD); 
    MPI_Finalize(); 

    gettimeofday(&tv2, NULL); 

    elapsed_time = (tv2.tv_sec - tv1.tv_sec) + ((tv2.tv_usec - tv1.tv_usec)/1000000.0); 

    printf ("elapsed_time=\t%lf (seconds)\n", elapsed_time); 

    print_results("C = ", c); 
} 

print_results(char *prompt, float a[N][N]) 
{ 
    int i, j; 

    printf ("\n\n%s\n", prompt); 
    for (i = 0; i < N; i++) { 
      for (j = 0; j < N; j++) { 
        printf(" %.2f", a[i][j]); 
      } 
      printf ("\n"); 
    } 
    printf ("\n\n"); 
} 

更新的代碼部分:

for (i=0;i<size; i++) 
      { 
        if (rank == i) 
        { 
          for (i = rank*(N/size); i < (rank*(N/size)+(N/size)); i++) 
          { 
            for (j = rank*(N/size); j < (rank*(N/size)+(N/size)); j++) 
            { 
              for (k = rank*N; k < rank*N+N; k++) 
              { 
                sum = sum + a[i][k] * b[k][j]; 
              } 
              c[i][j] = sum; 
              sum = 0; 
            } 
          } 
        } 
      } 
+0

有一件事跳出來:根應該在分散和收集中爲'recvbuf'提供神奇值'MPI_IN_PLACE'。只是爲'sendbuf'和'recvbuf'提供相同的指針在技術上違反了規範。另外,'MPI_Finalize'可能需要很長時間。你應該不會計時。 –

+1

對不起,'MPI_IN_PLACE'應該在'MPI_Gather'中爲'sendbuf'提供。此外,根參與分散和收集,但是然後您將其排除在計算之外。它應該像其他行列一樣參與進來。 –

+0

除了Greg Inozemtsev和Francesco已經發現的內容之外,您的計算內核在整個矩陣「a」中循環,而不僅僅是駐留在當前級別內存中的部分。 「i」循環的範圍應該相應地加以限制。 MPI還提供'MPI_Wtime()'定時器函數,它具有與'gettimeofday'相同的精度,因爲MPI標準建議實現者使用最高分辨率的定時器。 –

回答

3

代碼中的第一個問題是size可能不會分裂N。這意味着分散size數據包的長度爲N*N/size並不一定會發送整個矩陣。這可能是正確的最難的一點。

正如Greg Inozemtsev指出的那樣,第二個問題是您將進程0排除在計算之外,儘管它負責矩陣的一部分。

而另一個問題是,所有的I/O操作(讀取開頭的係數和輸出在最後的結果)應該僅由工藝0

做在另一方面,你應該指定返回類型(在這種情況下,voidprint_result函數,無論是在前向聲明還是在定義中。

+0

我認爲我現在唯一困惑的是如何讓每個處理器只在他們負責的矩陣部分工作...... – user939287

+1

想想你要並行化的原始版本中的哪一個循環。你已經在所有進程中分配了矩陣行,這意味着你在循環上並行化'i'。現在每個進程只需要處理其「N/size」行。 (再說一次,只有'size'除以'N'纔有效。) – Francesco

+0

好吧,N被定義爲512,我們只使用1,4,8,16和32個處理器,所以它總是平均的。我在採納你的建議後(我把它放在我的原始文章中,上面)中添加了一些代碼,我想我已經想通了,但是每當我使用多個處理器運行它時,我都會遇到一個段錯誤「mpirun注意到進程等級2爲PID節點上的30215 compute-1-2.local在信號11上退出(分段錯誤)「 – user939287