我对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
为什么打印出第一个结果? 如果有人愿意帮助我,我真的很感激。
答案 0 :(得分:1)
您有未定义的行为/您正在破坏您的记忆。让我们以N=4
,P=16
,p=4
为例。因此start=rank
。
Scatter
时你做什么?您将每个元素发送到16个进程。 MPI将假定根上的A
包含64个元素,但它只包含16个。此外,您将它们存储在A[start]
的所有等级中。我甚至不知道这是否已经完全定义,但它应该等于A[start][0]
,A
时rank >= 4
的内存已经超出Gather
。所以你已经读写了无效内存。极其无效的内存访问在循环中继续mpirun -np 16 valgrind ...
。
不幸的是,MPI程序很难调试,特别是在内存损坏方面。有非常有价值的信息for OpenMPI。阅读整个页面! Barrier
会告诉你这个问题。
其他一些值得注意的问题:
MPI的C ++绑定已被弃用多年。你应该 要么使用C ++中的C绑定,要么使用高级绑定 Boost.MPI
可变长度数组不是标准C ++。
在Gather
之前,您不需要assert
。
确保您的代码没有未经检查的假设。 P
P是正方形,如果你需要它,N可以被p整除,如果你需要的话。
永远不要将两个变量命名为p
和boost::mpi
。
现在除了使用调试工具之外,我还在努力建议你。如果需要快速并行矩阵乘法 - 使用库。如果你想编写漂亮的高级代码作为练习 - 使用std::vector<>(N*N)
和一些高级矩阵抽象。如果你想编写低级代码作为练习 - 使用mt = Mock(Thing)
Thing.main(mt)
print(mt.mock_calls)
[call.alpha(), call.bravo()]
,建立你自己的2D索引并仔细考虑如何索引它以及如何访问正确的内存块。