【问题标题】:MPI: Process 0 executing its code twiceMPI:进程 0 执行其代码两次
【发布时间】:2016-03-03 18:58:03
【问题描述】:

我在使用 MPI 程序时遇到了一个奇怪的问题。部分代码应该只由根(进程零)执行,但进程零似乎执行了两次。例如,

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 和两个 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

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

【问题讨论】:

  • 对于这么小的 n,N*N/p 将评估为 0。这似乎是一个问题。你试过N>16,P=16吗?
  • 这似乎给了我一个分段错误。我不认为N*N/p 评估为零;添加打印语句显示它为 4,P=16 和 N=4。通知p = sqrt(P)

标签: c++ parallel-processing mpi


【解决方案1】:

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

当你Scatter时你在做什么?您将 4 个元素分别发送到 16 个进程。 MPI 将假设根上的 A 包含 64 个元素,但它只包含 16 个。此外,您将它们存储在 A[start] 的所有等级中。我什至不知道这是否精确定义,但它应该等于A[start][0],当rank &gt;= 4 时,它超出了为A 分配的内存。所以你已经读写了无效的内存。极度无效的内存访问在循环中继续,Gather

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

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

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

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

  • Gather 之前不需要Barrier

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

  • 永远不要将两个变量命名为 Pp

现在,除了使用调试工具之外,我还在为我应该向您推荐什么而苦苦挣扎。如果您需要快速并行矩阵乘法 - 使用库。如果您想编写出色的高级代码作为练习 - 使用 boost::mpi 和一些高级矩阵抽象。如果您想编写低级代码作为练习 - 使用 std::vector&lt;&gt;(N*N),构建您自己的 2D 索引并仔细考虑如何对其进行索引以及如何访问正确的内存块。

【讨论】:

    猜你喜欢
    • 2011-10-12
    • 2011-12-08
    • 2015-12-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多