17.2.293. MPI_Reduce_init
MPI_Reduce, MPI_Ireduce, MPI_Reduce_init - 对组内所有进程的值进行归约操作。
17.2.293.1. 语法
17.2.293.1.1. C语法
#include <mpi.h>
int MPI_Reduce(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root,
MPI_Comm comm)
int MPI_Ireduce(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root,
MPI_Comm comm, MPI_Request *request)
int MPI_Reduce_init(const void *sendbuf, void *recvbuf, int count,
MPI_Datatype datatype, MPI_Op op, int root,
MPI_Comm comm, MPI_Info info, MPI_Request *request)
17.2.293.1.2. Fortran语法
USE MPI
! or the older form: INCLUDE 'mpif.h'
MPI_REDUCE(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, ROOT, COMM,
IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER COUNT, DATATYPE, OP, ROOT, COMM, IERROR
MPI_IREDUCE(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, ROOT, COMM,
REQUEST, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER COUNT, DATATYPE, OP, ROOT, COMM, REQUEST, IERROR
MPI_REDUCE_INIT(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, ROOT, COMM,
INFO, REQUEST, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER COUNT, DATATYPE, OP, ROOT, COMM, INFO, REQUEST, IERROR
17.2.293.1.3. Fortran 2008 语法
USE mpi_f08
MPI_Reduce(sendbuf, recvbuf, count, datatype, op, root, comm, ierror)
TYPE(*), DIMENSION(..), INTENT(IN) :: sendbuf
TYPE(*), DIMENSION(..) :: recvbuf
INTEGER, INTENT(IN) :: count, root
TYPE(MPI_Datatype), INTENT(IN) :: datatype
TYPE(MPI_Op), INTENT(IN) :: op
TYPE(MPI_Comm), INTENT(IN) :: comm
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
MPI_Ireduce(sendbuf, recvbuf, count, datatype, op, root, comm, request,
ierror)
TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
INTEGER, INTENT(IN) :: count, root
TYPE(MPI_Datatype), INTENT(IN) :: datatype
TYPE(MPI_Op), INTENT(IN) :: op
TYPE(MPI_Comm), INTENT(IN) :: comm
TYPE(MPI_Request), INTENT(OUT) :: request
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
MPI_Reduce_init(sendbuf, recvbuf, count, datatype, op, root, comm, info, request,
ierror)
TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
INTEGER, INTENT(IN) :: count, root
TYPE(MPI_Datatype), INTENT(IN) :: datatype
TYPE(MPI_Op), INTENT(IN) :: op
TYPE(MPI_Comm), INTENT(IN) :: comm
TYPE(MPI_Info), INTENT(IN) :: info
TYPE(MPI_Request), INTENT(OUT) :: request
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
17.2.293.2. 输入参数
sendbuf: 发送缓冲区的地址(选项)。count: 发送缓冲区中的元素数量(整数)。datatype: 发送缓冲区元素的数据类型(句柄)。op: 归约操作(句柄)。root: 根进程的排名(整数)。comm: 通信器(句柄)。info: 信息(句柄,持久化)。
17.2.293.3. 输出参数
recvbuf: 接收缓冲区的地址(仅在根节点有效,可选参数)。request: 请求(句柄,仅限非阻塞模式)。ierror: 仅限Fortran:错误状态(整数)。
17.2.293.4. 描述
全局归约函数(MPI_Reduce、MPI_Op_create、MPI_Op_free、 MPI_Allreduce、MPI_Reduce_scatter、MPI_Scan)在组内所有成员之间执行全局归约操作(如求和、最大值、逻辑与等)。归约操作可以是预定义操作列表中的一种,也可以是用户自定义操作。全局归约函数具有多种形式:将归约结果返回给单个节点的reduce操作、将结果返回给所有节点的all-reduce操作,以及扫描(并行前缀)操作。此外,reduce-scatter操作结合了归约和散射操作的功能。
MPI_Reduce 通过操作op将组内每个进程输入缓冲区中的元素进行组合,并将组合结果返回到根进程的输出缓冲区中。输入缓冲区由参数sendbuf、count和datatype定义;输出缓冲区由参数recvbuf、count和datatype定义;两者具有相同数量的元素和相同的数据类型。所有组成员调用此例程时需使用相同的count、datatype、op、root和comm参数。因此,所有进程提供的输入缓冲区和输出缓冲区长度相同,元素类型一致。每个进程可以提供一个元素或元素序列,在这种情况下,组合操作会对序列中的每个元素逐个执行。例如,如果操作为MPI_MAX且发送缓冲区包含两个浮点数元素(count=2且datatype=MPI_FLOAT),那么recvbuf(1) = 全局最大值(sendbuf(1))且recvbuf(2) = 全局最大值(sendbuf(2))。
17.2.293.5. 原地选项的使用
当通信器为内部通信器时,可以执行原地归约操作(输出缓冲区同时用作输入缓冲区)。将变量MPI_IN_PLACE作为根进程sendbuf的值。在这种情况下,根进程会从接收缓冲区获取输入数据,该数据随后将被输出数据替换。
请注意,MPI_IN_PLACE是一种特殊类型的值;其使用限制与MPI_BOTTOM相同。
由于原地(in-place)选项将接收缓冲区转换为发送-接收缓冲区,包含INTENT的Fortran绑定必须将其标记为INOUT,而非OUT。
17.2.293.6. 当通信器为跨通信器时
当通信器是跨通信器时,第一组中的根进程会收集第二组中所有进程的数据,然后执行op操作。第一组定义了根进程。该进程使用MPI_ROOT作为其root参数的值。其余进程使用MPI_PROC_NULL作为其root参数的值。第二组中的所有进程都使用第一组中该根进程的秩作为其root参数的值。在第二组中只有发送缓冲区参数是有效的,而在第一组的根进程中只有接收缓冲区参数是有效的。
17.2.293.7. 预定义的归约操作
MPI提供的预定义操作集如下所示(预定义归约操作)。该部分还列举了每个操作可应用的数据类型。此外,用户可以定义自己的操作,这些操作可以被重载以处理多种数据类型,包括基本类型和派生类型。关于用户自定义操作的详细说明,请参阅相关手册页(参见MPI_Op_create和MPI_Op_free)。
操作op始终被假定为具有结合性。所有预定义的操作也被假定为具有交换性。用户可以定义被假定为具有结合性但不具有交换性的操作。归约操作的"规范"执行顺序由组中进程的等级决定。然而,实现可以利用结合性,或结合性和交换性来改变执行顺序。对于非严格结合和交换的操作(如浮点加法),这可能会改变归约的结果。
预定义运算符仅适用于下面列出的MPI类型(预定义归约操作,以及下方的MINLOC和MAXLOC部分)。用户自定义运算符可以操作通用的派生数据类型。在这种情况下,归约操作应用的每个参数都是由此类数据类型描述的一个元素,可能包含多个基本值。MPI标准第4.9.4节"用户自定义操作"对此有进一步说明。
以下是为MPI_Reduce及相关函数MPI_Allreduce、MPI_Reduce_scatter和MPI_Scan提供的预定义操作。通过在op参数中指定以下操作来调用它们:
Name Meaning
--------- --------------------
MPI_MAX maximum
MPI_MIN minimum
MPI_SUM sum
MPI_PROD product
MPI_LAND logical and
MPI_BAND bit-wise and
MPI_LOR logical or
MPI_BOR bit-wise or
MPI_LXOR logical xor
MPI_BXOR bit-wise xor
MPI_MAXLOC max value and location
MPI_MINLOC min value and location
MPI_MINLOC和MPI_MAXLOC这两个操作将在下文中单独讨论(MINLOC和MAXLOC)。对于其他预定义操作,我们下面列举了允许的op和datatype参数组合。首先,按照以下方式定义MPI基本数据类型组:
C integer: MPI_INT, MPI_LONG, MPI_SHORT,
MPI_UNSIGNED_SHORT, MPI_UNSIGNED,
MPI_UNSIGNED_LONG
Fortran integer: MPI_INTEGER
Floating-point: MPI_FLOAT, MPI_DOUBLE, MPI_REAL,
MPI_DOUBLE_PRECISION, MPI_LONG_DOUBLE
Logical: MPI_LOGICAL
Complex: MPI_COMPLEX
Byte: MPI_BYTE
现在,每个选项的有效数据类型如下所示。
Op Allowed Types
---------------- ---------------------------
MPI_MAX, MPI_MIN C integer, Fortran integer,
floating-point
MPI_SUM, MPI_PROD C integer, Fortran integer,
floating-point, complex
MPI_LAND, MPI_LOR, C integer, logical
MPI_LXOR
MPI_BAND, MPI_BOR, C integer, Fortran integer, byte
MPI_BXOR
示例1: 一个计算两个向量点积的例程,这两个向量分布在一组进程中,并在进程零处返回结果。
SUBROUTINE PAR_BLAS1(m, a, b, c, comm)
REAL a(m), b(m) ! local slice of array
REAL c ! result (at process zero)
REAL sum
INTEGER m, comm, i, ierr
! local sum
sum = 0.0
DO i = 1, m
sum = sum + a(i)*b(i)
END DO
! global sum
CALL MPI_REDUCE(sum, c, 1, MPI_REAL, MPI_SUM, 0, comm, ierr)
RETURN
示例2: 一个计算向量与数组乘积的例程,该向量和数组分布在一组进程中,并在进程零处返回结果。
SUBROUTINE PAR_BLAS2(m, n, a, b, c, comm)
REAL a(m), b(m,n) ! local slice of array
REAL c(n) ! result
REAL sum(n)
INTEGER n, comm, i, j, ierr
! local sum
DO j= 1, n
sum(j) = 0.0
DO i = 1, m
sum(j) = sum(j) + a(i)*b(i,j)
END DO
END DO
! global sum
CALL MPI_REDUCE(sum, c, n, MPI_REAL, MPI_SUM, 0, comm, ierr)
! return result at process zero (and garbage at the other nodes)
RETURN
17.2.293.8. MINLOC 与 MAXLOC
操作符MPI_MINLOC用于计算全局最小值及其对应的索引。类似地,MPI_MAXLOC用于计算全局最大值及其索引。这些操作的一个应用场景是计算全局最小(最大)值以及包含该值的进程的排名。
定义MPI_MAXLOC的操作是
( u ) ( v ) ( w )
( ) o ( ) = ( )
( i ) ( j ) ( k )
where
w = max(u, v)
and
( i if u > v
(
k = ( min(i, j) if u = v
(
( j if u < v)
MPI_MINLOC的定义方式类似:
( u ) ( v ) ( w )
( ) o ( ) = ( )
( i ) ( j ) ( k )
where
w = min(u, v)
and
( i if u < v
(
k = ( min(i, j) if u = v
(
( j if u > v)
这两种操作都具有结合性和交换性。需要注意的是,如果对一系列(u(0), 0), (u(1), 1), ..., (u(n-1), n-1)这样的数值对应用MPI_MAXLOC进行归约操作,返回的结果将是(u, r),其中u= max(i) u(i),而r是该序列中第一个全局最大值的索引。因此,如果每个进程提供一个值及其在组内的排名,那么使用op = MPI_MAXLOC的归约操作将返回最大值以及第一个具有该值的进程的排名。类似地,MPI_MINLOC可用于返回最小值及其索引。更一般地说,MPI_MINLOC计算的是字典序最小值,其中元素根据每对数值的第一个分量排序,当第一个分量相同时则根据第二个分量决定顺序。
归约操作被定义为对由值和索引组成的参数对进行操作。对于Fortran和C语言,都提供了描述这种参数对的类型。这种参数可能具有混合类型的特性在Fortran中会带来问题。针对Fortran,通过让MPI提供的类型由与值相同类型的对组成,并将索引强制转换为相同类型,从而规避了这个问题。而在C语言中,MPI提供的参数对类型具有不同的类型,且索引是一个整型。
为了在归约操作中使用MPI_MINLOC和MPI_MAXLOC,必须提供一个表示值-索引对的数据类型参数。OpenMPI提供了九种这样的预定义数据类型。MPI_MAXLOC和MPI_MINLOC操作可与以下每种数据类型配合使用:
Fortran:
Name Description
MPI_2REAL pair of REALs
MPI_2DOUBLE_PRECISION pair of DOUBLE-PRECISION variables
MPI_2INTEGER pair of INTEGERs
C:
Name Description
MPI_FLOAT_INT float and int
MPI_DOUBLE_INT double and int
MPI_LONG_INT long and int
MPI_2INT pair of ints
MPI_SHORT_INT short and int
MPI_LONG_DOUBLE_INT long double and int
数据类型MPI_2REAL等同于:
MPI_TYPE_CONTIGUOUS(2, MPI_REAL, MPI_2REAL)
类似的声明适用于MPI_2INTEGER、MPI_2DOUBLE_PRECISION和MPI_2INT。
MPI_FLOAT_INT 数据类型相当于通过以下指令序列定义的。
type[0] = MPI_FLOAT
type[1] = MPI_INT
disp[0] = 0
disp[1] = sizeof(float)
block[0] = 1
block[1] = 1
MPI_TYPE_STRUCT(2, block, disp, type, MPI_FLOAT_INT)
类似的声明也适用于MPI_LONG_INT和MPI_DOUBLE_INT。
示例3: 在C语言中,每个进程都有一个包含30个双精度浮点数的数组。针对这30个位置中的每一个,计算包含最大值的进程的值和排名。
...
/* each process has an array of 30 double: ain[30]
*/
double ain[30], aout[30];
int ind[30];
struct {
double val;
int rank;
} in[30], out[30];
int i, myrank, root;
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
for (i=0; i<30; ++i) {
in[i].val = ain[i];
in[i].rank = myrank;
}
MPI_Reduce( in, out, 30, MPI_DOUBLE_INT, MPI_MAXLOC, root, comm );
/* At this point, the answer resides on process root
*/
if (myrank == root) {
/* read ranks out
*/
for (i=0; i<30; ++i) {
aout[i] = out[i].val;
ind[i] = out[i].rank;
}
}
示例4: 相同的示例,使用Fortran语言。
...
! each process has an array of 30 double: ain(30)
DOUBLE PRECISION :: ain(30), aout(30)
INTEGER :: ind(30)
DOUBLE PRECISION :: in(2,30), out(2,30)
INTEGER :: i, myrank, root, ierr
call MPI_COMM_RANK(MPI_COMM_WORLD, myrank)
DO I=1, 30
in(1,i) = ain(i)
in(2,i) = myrank ! myrank is coerced to a double
END DO
call MPI_REDUCE( in, out, 30, MPI_2DOUBLE_PRECISION, MPI_MAXLOC, root, &
comm, ierr )
! At this point, the answer resides on process root
IF (myrank == root) THEN
! read ranks out
DO I= 1, 30
aout(i) = out(1,i)
ind(i) = out(2,i) ! rank is coerced back to an integer
END DO
END IF
示例5:每个进程都有一个非空的值数组。找出全局最小值、持有该值的进程的排名以及该值在该进程中的索引。
#define LEN 1000
float val[LEN]; /* local array of values */
int count; /* local number of values */
int myrank, minrank, minindex;
float minval;
struct {
float value;
int index;
} in, out;
/* local minloc */
in.value = val[0];
in.index = 0;
for (i=1; i < count; i++)
if (in.value > val[i]) {
in.value = val[i];
in.index = i;
}
/* global minloc */
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
in.index = myrank*LEN + in.index;
MPI_Reduce( in, out, 1, MPI_FLOAT_INT, MPI_MINLOC, root, comm );
/* At this point, the answer resides on process root
*/
if (myrank == root) {
/* read answer out
*/
minval = out.value;
minrank = out.index / LEN;
minindex = out.index % LEN;
所有MPI对象(例如MPI_Datatype、MPI_Comm)在Fortran中都是INTEGER类型。
17.2.293.9. 关于集合操作的注意事项
归约函数(MPI_Op)不会返回错误值。因此,如果这些函数检测到错误,它们只能选择调用MPI_Abort或静默跳过问题。因此,如果您将错误处理程序从MPI_ERRORS_ARE_FATAL更改为其他选项,例如MPI_ERRORS_RETURN,则可能不会指示任何错误。
原因在于确保所有集合例程返回相同错误值时存在的性能问题。
17.2.293.10. 错误
几乎所有MPI例程都会返回一个错误值;C语言例程通过函数返回值返回,Fortran例程则通过最后一个参数返回。
在返回错误值之前,会调用与通信对象(如通信器、窗口、文件)关联的当前MPI错误处理程序。如果MPI调用未关联任何通信对象,则该调用被视为附加到MPI_COMM_SELF,并将调用关联的MPI错误处理程序。当MPI_COMM_SELF未初始化时(即在MPI_Init/MPI_Init_thread之前、MPI_Finalize之后,或仅使用会话模型时),错误会触发初始错误处理程序。初始错误处理程序可通过在使用世界模型时调用MPI_Comm_set_errhandler来修改MPI_COMM_SELF,或通过mpiexec的mpi_initial_errhandler命令行参数,或MPI_Comm_spawn/MPI_Comm_spawn_multiple的info键来设置。如果未设置其他适当的错误处理程序,则MPI I/O函数将调用MPI_ERRORS_RETURN错误处理程序,而其他所有MPI函数将调用MPI_ERRORS_ABORT错误处理程序。
Open MPI 包含三个可使用的预定义错误处理器:
MPI_ERRORS_ARE_FATAL导致程序中止所有连接的MPI进程。MPI_ERRORS_ABORT一个可在通信器、窗口、文件或会话上调用的错误处理程序。当在通信器上调用时,其行为类似于在该通信器上调用MPI_Abort。如果在窗口或文件上调用,则行为类似于在包含对应窗口或文件中进程组的通信器上调用MPI_Abort。如果在会话上调用,则仅中止本地进程。MPI_ERRORS_RETURN向应用程序返回一个错误代码。
MPI应用程序也可以通过调用以下方式实现自己的错误处理程序:
请注意,MPI不保证MPI程序在出现错误后能够继续运行。
有关更多信息,请参阅MPI-3.1标准中的错误处理部分。