【问题标题】:MPI_Irecv does not receive all sends?MPI_Irecv 不接收所有发送?
【发布时间】:2012-11-17 01:33:01
【问题描述】:

我想在这个简化的代码中实现的是:

  • 2 种类型的进程(root 和 children,ids/rank 分别 = 10 和 0-9)
  • 初始化:
    • root 将听孩子“完成”
    • 孩子们将在所有完成后收听根通知
  • 虽然没有获胜者(尚未全部完成):
    • 孩子们将有 20% 的机会完成(并通知 root 他们完成了)
    • root 将检查所有操作是否已完成
      • 如果全部完成:向“获胜者”的孩子发送通知

我有这样的代码:

int numprocs, id, arr[10], winner = -1;
bool stop = false;
MPI_Request reqs[10], winnerNotification;

MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &id);

for (int half = 0; half < 1; half++) {
    for (int round = 0; round < 1; round++) {
        if (id == 10) { // root
            // keeps track of who has "completed"
            fill_n(arr, 10, -1);
            for (int i = 0; i < 10; i++) {
                MPI_Irecv(&arr[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &reqs[i]);
            }
        } else if (id < 10) { // children
            // listen to root of winner notification/indication to stop
            MPI_Irecv(&winner, 1, MPI_INT, 10, 1, MPI_COMM_WORLD, &winnerNotification);
        }

        while (winner == -1) {
            //cout << id << " is in loop" << endl;

            if (id < 10 && !stop && ((rand() % 10) + 1) < 3) { 
                // children has 20% chance to stop (finish work)
                MPI_Send(&id, 1, MPI_INT, 10, 0, MPI_COMM_WORLD);
                cout << id << " sending to root" << endl;
                stop = true;
            } else if (id == 10) {
                // root checks number of children completed
                int numDone = 0;
                for (int i = 0; i < 10; i++) {
                    if (arr[i] >= 0) {
                        //cout << "root knows that " << i << " has completed" << endl;
                        numDone++;
                    }
                }
                cout << "numDone = " << numDone << endl;

                // if all done, send notification to players to stop
                if (numDone == 10) {
                    winner = 1;
                    for (int i = 0; i < 10; i++) {
                        MPI_Send(&winner, 1, MPI_INT, i, 1, MPI_COMM_WORLD);
                    }
                    cout << "root sent notification of winner" << endl;
                }
            }
        }
    }
}

MPI_Finalize();

调试couts 的输出看起来像:问题似乎是 root 没有收到所有孩子完成的通知?

2 sending to root
3 sending to root
0 sending to root
4 sending to root
1 sending to root
8 sending to root
9 sending to root
numDone = 1
numDone = 1
... // many numDone = 1, but why 1 only?
7 sending to root
...

我想也许我无法接收到数组中:但我试过了

if (id == 1) {
    int x = 60;
    MPI_Send(&x, 1, MPI_INT, 0, 0, MPI_COMM_WORLD);
} else if (id == 0) {
    MPI_Recv(&arr[1], 1, MPI_INT, 1, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    cout << id << " recieved " << arr[1] << endl;
}

哪个有效。

更新

如果我在 while 循环结束之前添加 MPI_Barrier(MPI_COMM_WORLD),这似乎可以解决,但为什么呢?即使进程不同步,最终,孩子们会向 root 发送他们已经完成的消息,并且 root 应该“倾听”并相应地进行处理?似乎正在发生的事情是 root 一直在运行,占用了所有资源供孩子们执行?或者这里发生了什么?

更新 2:一些孩子没有收到来自 root 的通知

好的,现在root没有收到@MichaelSh的回答完成的孩子的通知的问题,我专注于没有收到父母的孩子。这是重现该问题的代码:

int numprocs, id, arr[10], winner = -1;
bool stop = false;
MPI_Request reqs[10], winnerNotification;

MPI_Init(NULL, NULL);
MPI_Comm_size(MPI_COMM_WORLD, &numprocs);
MPI_Comm_rank(MPI_COMM_WORLD, &id);

srand(time(NULL) + id);

if (id < 10) {
    MPI_Irecv(&winner, 1, MPI_INT, 10, 0, MPI_COMM_WORLD, &winnerNotification);
}
MPI_Barrier(MPI_COMM_WORLD);

while (winner == -1) {
    cout << id << " is in loop ..." << endl;
    if (id == 10) {
        if (((rand() % 10) + 1) < 2) {
            winner = 2;
            for (int i = 0; i < 10; i++) {
                MPI_Send(&winner, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
            }
            cout << "winner notifications sent" << endl;
        }
    }
}

cout << id << " b4 MPI_Finalize. winner is " << winner << endl;

MPI_Finalize();

输出如下:

# 1 run
winner notifications sent
10 b4 MPI_Finalize. winner is 2
9 b4 MPI_Finalize. winner is 2
0 b4 MPI_Finalize. winner is 2

# another run
winner notifications sent
10 b4 MPI_Finalize. winner is 2
8 b4 MPI_Finalize. winner is 2

注意到某些进程似乎没有收到父进程的通知?为什么,子进程的MPI_Wait 只会挂起它们?那么我该如何解决呢?

还有

所有MPI_Barrier 在您的情况下都 - 它等待子响应完成。请检查我的答案以获得更好的解决方案

如果我不这样做,我想每个孩子的反应只需要几毫秒?所以即使我不等待/障碍,我希望接收仍然会在发送后不久发生?除非进程最终占用资源并且其他进程无法运行?

【问题讨论】:

  • 你能追踪到MPI_Irecv(&amp;arr[i],...)被调用了多少次吗?
  • @MichaelSh,从代码来看,每一轮应该是10次(每个孩子一次)?我在 irecv 之后添加了一个 cout,确实如此,它被调用了 10 次
  • MPI_Wait 呢,在访问arr 之前不必调用它。在您的示例中,您使用阻塞发送/接收,但在您的应用中,您使用异步接收...
  • 嗯...我认为问题是我真的不想“等待”,如果有不完整的进程,我需要循环继续(如果我等待,循环将不会继续?)。我的印象是根检查每个循环上的 arr 应该有效吗?这意味着在某个时间点,arr 应该填充来自已完成 Irecv? 的结果
  • 所有MPI_Barrier 在你的情况下 - 它等待子响应完成。请检查我的答案以获得更好的解决方案。

标签: c++ openmpi


【解决方案1】:

请尝试这段代码(为简单起见省略了错误检查):

...
// root checks number of children completed
int numDone = 0;
MPI_Status statuses[10];
MPI_Waitall(10, reqs, statuses);
for (int i = 0; i < 10; i++) {
...

编辑更好的解决方案:
每个孩子都会发起根获胜者通知接收并将其通知发送给根。
Root 向数组发起获胜者通知接收并进入等待接收所有通知,然后将获胜者的 id 发送给孩子。 在for (int round = 0; round &lt; 1; round++)之后插入这段代码

            if (id == 10) 
            { // root
                // keeps track of who has "completed"
                memset(arr, -1, sizeof(arr));
                for (int i = 0; i < 10; i++) 
                {
                    MPI_Irecv(&arr[i], 1, MPI_INT, i, 0, MPI_COMM_WORLD, &reqs[i]);
                }
            } 
            else if (id < 10) 
            { // children
                // listen to root of winner notification/indication to stop
                MPI_Irecv(&winner, 1, MPI_INT, 10, 1, MPI_COMM_WORLD, &winnerNotification);
            }

            if (id < 10)
            {
                while(((rand() % 10) + 1) < 3) ;

                // children has 20% chance to stop (finish work)
                MPI_Send(&id, 1, MPI_INT, 10, 0, MPI_COMM_WORLD);
                std::cout << id << " sending to root" << std::endl;
                // receive winner notification
                MPI_Status status;
                MPI_Wait(&winnerNotification, &status);
                // Process winner notification
            } 
            else if (id == 10) 
            {
                MPI_Status statuses[10];
                MPI_Waitall(10, reqs, statuses);                    

                // if all done, send notification to players to stop
                {
                    winner = 1;
                    for (int i = 0; i < 10; i++) 
                    {
                        MPI_Send(&winner, 1, MPI_INT, i, 1, MPI_COMM_WORLD);
                    }
                    std::cout << "root sent notification of winner" << std::endl;
                }
            }                            

【讨论】:

  • 嗯是有道理的,有点......可能有点令人困惑,......但现在孩子们似乎没有收到获胜者“通知”,因此继续处于无限循环中,如果我尝试等待循环将根本不会继续(如预期的那样)......
  • 我们应该继续在聊天中讨论吗? chat.stackoverflow.com/rooms/19666/…?
  • "(为简单起见省略了错误检查)"。实际上,检查简单 MPI 程序中的错误是没有意义的,因为 MPI 标准要求默认错误处理程序中止程序执行。因此,MPI 调用要么返回MPI_SUCCESS,要么根本不返回(因为程序终止)。必须将每个通信器的错误处理程序显式更改为MPI_ERRORS_RETURN,以便从失败的调用中接收 MPI 状态代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-03
  • 2012-11-08
  • 2021-09-27
  • 1970-01-01
  • 2019-02-07
  • 2015-08-08
  • 1970-01-01
相关资源
最近更新 更多