这是我上一个问题的后续,结论是程序是错误的,因此预期的行为是未定义的。
我想在这里创建的是一个简单的错误处理机制,为此我使用Irecv请求的空消息作为"中止句柄",将其附加到我的正常MPI_Wait
调用(并将其转换为MPI_WaitAny
),以便允许我在进程0发生错误的情况下解锁进程1,它不能再达到应该发布匹配MPI_Recv
的点。
发生的事情是,由于内部消息缓冲,MPI_Isend
可能会立即成功,而其他进程无法发布匹配的MPI_Recv
。所以没办法消掉了
我希望一旦所有进程调用MPI_Comm_free
,我就可以一劳永逸地忘记这个消息,但是,事实证明,情况并非如此。相反,它将通过以下通信器传递给MPI_Recv
。
我的问题是:
- 这也是一个错误的程序,还是MPI实现(Intel MPI 4.0.3)中的错误?
- 如果我把
MPI_Isend
调用变成MPI_Issend
,程序按预期工作-至少在这种情况下,我可以放心程序是正确的吗? - 我在这里重新发明轮子吗?是否有更简单的方法来实现这一点?
再次,任何反馈是非常感谢!
#include "stdio.h"
#include "unistd.h"
#include "mpi.h"
#include "time.h"
#include "stdlib.h"
int main(int argc, char* argv[]) {
int rank, size;
MPI_Group group;
MPI_Comm my_comm;
srand(time(NULL));
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
MPI_Comm_group(MPI_COMM_WORLD, &group);
MPI_Comm_create(MPI_COMM_WORLD, group, &my_comm);
if (rank == 0) printf("created communicator %dn", my_comm);
if (rank == 1) {
MPI_Request req[2];
int msg = 123, which;
MPI_Isend(&msg, 1, MPI_INT, 0, 0, my_comm, &req[0]);
MPI_Irecv(NULL, 0, MPI_INT, 0, 0, my_comm, &req[1]);
MPI_Waitany(2, req, &which, MPI_STATUS_IGNORE);
MPI_Barrier(my_comm);
if (which == 0) {
printf("rank 1: send succeed; cancelling abort handlen");
MPI_Cancel(&req[1]);
MPI_Wait(&req[1], MPI_STATUS_IGNORE);
} else {
printf("rank 1: send aborted; cancelling send requestn");
MPI_Cancel(&req[0]);
MPI_Wait(&req[0], MPI_STATUS_IGNORE);
}
} else {
MPI_Request req;
int msg, r = rand() % 2;
if (r) {
printf("rank 0: receiving messagen");
MPI_Recv(&msg, 1, MPI_INT, 1, 0, my_comm, MPI_STATUS_IGNORE);
} else {
printf("rank 0: sending abort messagen");
MPI_Isend(NULL, 0, MPI_INT, 1, 0, my_comm, &req);
}
MPI_Barrier(my_comm);
if (!r) {
MPI_Cancel(&req);
MPI_Wait(&req, MPI_STATUS_IGNORE);
}
}
if (rank == 0) printf("freeing communicator %dn", my_comm);
MPI_Comm_free(&my_comm);
sleep(2);
MPI_Comm_create(MPI_COMM_WORLD, group, &my_comm);
if (rank == 0) printf("created communicator %dn", my_comm);
if (rank == 0) {
MPI_Request req;
MPI_Status status;
int msg, cancelled;
MPI_Irecv(&msg, 1, MPI_INT, 1, 0, my_comm, &req);
sleep(1);
MPI_Cancel(&req);
MPI_Wait(&req, &status);
MPI_Test_cancelled(&status, &cancelled);
if (cancelled) {
printf("rank 0: receive cancelledn");
} else {
printf("rank 0: OLD MESSAGE RECEIVED!!!n");
}
}
if (rank == 0) printf("freeing communicator %dn", my_comm);
MPI_Comm_free(&my_comm);
MPI_Finalize();
return 0;
}
输出:
created communicator -2080374784
rank 0: sending abort message
rank 1: send succeed; cancelling abort handle
freeing communicator -2080374784
created communicator -2080374784
rank 0: STRAY MESSAGE RECEIVED!!!
freeing communicator -2080374784
正如@kraffenetti在上面的评论中提到的,这是一个错误的程序,因为发送的消息没有被接收的消息匹配。即使消息被取消了,它们仍然需要在远程端有一个匹配的接收,因为对于已发送的消息来说,取消可能不会成功,因为它们在取消完成之前已经发送了(这里就是这种情况)。
这个问题引发了一个关于MPICH的问题,你可以在这里找到更多的细节。
我尝试使用open mpi构建您的代码,但它不起作用。mpicc抱怨status.cancelled
error: ‘MPI_Status’ has no member named ‘cancelled’
我想这是intel mpi的一个特性。如果您切换为:
会发生什么? ...
int flag;
MPI_Test_cancelled(&status, &flag);
if (flag) {
...
这给出了使用open mpi的预期输出(并且它使您的代码不那么依赖)。
使用英特尔mpi是这种情况吗?我们需要一个专家告诉我们什么是英特尔mpi的status.cancelled
,因为我对它一无所知!
status
中没有设置。部分答案可能在MPI_Wait()
, http://www.mpich.org/static/docs/v3.1/www3/MPI_Wait.html,
"只有当MPI例程的返回值为MPI_ERR_IN_STATUS时,状态返回的MPI_ERROR字段才被设置。该错误类仅由接受状态参数数组(MPI_Testall、MPI_Testsome、MPI_Waitall和MPI_Waitsome)的例程返回。在其他情况下,status中MPI_ERROR字段的值保持不变。请参阅MPI-1.1规范中的3.2.5节以了解确切的文本。如果MPI_Test_cancelled()使用了MPI_ERROR,事情可能会变得很糟糕。
技巧就在这里:使用MPI_Waitall(1,&req, &status)
!