2016-03-03 21 views
0

我在MPI程序中遇到了一个奇怪的问题。部分代码应该仅由根(进程0)执行,但进程0似乎执行两次。例如,MPI:进程0两次执行其代码

root = 0; 
if (rank == root) { 
    cout << "Hello from process " << rank << endl; 
} 

你好从进程0

你好从进程0

这似乎当我使用16个或多个进程才会发生。我一直试图调试这几天,但不能。

由于我不知道为什么会发生这种情况,我想我必须在这里复制我的整个代码。我说得很好,很清楚。目标是乘以两个矩阵(简化假设)。问题发生在最后的if块中。

#include <iostream> 
#include <cstdlib> 
#include <cmath> 
#include "mpi.h" 

using namespace std; 

int main(int argc, char *argv[]) { 
    if (argc != 2) { 
     cout << "Use one argument to specify the N of the matrices." << endl; 
     return -1; 
    } 

    int N = atoi(argv[1]); 
    int A[N][N], B[N][N], res[N][N]; 

    int i, j, k, start, end, P, p, rank; 

    int root=0; 
    MPI::Status status; 

    MPI::Init(argc, argv); 

    rank = MPI::COMM_WORLD.Get_rank(); 
    P = MPI::COMM_WORLD.Get_size(); 
    p = sqrt(P); 

    /* Designate the start and end position for each process. */ 
    start = rank * N/p; 
    end = (rank+1) * N/p; 

    if (rank == root) { // No problem here 
     /* Initialize matrices. */ 
     for (i=0; i<N; i++) 
      for (j=0; j<N; j++) { 
       A[i][j] = N*i + j; 
       B[i][j] = N*i + j; 
      } 

     cout << endl << "Matrix A: " << endl; 
     for(i=0; i<N; ++i) 
      for(j=0; j<N; ++j) { 
       cout << " " << A[i][j]; 
       if(j==N-1) 
        cout << endl; 
      } 

     cout << endl << "Matrix B: " << endl; 
     for(i=0; i<N; ++i) 
      for(j=0; j<N; ++j) { 
       cout << " " << B[i][j]; 
       if(j==N-1) 
        cout << endl; 
      } 
    } 

    /* Broadcast B to all processes. */ 
    MPI::COMM_WORLD.Bcast(B, N*N, MPI::INT, 0); 

    /* Scatter A to all processes. */ 
    MPI::COMM_WORLD.Scatter(A, N*N/p, MPI::INT, A[start], N*N/p, MPI::INT, 0); 
    /* Compute your portion of the final result. */  
    for(i=start; i<end; i++) 
     for(j=0; j<N; j++) { 
      res[i][j] = 0; 
      for(k=0; k<N; k++) 
       res[i][j] += A[i][k]*B[k][j]; 
     } 

    MPI::COMM_WORLD.Barrier(); 
    /* Gather results form all processes. */  
    MPI::COMM_WORLD.Gather(res[start], N*N/p, MPI::INT, res, N*N/p, MPI::INT, 0); 


    if (rank == root) { // HERE is the problem! 
     // This chunk executes twice in process 0 
     cout << endl << "Result of A x B: " << endl; 
     for(i=0; i<N; ++i) 
      for(j=0; j<N; ++j) { 
       cout << " " << res[i][j]; 
       if(j == N-1) 
        cout << endl; 
      } 
    } 

    MPI::Finalize(); 
    return 0; 
} 

当运行具有P = 16和两个4×4矩阵中的程序:

>$ mpirun -np 16 ./myprog 4 

Matrix A: 
    0 1 2 3 
    4 5 6 7 
    8 9 10 11 
    12 13 14 15 

Matrix B: 
    0 1 2 3 
    4 5 6 7 
    8 9 10 11 
    12 13 14 15 

Result of A x B: 
    6366632 0 0 0 
    -12032 32767 0 0 
    0 0 -1431597088 10922 
    1 10922 0 0 

Result of A x B: 
    56 62 68 74 
    152 174 196 218 
    248 286 324 362 
    344 398 452 506 

为什么打印出该第一结果? 如果有人愿意帮助我,我将不胜感激。

+1

有了这么小的n,'N * N/p'将评估为0.这似乎是一个问题。你试过N> 16,P = 16吗? – NoseKnowsAll

+0

这似乎给我一个分段错误。我不认为'N * N/p'正在评估为零;添加打印语句显示它是4,P = 16和N = 4。注意'p = sqrt(P)'。 – Novice

回答

1

你有未定义的行为/你正在破坏你的记忆。以N=4P=16p=4为例。因此start=rank

当你做什么Scatter?你发送4个元素到16个进程。 MPI将假设A的根目录包含64个元素,但它只包含16个。此外,您将它们存储在A[start]的所有级别中。我甚至不知道它是否被精确定义,但它应该等于A[start][0],当rank >= 4时,它不在A的分配内存中。所以你已经读取和写入无效的内存。内存访问非常无效,在循环中继续并Gather

不幸的是,MPI程序很难调试,特别是在内存损坏方面。有非常有价值的信息for OpenMPI。阅读整个页面! mpirun -np 16 valgrind ...会告诉你这个问题。

其他一些值得注意的问题:

  • C++的MPI的绑定已弃用多年。您应该使用C++中的C绑定或高级别绑定,如 Boost.MPI

  • 变长数组不是标准的C++。

  • 你不需要在Gather之前Barrier

  • 确保您的代码没有充满未经检查的假设。做assert P是正方形的,如果你需要它,N可以被p整除,如果你需要的话。

  • 从不命名两个变量Pp

现在我正在努力,除了使用调试工具之外,我还应该推荐您。如果你需要一个快速的并行矩阵乘法 - 使用一个库。如果你想编写漂亮的高级代码 - 使用boost::mpi和一些高级矩阵抽象。如果您想编写低级代码作为练习 - 请使用std::vector<>(N*N),请构建您自己的2D索引,并仔细考虑如何为其编制索引以及如何访问正确的内存块。