reduce 介绍
规约 Reduce 是函数式编程中的经典概念。数据规约 涉及通过函数将一组数字简化为较小的一组数字。例如,假设我们有一个数字列表 [1、2、3、4、5]
。用 sum
函数 此数字列表规约 将产生 sum([1、2、3、4、5])=15
。类似地,乘法规约将产生 multiply([1、2、3、4、5]) = 120
。
就像您想象的那样,将归约函数应用于一组分布式数字可能非常麻烦。随之而来的是,难以有效地编程以设定顺序发生的规约。幸运的是,MPI 有一个方便的函数,称为MPI_Reduce
,它将处理程序员在并行应用程序中需要执行的常见的规约操作。
MPI_Reduce
与 MPI_Gather
相似,MPI_Reduce
在每个进程上获取一个输入元素数组,并将输出元素数组返回给根进程。输出元素包含规约的结果。 MPI_Reduce
的原型如下所示:
MPI_Reduce(
void* send_data,
void* recv_data,
int count,
MPI_Datatype datatype,
MPI_Op op,
int root,
MPI_Comm communicator)
send_data
参数是每个进程要规约的数据数组。 recv_data
仅与具有根级别的进程相关。 recv_data
数组包含规约的结果,大小为 sizeof(datatype)* count
。 op
参数是您希望应用于数据的操作。MPI 包含一组可以使用的常见归约运算。尽管可以定义自定义归约操作,但这超出了本课程的范围。 MPI 定义的规约操作包括:
MPI_MAX
- 返回最大元素MPI_MIN
- 返回最小元素MPI_SUM
- 返回元素和MPI_PROD
- 返回元素累乘MPI_LAND
- 跨元素执行逻辑 ANDMPI_LOR
- 跨元素执行逻辑 ORMPI_BAND
- 跨元素按位执行逻辑 ANDMPI_BOR
- 跨元素按位执行逻辑 ORMPI_MAXLOC
- 返回元素最大值及其进程秩MPI_MINLOC
- 返回元素最小值及其进程秩
下图说明了 MPI_Reduce
的通讯方式:
在上面,每个进程包含一个整数。以0 的根进程调用 MPI_Reduce
,并使用 MPI_SUM
作为规约操作。这四个数字相加后得出结果,并存储在根进程中。
查看进程包含多个元素时会发生什么也很有用。下图显示了每个进程规约多个数字的情况。
上图中的过程每个都有两个元素。结果求和基于每个元素进行。换句话说,不是将所有数组中的所有元素求和成一个元素,而是将每个数组中的第 i 个元素求和成过程0 的结果数组中的第 i 个元素。
使用 MPI_Reduce 计算数字平均值
在上一课中展示了如何使用 MPI_Scatter
和 MPI_Gather
计算平均值。使用 MPI_Reduce
可以简化上一课的代码
int main(int argc, char** argv) {
if (argc != 2) {
fprintf(stderr, "Usage: avg num_elements_per_proc\n");
exit(1);
}
int num_elements_per_proc = atoi(argv[1]);
MPI_Init(NULL, NULL