如何使用C语言在MPI中调用相同的排名?

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

#include "mpi.h" #include  #include  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时,在该点之后没有也不会有任何从站等待接收消息,因为从站已完成执行其他块。 当主节点等待发送最终消息时,程序被阻止。

为了解决这个问题,您必须确保等级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:只是说主/从方法不受青睐,并且可能会使程序员的思维持久,导致生产中的代码不佳


虽然克拉丽莎是完全正确的,她的回答非常明确,但我想补充一些一般性评论,不是关于代码本身,而是关于并行计算理念和良好习惯。

首先是一个快速的序言:当一个人想要并行化一个代码时,可能有两个主要原因:通过克服单个机器上的限制(如内存限制)使其更快和/或允许它处理更大的问题。 但在所有情况下,性能都很重要,我将始终认为MPI(或一般而言并行)程序员对性能感兴趣并关注。 所以我的post的其余部分将假设你是。

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

但是,您必须记住,您编写的这些程序只是:API学习练习。 它们不是真实的东西,它们并不反映实际并行程序是或应该是什么的哲学。 我在这里得到的答案就是我在这里看到的,并且在其他问题和答案中反复提出“主”过程和“奴隶”过程的状态。 这是错误的,从根本上说是错误的! 让我解释一下原因:

正如克拉丽莎完美地指出的那样,“在MPI中,每个进程都执行相同的代码”。 我们的想法是找到一种方法,使多个流程相互作用,共同解决(可能更大的)问题(希望更快)。 但在这些过程中,没有人获得任何特殊地位,他们都是平等的。 他们被赋予了能够解决它们的id,但是等级0并不比等级1或等级1025更好……通过人为地确定过程#0是“主人”而其他人是它的“奴隶”,你打破了这种对称性有其后果:

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

那么,以下是错误的:

  1. 在主服务器获取数据期间,从服务器处于空闲状态。 这是连续且无效的处理……
  2. 然后,数据的分布和要做的工作意味着大量的转移。 这需要时间,因为它仅在进程#0和所有其他进程之间,这可能会在一个链接中在网络上产生大量拥塞。
  3. 当工人做他们的工作时,主人应该做什么? 工作得好吗? 如果是,则可能无法随时处理来自奴隶的请求,从而延迟整个并行处理。 等待这些要求? 然后,它通过闲置来浪费大量的计算能力……最终,没有好的答案。
  4. 然后以相反的顺序重复点1和2,收集结果并输出或结果。 这是大量的数据传输和顺序处理,这将严重损害全局可扩展性,有效性和性能。

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

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

对于这个冗长而有些偏离主题的答案感到抱歉,祝你顺利编程好运。