MPI:进程0执行其代码两次

时间:2016-03-03 18:58:03

标签: c++ parallel-processing mpi

我对MPI程序有一个奇怪的问题。部分代码应该只由root(进程零)执行,但进程零似乎执行两次。例如,

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

给出

  

来自流程0的Hello

     

来自流程0的Hello

这似乎只发生在我使用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的程序和两个4x4矩阵时:

>$ 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 个答案:

答案 0 :(得分:1)

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

Scatter时你做什么?您将每个元素发送到16个进程。 MPI将假定根上的A包含64个元素,但它只包含16个。此外,您将它们存储在A[start]的所有等级中。我甚至不知道这是否已经完全定义,但它应该等于A[start][0]Arank >= 4的内存已经超出Gather。所以你已经读写了无效内存。极其无效的内存访问在循环中继续mpirun -np 16 valgrind ...

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

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

  • MPI的C ++绑定已被弃用多年。你应该 要么使用C ++中的C绑定,要么使用高级绑定 Boost.MPI

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

  • Gather之前,您不需要assert

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

  • 永远不要将两个变量命名为pboost::mpi

现在除了使用调试工具之外,我还在努力建议你。如果需要快速并行矩阵乘法 - 使用库。如果你想编写漂亮的高级代码作为练习 - 使用std::vector<>(N*N)和一些高级矩阵抽象。如果你想编写低级代码作为练习 - 使用mt = Mock(Thing) Thing.main(mt) print(mt.mock_calls) [call.alpha(), call.bravo()] ,建立你自己的2D索引并仔细考虑如何索引它以及如何访问正确的内存块。