我有一个非常简单的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结果?
答案 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-program,MPI_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
。