C语言如何在MPI中调用same rank?

How to call same rank in MPI using C language?

我正在尝试学习 MPI 编程并编写了以下程序。它添加一整行数组并输出总和。在 rank 0(或 process 0),它会调用它的所有 slave ranks 来进行计算。我只想使用另外两个奴隶 ranks/process 来做到这一点。每当我尝试调用相同等级两次(如下面的代码所示)时,我的代码就会挂在中间而不会执行。如果我不调用相同的等级两次,代码将正常工作

#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
        MPI_Init(&argc, &argv);
        int world_rank;
        MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
        int world_size;
        MPI_Comm_size(MPI_COMM_WORLD, &world_size);
        int tag2 = 1;       
        int arr[30] = {0};

        MPI_Request request;
        MPI_Status status;

        printf ("\n--Current Rank: %d\n", world_rank);
        int index;
        int source = 0;
        int dest;
        if (world_rank == 0)
        {
            int i;

            printf("* Rank 0 excecuting\n");
            index = 0;
            dest = 1;
            for ( i = 0; i < 30; i++ )
            {
                arr[ i ] = i + 1; 
            }           
            MPI_Send(&arr[0], 30, MPI_INT, dest, tag2, MPI_COMM_WORLD); 

            index = 0;
            dest = 2;
            for ( i = 0; i < 30; i++ )
            {
                arr[ i ] = 0; 
            }
            MPI_Send(&arr[0], 30, MPI_INT, dest, tag2, MPI_COMM_WORLD);         

            index = 0;
            dest = 2; //Problem happens here when I try to call the same destination(or rank 2) twice
            //If I change this dest value to 3 and run using: mpirun -np 4 test, this code will work correctly
            for ( i = 0; i < 30; i++ )
            {
                arr[ i ] = 1; 
            }
            MPI_Send(&arr[0], 30, MPI_INT, dest, tag2, MPI_COMM_WORLD);
        }
        else 
        {
            int sum = 0;
            int i;
            MPI_Irecv(&arr[0], 30, MPI_INT, source, tag2, MPI_COMM_WORLD, &request);
            MPI_Wait (&request, &status);
            for(i = 0; i<30; i++)
            {   
                sum = arr[i]+sum;
            }
            printf("\nSum is: %d at rank: %d\n", sum, world_rank);
        }       

        MPI_Finalize();
}

使用结果:mpirun -np 3 test

--Current Rank: 2

Sum is: 0 at rank: 2

--Current Rank: 0
* Rank 0 excecuting

--Current Rank: 1

Sum is: 524800 at rank: 1
                  //Program hangs here and wouldn't show sum of 30 

请告诉我如何调用相同的等级两次。例如,如果我只有两个可以调用的其他从属进程。 如果可能请举例说明

在 MPI 中,每个进程执行相同的代码,正如您所做的那样,您主要通过检查 if/else 语句中的排名来区分不同的进程。级别为 0 的主进程正在执行 3 次发送:一次发送到进程 1,然后两次发送到进程 2。从属进程每个只执行一次接收,这意味着级别 1 接收其第一条消息,级别 2 接收其第一条消息。当您在进程 0 上调用第三个 MPI_Send 时,在那之后没有也不会有任何从站等待接收消息,因为从站已经完成了他们的 else 块。主程序等待发送最终消息时程序被阻塞。

为了解决这个问题,您必须确保等级 2 的从站执行两次接收,方法是仅为该进程添加一个循环或仅为该进程重复(因此,使用 if(world_rank == 2) 检查)代码块

        sum = 0; //resetting sum
        MPI_Irecv(&arr[0], 1024, MPI_INT, source, tag2, MPI_COMM_WORLD, &request);
        MPI_Wait (&request, &status);
        for(i = 0; i<1024; i++)
        {   
            sum = arr[i]+sum;
        }
        printf("\nSum is: %d at rank: %d\n", sum, world_rank); 

TL;DR: 只是说 master/slave 方法不受欢迎,它可能会持久地格式化程序员的思维,导致在投入生产时产生糟糕的代码


虽然 Clarissa 完全正确并且她的回答非常清楚,但我想补充一些一般性的评论,不是关于代码本身,而是关于并行计算哲学和良好习惯。

首先是一个简短的序言:当一个人想要并行化一个代码时,可能有两个主要原因:使其更快 and/or 允许它通过克服发现的限制(如内存限制)来处理更大的问题在一台机器上。但在所有情况下,性能都很重要,我将始终假设 MPI(或通常所说的并行)程序员对性能感兴趣并关心。所以我剩下的post会假设你是。

现在这个 post 的主要原因:在过去的几天里,我在 SO 上看到了一些关于 MPI 和并行化的问题,显然来自渴望学习 MPI(或 OpenMP for那件事)。这很棒!并行编程很棒,而且永远不会有足够的并行程序员。所以我很高兴(我相信很多 SO 成员也是)回答帮助人们学习如何并行编程的问题。在学习如何并行编程的情况下,您必须编写一些简单的代码,做一些简单的事情,才能理解 API 的作用及其工作原理。从远处看,这些程序可能看起来很愚蠢而且非常无效,但没关系,这就是学习的原理。大家都是这样学的。

但是,您必须记住,您编写的这些程序只是:API 学习练习。它们不是真实的东西,它们不反映实际并行程序是什么或应该是什么的哲学。促使我在这里回答的原因是,我在这里以及其他问题和答案中反复提出了 "master" 流程和 "slaves" 流程的状态。那是错误的,根本上是错误的!让我解释一下原因:

正如 Clarissa 完美指出的那样,"in MPI, each process executes the same code"。这个想法是找到一种方法,使多个进程相互作用,共同解决(可能更大的)问题(希望更快)。但是在这些进程中,none 有什么特殊的地位,他们都是平等的。他们被赋予了一个 id 以便能够解决它们,但是等级 0 并不比等级 1 或等级 1025 好......通过人为地决定进程 #0 是 "master" 而其他是它的 "slaves",你打破了这种对称性,它会产生后果:

既然排名#0 是主人,它发号施令,对吧?这就是大师所做的。因此,它将获得 运行 代码所需的信息,将其共享给工作人员,将指示他们进行处理。然后它将等待处理结束(可能让自己在中间忙碌,但更可能只是等待或戳工人,因为这是主人所做的),收集结果,做一些重组并输出它。任务完成!这有什么问题吗?

嗯,以下是错误的:

  1. 在master获取数据期间,slave处于空闲状态。这是顺序且无效的处理...
  2. 然后数据的分布和要做的工作意味着大量的传输。这需要时间,因为它仅在进程 #0 和所有其他进程之间,这可能会在单个 link.
  3. 中造成大量网络拥塞。
  4. 工人干活的时候,主人应该干什么?也工作?如果是,那么它可能无法随时处理来自从属的请求,从而延迟整个并行处理。等待这些请求?然后就是闲置着浪费了很多算力。。。最终,没有好的答案。
  5. 然后以相反的顺序重复第1点和第2点,收集结果并输出结果。这是大量的数据传输和顺序处理,这将严重损害全局可扩展性、有效性和性能。

所以我希望您现在明白为什么 master/slave 的方法(通常,不总是,但经常)是错误的。从过去几天我读到的问题和答案中我看到的危险是,您可能会在这种方法中格式化思维,就好像它是并行思考的 "normal" 方式一样。好吧,它不是!并行编程是对称的。它在全球范围内同时在所有地方处理这个问题。您必须从一开始就并行思考,并将您的代码视为一个全局并行实体,而不仅仅是需要指示该做什么的 b运行ch 进程。每个进程都是它自己的主人,在平等的基础上与它的同行打交道。每个进程应该(尽可能)自己获取数据(使其成为并行处理);根据处理中涉及的对等点数量及其 ID 决定要做什么;必要时与同行交换信息,可以是本地(点对点通信)或全球(集体通信);并发布自己的结果份额(再次导致并行处理)...

好吧,这对于刚开始学习并行编程的人来说有点极端,我不想告诉你你的学习练习应该是这样的。但是请牢记目标,不要忘记 API 学习示例只是 API 学习示例,而不是实际代码的简化模型。因此,请继续尝试 MPI 调用以了解它们的作用以及它们的工作方式,但尝试在您的示例中慢慢趋向于对称方法。从长远来看,这只会对您有益。

对于这个冗长且有点偏离主题的回答,我们深表歉意,祝您并行编程顺利。