MPI_Reduce 没有将结果传输到根进程

MPI_Reduce not transferring results to root process

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

~ 首先让每个进程创建一个随机数(范围1-100) 然后 运行 程序与 mpirun -np 5 <program_name_here>

这是我的程序:

#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 结果?

我通过实验弄清楚了你的程序有什么问题,基于此,我有一个假设 为什么 它是错误的.

你的程序的这个修改版本做了你期望它做的事情:

#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;
}

我所做的许多更改只是装饰性的。不同之处在于,all 进程必须执行 MPI_Reduce 调用的 all 才能使所有结果计算。

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

如果我的假设是正确的,那么如果您想在不同的过程中执行每个 计算 ,则需要以完全不同的方式构建程序。抽象地说,您需要一个全对全广播,以便 all 进程具有 all 数字,然后在本地计算总和、乘积等。 ,然后多对一将值发送回根。如果我没看错 http://mpitutorial.com/tutorials/mpi-scatter-gather-and-allgather/#mpi_allgather-and-modification-of-average-programMPI_Allgather 是执行全对全广播的函数的名称。

zwol的回答基本上是正确的,但我想打消他的假设:

MPI_Reduce是一个集体操作,它必须被通讯器参数的所有成员调用。在 MPI_COMM_WORLD 的情况下,这意味着应用程序中的所有初始排名。

The MPI standard (5.9.1) 在这里也有帮助:

The routine is called by all group members using the same arguments for count, datatype, op, root and comm. Thus, all processes provide input buffers of the same length [...]

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

所以如果你想要排名 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