MPI_Reduce不将结果传输到根进程

时间:2016-04-04 00:19:31

标签: c mpi

我有一个非常简单的MPI程序来测试MPI_Reduce的行为。我的目标很简单:

〜首先让每个进程创建一个随机数(范围1-100) 然后使用mpirun -np 5 <program_name_here>

运行程序
  • 进程0,找到所有5个数字的总和
  • 拥有流程1,找到所有5个数字的产品
  • 进程2,找到所有5个数字的最大值
  • 进程3,找到所有5个数字的分钟
  • 有进程4,找到按位和所有5个数字

这是我的计划:

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

int sum = 0;
int product = 0;
int max = 0;
int min = 0;
int bitwiseAnd = 0;

int main ( int argc, char **argv )
{
   int my_id, num_procs;
   MPI_Init(&argc, &argv);

   MPI_Comm_rank(MPI_COMM_WORLD, &my_id);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

   int num;
   srand(time(NULL) * my_id);
   num = rand() % 100; //give num the random number   

   printf("Process #%i: Here is num: %i\n",my_id,num);


   if(my_id == 0){
      printf("Okay it entered 0\n");
      MPI_Reduce(&num, &sum,1,MPI_INT,MPI_SUM, 0, MPI_COMM_WORLD);
   }else if(my_id == 1){
      printf("Okay it entered 1\n");
      MPI_Reduce(&num, &product,1,MPI_INT,MPI_PROD, 0, MPI_COMM_WORLD);
   }else if(my_id == 2){
      printf("Okay it entered 2\n");
      MPI_Reduce(&num, &max,1,MPI_INT,MPI_MAX, 0, MPI_COMM_WORLD);
   }else if(my_id == 3){
      printf("Okay it entered 3\n");
      MPI_Reduce(&num, &min,1,MPI_INT,MPI_MIN, 0, MPI_COMM_WORLD);
   }else if(my_id == 4){
      printf("Okay it entered 4\n");
      MPI_Reduce(&num, &bitwiseAnd,1,MPI_INT,MPI_BAND, 0, MPI_COMM_WORLD);
   }

   MPI_Barrier(MPI_COMM_WORLD);

   if(my_id == 0){
      printf("I am process %i and the sum is %i\n",my_id,sum);
      printf("I am process %i and the product is %i\n",my_id,product);
      printf("I am process %i and the max is %i\n",my_id,max);
      printf("I am process %i and the min is %i\n",my_id,min);
      printf("I am process %i and the bitwiseAdd is %i\n",my_id,bitwiseAnd);
   }

   MPI_Finalize();
}

这会产生如下输出:

[blah@blah example]$ mpirun -np 5 all
Process #2: Here is num: 21
Okay it entered 2
Process #4: Here is num: 52
Okay it entered 4
Process #0: Here is num: 83
Okay it entered 0
Process #1: Here is num: 60
Okay it entered 1
Process #3: Here is num: 66
Okay it entered 3
I am process 0 and the sum is 282
I am process 0 and the product is 0
I am process 0 and the max is 0
I am process 0 and the min is 0
I am process 0 and the bitwiseAdd is 0
[blah@blah example]$

为什么不处理0从其他进程中获取MPI_Reduce结果?

2 个答案:

答案 0 :(得分:2)

我通过实验找出了你的程序有什么问题,基于此,我有一个假设关于为什么它是错误的。

您程序的这个修改版本符合您的预期:

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

int main (int argc, char **argv)
{
   int my_id;
   int num_procs;
   int num;
   int sum = 0;
   int product = 0;
   int max = 0;
   int min = 0;
   int bitwiseAnd = 0;
   int seed = time(0);

   MPI_Init(&argc, &argv);
   MPI_Comm_rank(MPI_COMM_WORLD, &my_id);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);

   srand(seed * my_id);
   num = rand() % 100;

   printf("Process #%i: Here is num: %i\n",my_id,num);

   MPI_Reduce(&num, &sum,        1, MPI_INT, MPI_SUM,  0, MPI_COMM_WORLD);
   MPI_Reduce(&num, &product,    1, MPI_INT, MPI_PROD, 0, MPI_COMM_WORLD);
   MPI_Reduce(&num, &max,        1, MPI_INT, MPI_MAX,  0, MPI_COMM_WORLD);
   MPI_Reduce(&num, &min,        1, MPI_INT, MPI_MIN,  0, MPI_COMM_WORLD);
   MPI_Reduce(&num, &bitwiseAnd, 1, MPI_INT, MPI_BAND, 0, MPI_COMM_WORLD);

   MPI_Barrier(MPI_COMM_WORLD);

   if (my_id == 0) {
      printf("The sum is %i\n", sum);
      printf("The product is %i\n", product);
      printf("The max is %i\n", max);
      printf("The min is %i\n", min);
      printf("The bitwiseAnd is %i\n", bitwiseAnd);
   }

   MPI_Finalize();
   return 0;
}

我所做的许多改变都只是化妆品。造成差异的变化是,所有进程必须执行MPI_Reduce调用的所有,以便计算所有结果。

现在,为什么重要?我必须强调这是一个假设。我不知道。但是,符合可用事实的解释是:在我和你的MPI实现中,MPI_Reduce调用中的实际计算在根进程上只发生 ,但所有其他进程也必须调用MPI_Reduce以发送带有其值的消息。该消息不依赖于操作参数。所以MPI_SUM调用完成了它应该做的意外,因为其他调用MPI_Reduce提供了它所需的值。但其他调用都没有进行任何计算。

如果我的假设是正确的,那么如果你想在不同的进程中执行每个计算,那么你需要对你的程序进行相当不同的构建。抽象地说,您需要一个全部广播,以便所有进程所有数字,然后本地计算总和,产品等,然后全部到 - 一个将值发送回根。如果我正确地阅读http://mpitutorial.com/tutorials/mpi-scatter-gather-and-allgather/#mpi_allgather-and-modification-of-average-programMPI_Allgather是执行所有广播的功能的名称。

答案 1 :(得分:2)

zwol的答案基本上是正确的,但我想向他的假设保证:

MPI_Reduce是一个集体操作,它必须由传播者参数的所有成员调用。如果是MPI_COMM_WORLD,则表示应用程序中的所有初始排名。

The MPI standard (5.9.1)在这里也很有用:

  

所有组成员使用相同的参数调用例程   用于count,datatype,op,root和comm。因此,所有过程都提供   输入缓冲区长度相同[...]

重要的是要理解,根不是进行所有计算的根。该操作以分布式方式完成,通常使用树算法。这意味着只需要执行对数量的时间步骤,并且比仅将所有数据收集到根并在那里执行操作更有效,特别是对于大量的等级。

因此,如果您希望结果为0级,那么您确实必须无条件地运行代码:

MPI_Reduce(&num, &sum,        1, MPI_INT, MPI_SUM,  0, MPI_COMM_WORLD);
MPI_Reduce(&num, &product,    1, MPI_INT, MPI_PROD, 0, MPI_COMM_WORLD);
MPI_Reduce(&num, &max,        1, MPI_INT, MPI_MAX,  0, MPI_COMM_WORLD);
MPI_Reduce(&num, &min,        1, MPI_INT, MPI_MIN,  0, MPI_COMM_WORLD);
MPI_Reduce(&num, &bitwiseAnd, 1, MPI_INT, MPI_BAND, 0, MPI_COMM_WORLD);

如果您需要不同等级的结果,则可以相应地更改root参数。如果您希望结果在所有排名中都可用,请改用MPI_Allreduce