17.2.246. MPI_Iscatterv

MPI_Scatterv, MPI_Iscatterv, MPI_Scatterv_init - 将缓冲区数据分块分发到组内所有任务。

17.2.246.1. 语法

17.2.246.1.1. C语言语法

#include <mpi.h>

int MPI_Scatterv(const void *sendbuf, const int sendcounts[], const int displs[],
     MPI_Datatype sendtype, void *recvbuf, int recvcount,
     MPI_Datatype recvtype, int root, MPI_Comm comm)

int MPI_Iscatterv(const void *sendbuf, const int sendcounts[], const int displs[],
     MPI_Datatype sendtype, void *recvbuf, int recvcount,
     MPI_Datatype recvtype, int root, MPI_Comm comm, MPI_Request *request)

int MPI_Scatterv_init(const void *sendbuf, const int sendcounts[], const int displs[],
     MPI_Datatype sendtype, void *recvbuf, int recvcount,
     MPI_Datatype recvtype, int root, MPI_Comm comm, MPI_Info info, MPI_Request *request)

17.2.246.1.2. Fortran语法

USE MPI
! or the older form: INCLUDE 'mpif.h'
MPI_SCATTERV(SENDBUF, SENDCOUNTS, DISPLS, SENDTYPE, RECVBUF,
             RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER SENDCOUNTS(*), DISPLS(*), SENDTYPE
     INTEGER RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR

MPI_ISCATTERV(SENDBUF, SENDCOUNTS, DISPLS, SENDTYPE, RECVBUF,
             RECVCOUNT, RECVTYPE, ROOT, COMM, REQUEST, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER SENDCOUNTS(*), DISPLS(*), SENDTYPE
     INTEGER RECVCOUNT, RECVTYPE, ROOT, COMM, REQUEST, IERROR

MPI_SCATTERV_INIT(SENDBUF, SENDCOUNTS, DISPLS, SENDTYPE, RECVBUF,
             RECVCOUNT, RECVTYPE, ROOT, COMM, INFO, REQUEST, IERROR)
     <type>  SENDBUF(*), RECVBUF(*)
     INTEGER SENDCOUNTS(*), DISPLS(*), SENDTYPE
     INTEGER RECVCOUNT, RECVTYPE, ROOT, COMM, INFO, REQUEST, IERROR

17.2.246.1.3. Fortran 2008 语法

USE mpi_f08
MPI_Scatterv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcount,
             recvtype, root, comm, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN) :: sendbuf
     TYPE(*), DIMENSION(..) :: recvbuf
     INTEGER, INTENT(IN) :: sendcounts(*), displs(*), recvcount, root
     TYPE(MPI_Datatype), INTENT(IN) :: sendtype, recvtype
     TYPE(MPI_Comm), INTENT(IN) :: comm
     INTEGER, OPTIONAL, INTENT(OUT) :: ierror

MPI_Iscatterv(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcount,
             recvtype, root, comm, request, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
     TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
     INTEGER, INTENT(IN), ASYNCHRONOUS :: sendcounts(*), displs(*)
     INTEGER, INTENT(IN) :: recvcount, root
     TYPE(MPI_Datatype), INTENT(IN) :: sendtype, recvtype
     TYPE(MPI_Comm), INTENT(IN) :: comm
     TYPE(MPI_Request), INTENT(OUT) :: request
     INTEGER, OPTIONAL, INTENT(OUT) :: ierror

MPI_scatterv_init(sendbuf, sendcounts, displs, sendtype, recvbuf, recvcount,
             recvtype, root, comm, info, request, ierror)
     TYPE(*), DIMENSION(..), INTENT(IN), ASYNCHRONOUS :: sendbuf
     TYPE(*), DIMENSION(..), ASYNCHRONOUS :: recvbuf
     INTEGER, INTENT(IN), ASYNCHRONOUS :: sendcounts(*), displs(*)
     INTEGER, INTENT(IN) :: recvcount, root
     TYPE(MPI_Datatype), INTENT(IN) :: sendtype, recvtype
     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.246.2. 输入参数

  • sendbuf: 发送缓冲区的地址(仅在根节点有效,可选)。

  • sendcounts: 整数数组(长度为组大小),指定发送给每个处理器的元素数量。

  • displs: 整型数组(长度为组大小)。条目i指定相对于sendbuf的位移量,表示发送给进程i的起始数据位置。

  • sendtype: 发送缓冲区元素的数据类型(句柄)。

  • recvcount: 接收缓冲区中的元素数量(整数)。

  • recvtype: 接收缓冲区元素的数据类型(句柄)。

  • root: 发送进程的等级(整数)。

  • comm: 通信器(句柄)。

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

17.2.246.3. 输出参数

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

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

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

17.2.246.4. 描述

MPI_ScattervMPI_Gatherv 的逆操作。

MPI_Scatterv 扩展了 MPI_Scatter 的功能,允许向每个进程发送不同数量的数据,因为 sendcounts 现在是一个数组。它还通过提供新参数 displs,在根进程上获取数据的位置方面提供了更大的灵活性。

结果相当于根节点执行了n次发送操作,

MPI_Send(sendbuf + displs[i] * extent(sendtype),
         sendcounts[i], sendtype, i, ...);

// and each process executed a receive,

MPI_Recv(recvbuf, recvcount, recvtype, root, ...)

对于所有非根进程,发送缓冲区将被忽略。

根进程中由sendcount[i]和sendtype隐含的类型签名必须与进程i中由recvcountrecvtype隐含的类型签名相等(但类型映射可以不同)。这意味着发送的数据量必须等于接收的数据量,在每个进程与根进程之间成对对应。发送方和接收方之间仍允许使用不同的类型映射。

该函数的所有参数在进程root上都是有效的,而在其他进程上,只有参数recvbufrecvcountrecvtyperootcomm是有效的。参数rootcomm在所有进程上的值必须相同。

计数、类型和位移的指定不应导致根节点上的任何位置被多次读取。

示例1: MPI_Gatherv手册页中示例5的反向操作。在发送端(根进程)各数据块之间存在可变步长,在接收端每个进程i会将100 - i个元素接收到100x150的C数组的第i列中。

MPI_Comm comm;
int gsize,recvarray[100][150],*rptr;
int root, *sendbuf, myrank, bufsize, *stride;
MPI_Datatype rtype;
int i, *displs, *scounts, offset;
...
MPI_Comm_size( comm, &gsize);
MPI_Comm_rank( comm, &myrank );

stride = (int *)malloc(gsize*sizeof(int));
...
/* stride[i] for i = 0 to gsize-1 is set somehow
 * sendbuf comes from elsewhere
 */
...
displs = (int *)malloc(gsize*sizeof(int));
scounts = (int *)malloc(gsize*sizeof(int));
offset = 0;
for (i=0; i<gsize; ++i) {
    displs[i] = offset;
    offset += stride[i];
    scounts[i] = 100 - i;
}
/* Create datatype for the column we are receiving
 */
MPI_Type_vector( 100-myrank, 1, 150, MPI_INT, &rtype);
MPI_Type_commit( &rtype );
rptr = &recvarray[0][myrank];
MPI_Scatterv(sendbuf, scounts, displs, MPI_INT,
             rptr, 1, rtype, root, comm);

示例2: MPI_Gather手册页中示例1的反向操作。根进程将每组100个整数的数据分散到其他进程,但这些100个整数的组在发送缓冲区中相隔stride个整数。需要使用MPI_Scatterv,其中stride >= 100。

MPI_Comm comm;
int gsize,*sendbuf;
int root, rbuf[100], i, *displs, *scounts;

...

MPI_Comm_size(comm, &gsize);
sendbuf = (int *)malloc(gsize*stride*sizeof(int));
...
displs = (int *)malloc(gsize*sizeof(int));
scounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i<gsize; ++i) {
   displs[i] = i*stride;
   scounts[i] = 100;
}
MPI_Scatterv(sendbuf, scounts, displs, MPI_INT,
             rbuf, 100, MPI_INT, root, comm);

17.2.246.5. 原地选项的使用

当通信器为内部通信器时,可以执行原地分散操作(输出缓冲区同时用作输入缓冲区)。将变量MPI_IN_PLACE作为根进程recvbuf的值。在这种情况下,recvcountrecvtype将被忽略,且根进程不会向自身发送数据。

请注意,MPI_IN_PLACE是一种特殊类型的值;其使用限制与MPI_BOTTOM相同。

由于原地(in-place)选项将接收缓冲区转换为发送-接收缓冲区,包含INTENT的Fortran绑定必须将其标记为INOUT,而非OUT。

17.2.246.6. 当通信器为跨通信器时

当通信器是跨通信器时,第一组中的根进程会向第二组中的所有进程发送数据。第一组定义了根进程。该进程使用MPI_ROOT作为其root参数的值。其余进程使用MPI_PROC_NULL作为它们的root参数值。第二组中的所有进程使用第一组中该根进程的秩作为它们root参数的值。第一组中根进程的接收缓冲区参数必须与第二组中进程的接收缓冲区参数保持一致。

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

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