1

这是我之前的问题的后续,其结论是程序错误,因此预期的行为是未定义的。

我在这里尝试创建的是一个简单的错误处理机制,为此我将针对空消息的 Irecv 请求用作“中止句柄”,将其附加到我的正常MPI_Wait调用(并将其转换为MPI_WaitAny),以便允许我取消阻止进程 1,以防进程 0 发生错误并且它无法再到达应该发布匹配的点MPI_Recv

发生的事情是,由于内部消息缓冲,MPI_Isend可能会立即成功,而其他进程无法发布匹配的MPI_Recv. 所以没有办法取消它了。

我希望一旦所有进程调用,MPI_Comm_free我就可以一劳永逸地忘记该消息,但事实证明,情况并非如此。相反,它被传递到MPI_Recv以下通信器中。

所以我的问题是:

  1. 这也是一个错误的程序,还是 MPI 实现(英特尔 MPI 4.0.3)中的一个错误?
  2. 如果我把我的MPI_Isend电话变成MPI_Issend,程序按预期工作 - 我至少在这种情况下可以放心程序是正确的吗?
  3. 我在这里重新发明轮子吗?有没有更简单的方法来实现这一点?

再次,非常感谢任何反馈!


#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 %d\n", 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 handle\n");
            MPI_Cancel(&req[1]);
            MPI_Wait(&req[1], MPI_STATUS_IGNORE);
        } else {
            printf("rank 1: send aborted; cancelling send request\n");
            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 message\n");
            MPI_Recv(&msg, 1, MPI_INT, 1, 0, my_comm, MPI_STATUS_IGNORE);
        } else {
            printf("rank 0: sending abort message\n");
            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 %d\n", my_comm);
    MPI_Comm_free(&my_comm);

    sleep(2);

    MPI_Comm_create(MPI_COMM_WORLD, group, &my_comm);
    if (rank == 0) printf("created communicator %d\n", 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 cancelled\n");
        } else {
            printf("rank 0: OLD MESSAGE RECEIVED!!!\n");
        }
    }

    if (rank == 0) printf("freeing communicator %d\n", 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
4

2 回答 2

2

正如@kraffenetti 的上述评论之一所述,这是一个错误的程序,因为发送的消息与接收不匹配。即使消息被取消,它们仍然需要在远程端有一个匹配的接收,因为对于已发送的消息,取消可能不会成功,因为它们在取消完成之前已经发送(即这里的情况)。

这个问题在 MPICH 的票上启动了一个线程,您可以在此处找到更多详细信息。

于 2014-05-28T18:49:49.047 回答
0

我尝试使用 open mpi 构建您的代码,但没有成功。mpcc 抱怨status.cancelled

  error: ‘MPI_Status’ has no member named ‘cancelled’

我想这是英特尔 mpi 的一个功能。如果您切换到 会发生什么:

    ...
    int flag;
    MPI_Test_cancelled(&status, &flag);
    if (flag) {
    ...

这使用 open mpi 给出了预期的输出(并且它使您的代码不那么依赖)。是使用 intel mpi 的情况吗?

我们需要一位专家来告诉我们status.cancelledintel mpi 中有什么,因为我对此一无所知!

编辑:我多次测试我的答案,我发现输出是随机的,有时是正确的,有时不是。抱歉……好像里面的东西status没有设置。部分答案可能在http://www.mpich.org/static/docs/v3.1/www3/MPI_Wait.htmlMPI_Wait()

" 仅当 MPI 例程的返回为 MPI_ERR_IN_STATUS 时,才会设置状态返回的 MPI_ERROR 字段。该错误类仅由采用状态参数数组(MPI_Testall、MPI_Testsome、MPI_Waital 和 MPI_Waitsome)的例程返回。总而言之其他情况下,状态中 MPI_ERROR 字段的值不变。请参阅 MPI-1.1 规范中的第 3.2.5 节了解确切的文本。“如果 MPI_Test_cancelled() 使用 MPI_ERROR,事情可能会变得很糟糕。

所以这里是诀窍:使用MPI_Waitall(1,&req, &status)!最后输出正确!

于 2014-05-26T22:58:22.733 回答