17.2.244. MPI_Iscan

MPI_Scan, MPI_Iscan, MPI_Scan_init - 计算包含式扫描(部分归约)

17.2.244.1. 语法

17.2.244.1.1. C语法

#include <mpi.h>

int MPI_Scan(const void *sendbuf, void *recvbuf, int count,
             MPI_Datatype datatype, MPI_Op op, MPI_Comm comm)

int MPI_Iscan(const void *sendbuf, void *recvbuf, int count,
              MPI_Datatype datatype, MPI_Op op, MPI_Comm comm,
              MPI_Request *request)

int MPI_Scan_init(const void *sendbuf, void *recvbuf, int count,
              MPI_Datatype datatype, MPI_Op op, MPI_Comm comm,
              MPI_Info info, MPI_Request *request)

17.2.244.1.2. Fortran语法

USE MPI
! or the older form: INCLUDE 'mpif.h'
MPI_SCAN(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, COMM, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER COUNT, DATATYPE, OP, COMM, IERROR

MPI_ISCAN(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, COMM, REQUEST, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER COUNT, DATATYPE, OP, COMM, REQUEST, IERROR

MPI_SCAN_INIT(SENDBUF, RECVBUF, COUNT, DATATYPE, OP, COMM, INFO, REQUEST, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER COUNT, DATATYPE, OP, COMM, INFO, REQUEST, IERROR

17.2.244.1.3. Fortran 2008 语法

USE mpi_f08
MPI_Scan(sendbuf, recvbuf, count, datatype, op, comm, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN) :: sendbuf
     TYPE(*), DIMENSION(..) :: recvbuf
     INTEGER, INTENT(IN) :: count
     TYPE(MPI_Datatype), INTENT(IN) :: datatype
     TYPE(MPI_Op), INTENT(IN) :: op
     TYPE(MPI_Comm), INTENT(IN) :: comm
     INTEGER, OPTIONAL, INTENT(OUT) :: ierror

MPI_Iscan(sendbuf, recvbuf, count, datatype, op, comm, request, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
     TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
     INTEGER, INTENT(IN) :: count
     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_Scan_init(sendbuf, recvbuf, count, datatype, op, comm, info, request, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
     TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
     INTEGER, INTENT(IN) :: count
     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.244.2. 输入参数

  • sendbuf: 发送缓冲区(可选)。

  • count: 输入缓冲区中的元素数量(整数)。

  • datatype: 输入缓冲区元素的数据类型(句柄)。

  • op: 操作(句柄)。

  • comm: 通信器(句柄)。

  • info: 信息(句柄,仅持久化)

17.2.244.3. 输出参数

  • recvbuf: 接收缓冲区(选项)。

  • request: 请求(句柄,仅限非阻塞模式)。

  • ierror: 仅限Fortran:错误状态(整数)。

17.2.244.4. 描述

MPI_Scan 用于对分布在调用进程间的数据执行包含式前缀归约操作。该操作会在秩为i的进程的recvbuf中返回秩为0到i(包含)的进程sendbuf中值的归约结果(根据op函数计算)。支持的操作类型、其语义以及发送接收缓冲区的约束条件与MPI_Reduce相同。

17.2.244.5. 示例

本示例使用用户自定义操作来生成分段扫描。分段扫描接收一组值和一组逻辑值作为输入,其中逻辑值用于划分扫描的不同段。例如,

values     v1      v2      v3      v4      v5      v6      v7      v8
logicals   0       0       1       1       1       0       0       1
result     v1    v1+v2     v3    v3+v4  v3+v4+v5   v6    v6+v7     v8

因此,对于等级j的结果是v(i) + … + v(j)的总和,其中i是最低等级,使得对于所有等级n,i <= n <= j,logical(n) = logical(j)。产生这种效果的运算符是

      [ u ]     [ v ]     [ w ]
      [   ]  o  [   ]  =  [   ]
      [ i ]     [ j ]     [ j ]

where

    ( u + v if i = j w = ( ( v if i != j

请注意这是一个非交换运算符。以下是实现它的C代码。

typedef struct {
        double val;
        int log;
} SegScanPair;

/*
 * the user-defined function
 */
void segScan(SegScanPair *in, SegScanPair *inout, int *len,
        MPI_Datatype *dptr)
{
        int i;
        SegScanPair c;

        for (i = 0; i < *len; ++i) {
                if (in->log == inout->log)
                        c.val = in->val + inout->val;
                else
                        c.val = inout->val;

                c.log = inout->log;
                *inout = c;
                in++;
                inout++;
        }
}

请注意,用户定义函数中的inout参数对应于运算符的右侧操作数。在使用此运算符时,我们必须小心指定它是不可交换的,如下所示:

int                     i, base;
SeqScanPair     a, answer;
MPI_Op          myOp;
MPI_Datatype    type[2] = {MPI_DOUBLE, MPI_INT};
MPI_Aint                disp[2];
int                     blocklen[2] = {1, 1};
MPI_Datatype    sspair;

/*
 * explain to MPI how type SegScanPair is defined
 */
MPI_Get_address(a, disp);
MPI_Get_address(a.log, disp + 1);
base = disp[0];
for (i = 0; i < 2; ++i)
        disp[i] -= base;
MPI_Type_struct(2, blocklen, disp, type, &sspair);
MPI_Type_commit(&sspair);

/*
 * create the segmented-scan user-op
 * noncommutative - set commute (arg 2) to 0
 */
MPI_Op_create((MPI_User_function *)segScan, 0, &myOp);
...
MPI_Scan(a, answer, 1, sspair, myOp, comm);

17.2.244.6. 原地选项的使用

当通信器为内部通信器时,您可以执行原地扫描操作(输出缓冲区同时用作输入缓冲区)。将变量MPI_IN_PLACE作为sendbuf参数的值。输入数据取自接收缓冲区,并被输出数据替换。

17.2.244.7. 关于集合操作的注意事项

MPI_Op类型的归约函数不会返回错误值。因此,如果这些函数检测到错误,它们只能选择调用MPI_Abort或静默跳过问题。因此,如果将错误处理程序从MPI_ERRORS_ARE_FATAL更改为其他设置(例如MPI_ERRORS_RETURN),则可能不会显示任何错误。

原因在于确保所有集合例程返回相同错误值时存在的性能问题。

17.2.244.8. 错误

几乎所有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手册页获取完整的MPI错误代码列表。

有关更多信息,请参阅MPI-3.1标准中的错误处理部分。