17.2.270. MPI_Op_create
MPI_Op_create — 创建一个用户自定义的组合函数句柄。
17.2.270.1. 语法
17.2.270.1.1. C语法
#include <mpi.h>
int MPI_Op_create(MPI_User_function *function, int commute,
MPI_Op *op)
17.2.270.1.2. Fortran语法
USE MPI
! or the older form: INCLUDE 'mpif.h'
MPI_OP_CREATE(FUNCTION, COMMUTE, OP, IERROR)
EXTERNAL FUNCTION
LOGICAL COMMUTE
INTEGER OP, IERROR
17.2.270.1.3. Fortran 2008 语法
USE mpi_f08
MPI_Op_create(user_fn, commute, op, ierror)
PROCEDURE(MPI_User_function) :: user_fn
LOGICAL, INTENT(IN) :: commute
TYPE(MPI_Op), INTENT(OUT) :: op
INTEGER, OPTIONAL, INTENT(OUT) :: ierror
17.2.270.2. 输入参数
function: 用户自定义函数(function)。commute: 如果可交换则为True;否则为false。
17.2.270.3. 输出参数
op: 操作(句柄)。ierror: 仅限Fortran:错误状态(整数)。
17.2.270.4. 描述
MPI_Op_create 将用户自定义的全局操作绑定到一个操作句柄上,该句柄随后可用于 MPI_Reduce、MPI_Allreduce、MPI_Reduce_scatter 和 MPI_Scan。用户自定义操作被假定为可结合的。如果 commute = true,则该操作应同时满足交换律和结合律。如果 commute = false,则操作数的顺序是固定的,定义为按进程秩号升序排列,从秩号为0的进程开始。可以利用操作的结合性来改变求值顺序。如果 commute = true,则可以利用交换性和结合性来改变求值顺序。
function 是用户自定义的函数,必须包含以下四个参数:invec、inoutvec、len 和 datatype。
该函数的ANSI-C原型如下:
typedef void MPI_User_function(void *invec, void *inoutvec,
int *len,
MPI_Datatype *datatype);
用户自定义函数的Fortran声明如下所示。
FUNCTION USER_FUNCTION( INVEC(*), INOUTVEC(*), LEN, TYPE)
<type> INVEC(LEN), INOUTVEC(LEN)
INTEGER LEN, TYPE
datatype参数是传递给MPI_Reduce调用的数据类型的句柄。用户定义的归约函数应按以下规则编写:设u[0], ..., u[len-1]为函数调用时由参数invec、len和datatype描述的通信缓冲区中的len个元素;设v[0], ..., v[len-1]为函数调用时由参数inoutvec、len和datatype描述的通信缓冲区中的len个元素;设w[0], ..., w[len-1]为函数返回时由参数inoutvec、len和datatype描述的通信缓冲区中的len个元素;那么对于i=0,...,len-1,有w[i] = u[i] o v[i],其中o是该函数计算的归约操作。
通俗地说,我们可以将invec和inoutvec视为包含len个元素的数组,函数正在对这些元素进行组合操作。归约操作的结果会覆盖inoutvec中的值,因此得名。每次调用该函数都会对len个元素执行逐点归约运算:即对于i = 0...count-1,函数会在inoutvec[i]中返回invec[i] o inoutvec[i]的值,其中o表示该函数计算的组合操作。
通过在内部将数据类型参数的值与已知的全局句柄进行比较,可以针对多种不同数据类型重载单个用户定义函数的使用。
通用数据类型可以传递给用户函数。然而,使用非连续的数据类型可能会导致效率低下。
在用户函数内部不得调用任何MPI通信函数。 MPI_Abort可在函数内部出现错误时调用。
17.2.270.5. 注意事项
假设用户定义了一个重载的自定义归约函数库:数据类型参数用于根据操作数的类型,在每次调用时选择正确的执行路径。用户自定义的归约函数无法"解码"传入的数据类型参数,也无法自行识别数据类型句柄与其所表示的数据类型之间的对应关系。这种对应关系是在创建数据类型时建立的。在使用该库之前,必须执行库初始化前导代码。这段前导代码将定义库所使用的数据类型,并将这些数据类型的句柄存储在用户代码和库代码共享的全局静态变量中。
示例: 用户自定义归约操作的示例:
计算一个复数数组的乘积,使用C语言实现。
typedef struct {
double real,imag;
} Complex;
/* the user-defined function
*/
void myProd( Complex *in, Complex *inout, int *len,
MPI_Datatype *dptr )
{
int i;
Complex c;
for (i=0; i< *len; ++i) {
c.real = inout->real*in->real -
inout->imag*in->imag;
c.imag = inout->real*in->imag +
inout->imag*in->real;
*inout = c;
in++; inout++;
}
}
/* and, to call it...
*/
...
/* each process has an array of 100 Complexes
*/
Complex a[100], answer[100];
MPI_Op myOp;
MPI_Datatype ctype;
/* explain to MPI how type Complex is defined
*/
MPI_Type_contiguous( 2, MPI_DOUBLE, &ctype );
MPI_Type_commit( &ctype );
/* create the complex-product user-op
*/
MPI_Op_create( myProd, True, &myOp );
MPI_Reduce( a, answer, 100, ctype, myOp, root, comm );
/* At this point, the answer, which consists of 100 Complexes,
* resides on process root
*/
Fortran版本的MPI_Reduce将使用Fortran调用约定调用用户定义的归约函数,并传递Fortran类型的数据类型参数;而C版本将使用C调用约定和C语言表示的数据类型句柄。计划混合使用不同语言的用户应相应地定义其归约函数。
17.2.270.6. 关于集合操作的注意事项
归约函数(MPI_Op)不会返回错误值。因此,如果这些函数检测到错误,它们只能选择调用MPI_Abort或静默跳过问题。因此,如果您将错误处理程序从MPI_ERRORS_ARE_FATAL更改为其他选项,例如MPI_ERRORS_RETURN,则可能不会指示任何错误。
原因在于确保所有集合例程返回相同错误值时存在的性能问题。
17.2.270.7. 错误
几乎所有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标准中的错误处理部分。