📄 mpi45.htm
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>第四章: 集合通信</TITLE>
<META NAME="GENERATOR" CONTENT="Mozilla/3.0Gold (Win16; I) [Netscape]">
</HEAD>
<BODY BGCOLOR="#F0F8FF">
<TABLE WIDTH="100%" >
<TR>
<TD align=left><B><FONT SIZE=+1>4.5 收集(Gather)</FONT></B></TD>
<TD align=right><A HREF="mpi44.htm" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi44.htm"><IMG SRC="backward.gif" tppabs="http://arch.cs.pku.edu.cn/image/backward.gif" ALT="BACKWARD" HEIGHT=32 WIDTH=32></A><A HREF="mpi46.htm" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi46.htm"><IMG SRC="forward.gif" tppabs="http://arch.cs.pku.edu.cn/image/forward.gif" ALT="FORWARD" HEIGHT=32 WIDTH=32></A>
</TD>
</TR>
</TABLE>
<P>
<HR WIDTH="100%"></P>
<PRE>MPI_GATHER(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root , comm)
IN sendbuf 发送消息缓冲区的起始地址(可变)
IN sendcount 发送消息缓冲区中的数据个数(整型)
IN sendtype 发送消息缓冲区中的数据类型(句柄)
OUT recvbuf 接收消息缓冲区的起始地址(可变,仅对于根进程)
IN recvcount 待接收的元素个数(整型,仅对于根进程)
IN recvtype 接收元素的数据类型(句柄,仅对于根进程)
IN root 接收进程的序列号(整型)
IN comm 通信子(句柄)</PRE>
<PRE>int MPI_Gather(void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm)</PRE>
<PRE>MPI_GATHER(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNT, RECVTYPE,
ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE, RECVCOUNT, RECVTYPE, ROOT, COMM, IERROR</PRE>
<P>每个进程(包括根进程)将其发送缓冲区中的内容发送到根进程,根进程根据发送这些数据的进程的序列号将它们依次存放到自已的消息缓冲区中.其结果就象一个组中的n个进程(包括根进程在内)都执行了一个调用:
</P>
<PRE> MPI_Send(sendbuf, sendcount, sendtype, root, ...), </PRE>
<P>同时根进程执行了n次调用: </P>
<PRE> MPI_Recv(recvbuf+i*recvcount*extent(recvtype), recvcount, recvtype, i,...),</PRE>
<P>此处extent(recvtype)是调用函数MPI_Type_extent()所返回的类型,另外一种描述就象是组中的n个进程发送的n条消息按照它们的序列号连接起来,根进程通过调用MPI_RECV(recvbuf,
recvcount*n, recvtype,...) 来将结果消息接收过来.</P>
<P>对于所有非根进程,接收消息缓冲区被忽略. </P>
<P>一般来说,sendtype和recvtype都允许是派生类型,并且第i个进程的sendcount
和sendtype的类型必须和根进程中的recvcount和recvtype相同,这就隐含了在每个进程和根进程之间,发送的数据量必须和接收的数据量相等,但发送方和接收方之间的不同数据类型映射仍然是允许的.</P>
<P>此函数中的所有参数对根进程来说都是很重要的,而对于其他进程只有sendbuf、
sendcount、sendtype、root和comm是必不可少的.参数root和comm在所有进程中都必须是一致的.
</P>
<P>对数据个数和类型的描述不应当导致根进程消息缓冲区的任何部分被重写,这样的调用是错误的.
</P>
<P>注意在根进程中recvcount所表示的是它从每个进程中接收的消息条数, 而不是它所接收的全部消息条数.
</P>
<PRE>MPI_GATHERV(sendbuf, sendcount, sendtype, recvbuf, recvcounts, displs,
recvtype, root, comm)
IN sendbuf 发送消息缓冲区的起始地址(可变)
IN sendcount 发送消息缓冲区中的数据个数(整型)
IN sendtype 发送消息缓冲区中的数据类型(句柄)
OUT recvbuf 接收消息缓冲区的起始地址(可变,仅对于根进程)
IN recvcounts 整型数组(长度为组的大小), 其值为从每个进程接收
的数据个数(仅对于根进程)
IN displs 整数数组,每个入口i表示相对于recvbuf的位移,此位
移处存放着从进程i中接收的输入数据(仅对于根进程)
IN recvtype 接收消息缓冲区中数据类型(仅对于根进程)(句柄)
IN root 接收进程的序列号(句柄)
IN comm 通信子(句柄)
</PRE>
<PRE>int MPI_Gatherv(void* sendbuf, int sendcount, MPI_Datatype sendtype,
void* recvbuf, int *recvcounts, int *displs,
MPI_Datatype recvtype, int root, MPI_Comm comm) </PRE>
<PRE>MPI_GATHERV(SENDBUF, SENDCOUNT, SENDTYPE, RECVBUF, RECVCOUNTS,DISPLS,
RECVTYPE, ROOT, COMM, IERROR)
<type> SENDBUF(*), RECVBUF(*)
INTEGER SENDCOUNT, SENDTYPE, RECVCOUNTS(*), DISPLS(*), RECVTYPE,
ROOT, COMM, IERROR </PRE>
<P>MPI_GATHERV扩展了MPI_GATHER的功能, 它可以从不同的进程接收不同数量的数
据,因为此时recvcounts是一个数组,除此之外,它还提供了更大的灵活性,比如它提供了一个新的参数displs,用户可以将接收的数据存放到根进程消息缓冲区的任意处.</P>
<P>此函数调用的结果就象组中的每个进程(包括根进程在内)都向根进程发送一条消息:
</P>
<PRE> MPI_Send(sendbuf, sendcount, sendtype, root,...), </PRE>
<P>同时根进程执行n次接收动作, </P>
<PRE> MPI_Recv(recvbuf+disp[i]*extent(recvtype),recvcounts[i],
recvtype,i, ...), </PRE>
<P>消息按其源进程中的序列号依次存放在根进程的消息接收缓冲区中,也就是说,第j个进程发送给根进程的数据将存放到根进程消息接收缓冲区recvbuf的第j部分,第j部分从recvbuf的偏移量为displs[j]处开始.</P>
<P>对于所有非根进程,接收消息缓冲区被忽略.</P>
<CENTER><P><A NAME="fig4.2"></A><IMG SRC="mpi42.gif" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi42.gif" HEIGHT=169 WIDTH=459></P></CENTER>
<CENTER><P>图4.2: 根进程从组中的每个其他进程收集100个整型数据</P></CENTER>
<P>第i个进程的sendcount和sendtype的类型必须和根进程中的recvcounts[i]和recvtype相同,这意谓着在每个进程和根进程之间,发关的数据量必须和接收的数据量相等.但发送方和接收方之间的不同数据类型映射仍然是允许的,如例4.6所示.
</P>
<P>此函数中的所有参数对根进程来说都是很重要的,而对于其他进程只有sendbuf、
sendcount、sendtype、root和comm是必不可少的.参数root和comm在所有进程中都必须是一致的.</P>
<P>对数据个数和类型的描述不应当导致根进程消息缓冲区的任何部分被重写,这样的调用是错误的.</P>
<P><A NAME="section1"></A><B><FONT SIZE=+1>4.5.1 应用MPI_GATHER和MPI_GATHERV的例子</FONT></B></P>
<P>例4.2: 从组中的每个进程收集100个整型数送给根进程.见<A HREF="#fig4.2">图4.2</A>.</P>
<PRE> MPI_Comm comm;
int gsize,sendarray[100];
int root,*rbuf;
......
MPI_Comm_size(comm,&gsize);
rbuf=(int *)malloc(gsize*100*sizeof(int));
MPI_Gather(sendarray,100,MPI_INT,rbuf,100,MPI_INT,root,comm);</PRE>
<P>例4.3: 例4.2的变更,仅对根进程分配消息接收缓冲区. </P>
<PRE> MPI_Comm comm;
int gsize, sendarray[100];
int root, myrank, *rbuf;
......
MPI_Comm_rank(comm, myrank);
if (myrank == root) {
MPI_Comm_size(comm, &gsize);
rbuf=(int *)malloc(gsize*100*sizeof(int));
}
MPI_Gather(sendarray, 100, MPI_INT, rbuf, 100, MPI_INT,
root, comm);</PRE>
<P>例4.4: 功能同例4.3,但使用了派生数据类型.注意此时数据类型已经不是整个集合中gsize*100个整型数了,因为在收集动作中提供了每个进程和根进程之间的类型匹配.</P>
<PRE> MPI_Comm comm;
int gsize, sendarray[100];
int root, *rbuf;
MPI_Datatype rtype;
......
MPI_Comm_size(comm, &gsize);
MPI_Type_contiguous(100, MPI_INT, &rtype);
MPI_Type_commit(&rtype);
rbuf = (int *)malloc(gsize*100*sizeof(int));
MPI_Gather(sendarray, 100, MPI_INT, rbuf, 1, rtype,
root,comm);</PRE>
<P>例4.5 每个进程向根进程发送100个整型数,但在接收端设置每个集合(100个数据)的步长,用MPI_GATHERV函数和displs参数来实现,假设步长≥100.见<A HREF="#fig4.3">图4.3.</A></P>
<PRE> MPI_Comm comm;
int gsize, sendarray[100];
int root, *rbuf, stride;
int *displs, i, *rcounts;
......
MPI_Comm_size(comm, &gsize);
rbuf = (int *)malloc(gsize*stride*sizeof(int));
displs = (int *)malloc(gsize*sizeof(int));
rcounts = (int *)malloc(gsize*sizeof(int));
for (i=0; i<gsize; ++i) {
displs[i] = i*stride;
rcounts[i] = 100;
}
MPI_Gatherv(sendarray, 100, MPI_INT, rbuf, rcounts, displs, MPI_INT,
root, comm);</PRE>
<P>注意:如果步长<100时程序将出错.</P>
<CENTER><P><A NAME="fig4.3"></A><IMG SRC="mpi43.gif" tppabs="http://arch.cs.pku.edu.cn/parallelprogramming/mpispec/mpi43.gif" HEIGHT=170 WIDTH=451></P></CENTER>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -