并行算法实践.ppt
并行算法实践,上篇 并行程序设计导论,国家高性能计算中心(合肥),2,2019/10/22,并行算法实践上篇 并行程序设计导论,单元I 并行程序设计基础单元II 并行程序编程指南单元III 并行程序开发方法,国家高性能计算中心(合肥),3,2019/10/22,单元II 并行程序编程指南,第四章 MPI编程指南第五章 PVM编程指南第六章 HPF编程指南第七章 OpenMP编程指南,国家高性能计算中心(合肥),4,2019/10/22,第四章 MPI编程指南,4.1 引言4.2 6个基本函数组成的MPI子集4.3 MPI消息4.4 点对点通信4.5 群集通信4.6 MPI扩展4.7 小结,国家高性能计算中心(合肥),5,2019/10/22,4.1 引言,MPI(Message Passing Interface )是一个消息传递接口标准MPI提供一个可移植、高效、灵活的消息传递接口库MPI以语言独立的形式存在,可运行在不同的操作系统和硬件平台上MPI提供与C/C+和Fortran语言的绑定,国家高性能计算中心(合肥),6,2019/10/22,4.1 引言,MPI的版本MPICH:http:/www-unix.mcs.anl.gov/mpi/mpichLAM (Local Area Multicomputer): http:/www.lam-mpi.orgOpen-MPI: http:/www.open-mpi.org/CHIMP: ftp:/ftp.epcc.ed.ac.uk/pub/chimp/release/,国家高性能计算中心(合肥),7,2019/10/22,4.2 6个基本函数组成的MPI子集,#include "mpi.h" /*MPI头函数,提供了MPI函数和数据类型定义*/int main( int argc, char* argv )int rank, size, tag=1;int senddata,recvdata;MPI_Status status; MPI_Init( /*总进程数目*/,国家高性能计算中心(合肥),8,2019/10/22,4.2 6个基本函数组成的MPI子集,if (rank=0)senddata=9999; MPI_Send( ,国家高性能计算中心(合肥),9,2019/10/22,4.2 6个基本函数组成的MPI子集,MPI初始化:通过MPI_Init函数进入MPI环境并完成所有的初始化工作。int MPI_Init( int *argc, char * * * argv )MPI结束:通过MPI_Finalize函数从MPI环境中退出。int MPI_Finalize(void),国家高性能计算中心(合肥),10,2019/10/22,4.2 6个基本函数组成的MPI子集,获取进程的编号:调用MPI_Comm_rank函数获得当前进程在指定通信域中的编号,将自身与其他程序区分。int MPI_Comm_rank(MPI_Comm comm, int *rank)获取指定通信域的进程数:调用MPI_Comm_size函数获取指定通信域的进程个数,确定自身完成任务比例。int MPI_Comm_size(MPI_Comm comm, int *size),国家高性能计算中心(合肥),11,2019/10/22,4.2 6个基本函数组成的MPI子集,消息发送:MPI_Send函数用于发送一个消息到目标进程。int MPI_Send(void *buf, int count, MPI_Datatype dataytpe, int dest, int tag, MPI_Comm comm) 消息接受:MPI_Recv函数用于从指定进程接收一个消息int MPI_Recv(void *buf, int count, MPI_Datatype datatyepe,int source, int tag, MPI_Comm comm, MPI_Status *status),国家高性能计算中心(合肥),12,2019/10/22,4.3 MPI消息,一个消息好比一封信消息的内容即信的内容,在MPI中称为消息缓冲(Message Buffer)消息的接收/发送者即信的地址,在MPI中称为消息信封(Message Envelop),国家高性能计算中心(合肥),13,2019/10/22,4.3 MPI消息,MPI中,消息缓冲由三元组标识消息信封由三元组标识 三元组的方式使得MPI可以表达更为丰富的信息,功能更强大,国家高性能计算中心(合肥),14,2019/10/22,4.3 MPI消息(数据类型),MPI的消息类型分为两种:预定义类型和派生数据类型(Derived Data Type)预定义数据类型:MPI支持异构计算(Heterogeneous Computing),它指在不同计算机系统上运行程序,每台计算可能有不同生产厂商,不同操作系统。MPI通过提供预定义数据类型来解决异构计算中的互操作性问题,建立它与具体语言的对应关系。派生数据类型:MPI引入派生数据类型来定义由数据类型不同且地址空间不连续的数据项组成的消息。,国家高性能计算中心(合肥),15,2019/10/22,4.3 MPI消息(数据类型),国家高性能计算中心(合肥),16,2019/10/22,4.3 MPI消息(数据类型),MPI提供了两个附加类型:MPI_BYTE和MPI_PACKED 。MPI_BYTE表示一个字节,所有的计算系统中一个字节都代表8个二进制位。MPI_PACKED预定义数据类型被用来实现传输地址空间不连续的数据项 。,国家高性能计算中心(合肥),17,2019/10/22,4.3 MPI消息(数据类型),double A100;MPI_Pack_size (50,MPI_DOUBLE,comm, MPI_Pack_size函数来决定用于存放50个MPI_DOUBLE数据项的临时缓冲区的大小 调用malloc函数为这个临时缓冲区分配内存 for循环中将数组A的50个偶序数元素打包成一个消息并存放在临时缓冲区,国家高性能计算中心(合肥),18,2019/10/22,4.3 MPI消息(数据类型),消息打包,然后发送 MPI_Pack(buf, count, dtype, /以上为待打包消息描述 packbuf, packsize, packpos, /以上为打包缓冲区描述 communicator)消息接收,然后拆包MPI_Unpack(packbuf, packsize, packpos, /以上为拆包缓冲区描述 buf, count, dtype, / 以上为拆包消息描述 communicatior),国家高性能计算中心(合肥),19,2019/10/22,4.3 MPI消息(数据类型),派生数据类型可以用类型图来描述,这是一种通用的类型描述方法,它是一系列二元组的集合,可以表示成如下格式:,···,在派生数据类型中,基类型可以是任何MPI预定义数据类型,也可以是其它的派生数据类型,即支持数据类型的嵌套定义。 如图,阴影部分是基类型所占用的空间,其它空间可以是特意留下的,也可以是为了方便数据对齐。,国家高性能计算中心(合肥),20,2019/10/22,4.3 MPI消息(数据类型),MPI提供了全面而强大的构造函数(Constructor Function)来定义派生数据类型。,国家高性能计算中心(合肥),21,2019/10/22,4.3 MPI消息(数据类型),double A100;MPI_Datatype EvenElements;···MPI_Type_vector(50, 1, 2, MPI_DOUBLE, 首先声明一个类型为MPI_Data_type的变量EvenElements 调用构造函数MPI_Type_vector(count, blocklength, stride, oldtype, &newtype)来定义派生数据类型 新的派生数据类型必须先调用函数MPI_Type_commit获得MPI系统的确认后才能调用MPI_Send进行消息发送,国家高性能计算中心(合肥),22,2019/10/22,4.3 MPI消息(数据类型),调用构造函数MPI_Type_vector(count, blocklength, stride, oldtype, &newtype)来定义派生数据类型。该newtype由count个数据块组成。而每个数据块由blocklength个oldtype类型的连续数据项组成。参数stride定义了两个连续数据块的起始位置之间的oldtype类型元素的个数。因此,两个块之间的间隔可以由(stride-blocklength)来表示。MPI_Type_vector(50,1,2,MPI_DOUBLE,&EvenElements)函数调用产生了派生数据类型EvenElements,它由50个块组成,每个块包含一个双精度数,后跟一个(21)MPI_DOUBLE(8字节)的间隔,接在后面的是下一块。上面的发送语句获取数组A的所有序号为偶数的元素并加以传递。,国家高性能计算中心(合肥),23,2019/10/22,4.3 MPI消息(数据类型),MPI_Type_vector(count, blocklength, stride, oldtype, &newtype),oldtype,oldtype,blocklength,oldtype,blocklength,stride,oldtype,count,国家高性能计算中心(合肥),24,2019/10/22,4.3 MPI消息(数据类型),左图10×10整数矩阵的所有偶序号的行:MPI_Type_vector( 5, / count 10, / blocklength 20, / stride MPI_INT, /oldtype &newtype),国家高性能计算中心(合肥),25,2019/10/22,4.3 MPI消息(数据类型),MPI_Type_struct( count, /成员数 array_of_blocklengths, /成员块长度数组 array_of_displacements,/成员偏移数组 array_of_types, /成员类型数组 newtype / 新类型),国家高性能计算中心(合肥),26,2019/10/22,MPI_Type_struct,type1,type1,type2,type3,type3,type3,struct,count = 3,array_of_blocklengths= 2, 1, 3,array_of_displacement= ?, ?, ?,array_of_type= type1,type2,type3,国家高性能计算中心(合肥),27,2019/10/22,type1,type1,type2,type3,type3,type3,struct,array_of_displacement= ?, ?, ?,MPI_Address(成员,偏移),国家高性能计算中心(合肥),28,2019/10/22,4.3 MPI消息(消息标签),为什么需要消息标签?当发送者连续发送两个相同类型消息给同一个接收者,如果没有消息标签,接收者将无法区分这两个消息 这段代码打算传送A的前32个字节进入X,传送B的前16个字节进入Y。但是,尽管消息B后发送,但可能先到达进程Q,就会被第一个接收函数接收在X中。使用标签可以避免这个错误,国家高性能计算中心(合肥),29,2019/10/22,4.3 MPI消息(消息标签),添加标签使得服务进程可以对两个不同的用户进程分别处理,提高灵活性,国家高性能计算中心(合肥),30,2019/10/22,4.3 MPI消息(通信域),通信域(Communicator)包括进程组(Process Group)和通信上下文(Communication Context)等内容,用于描述通信进程间的通信关系。通信域分为组内通信域和组间通信域,分别用来实现MPI的组内通信(Intra-communication)和组间通信(Inter-communication)。,国家高性能计算中心(合肥),31,2019/10/22,4.3 MPI消息(通信域),进程组是进程的有限、有序集。 有限意味着,在一个进程组中,进程的个数n是有限的,这里的n称为进程组大小(Group Size)。有序意味着,进程的编号是按0,1,n-1排列的一个进程用它在一个通信域(组)中的编号进行标识。组的大小和进程编号可以通过调用以下的MPI函数获得:MPI_Comm_size(communicator, &group_size)MPI_Comm_rank(communicator, &my_rank),国家高性能计算中心(合肥),32,2019/10/22,4.3 MPI消息(通信域),通信上下文:安全的区别不同的通信以免相互干扰通信上下文不是显式的对象,只是作为通信域的一部分出现进程组和通信上下文结合形成了通信域MPI_COMM_WORLD是所有进程的集合,国家高性能计算中心(合肥),33,2019/10/22,4.3 MPI消息(通信域),MPI提供丰富的函数用于管理通信域,国家高性能计算中心(合肥),34,2019/10/22,4.3 MPI消息(通信域),一个在MPI中创建新通信域的例子MPI_Comm MyWorld, SplitWorld;int my_rank,group_size, Color, Key;MPI_Init(,国家高性能计算中心(合肥),35,2019/10/22,4.3 MPI消息(通信域),MPI_Comm_dup(MPI_COMM_WORLD,&MyWorld)创建了一个新的通信域MyWorld,它包含了与原通信域MPI_COMM_WORLD相同的进程组,但具有不同的通信上下文。MPI_Comm_split(MyWorld,Color,Key,&SplitWorld)函数调用则在通信域MyWorld的基础上产生了几个分割的子通信域。原通信域MyWorld中的进程按照不同的Color值处在不同的分割通信域中,每个进程在不同分割通信域中的进程编号则由Key值来标识。,国家高性能计算中心(合肥),36,2019/10/22,4.3 MPI消息(通信域),组间通信域是一种特殊的通信域,该通信域包括了两个进程组,分属于两个进程组的进程之间通过组间通信域实现通信。一般把调用进程所在的进程组称为本地进程组,而把另外一个称为远程进程组。,国家高性能计算中心(合肥),37,2019/10/22,4.3 MPI消息(消息状态),消息状态(MPI_Status类型)存放接收消息的状态信息,包括:消息的源进程标识MPI_SOURCE消息标签MPI_TAG 错误状态MPI_ERROR其他包括数据项个数等,但多为系统保留的。是消息接收函数MPI_Recv的最后一个参数。当一个接收者从不同进程接收不同大小和不同标签的消息时,消息的状态信息非常有用。,国家高性能计算中心(合肥),38,2019/10/22,4.3 MPI消息(消息状态),假设多个客户进程发送消息给服务进程请求服务,通过消息标签来标识客户进程,从而服务进程采取不同的服务while (true)MPI_Recv(received_request,100,MPI_BYTE,MPI_Any_source,MPI_Any_tag,comm,国家高性能计算中心(合肥),39,2019/10/22,4.4 点对点通信,MPI的点对点通信(Point-to-Point Communication )同时提供了阻塞和非阻塞两种通信机制 。同时也支持多种通信模式。不同通信模式和不同通信机制的结合,便产生了非常丰富的点对点通信函数。,国家高性能计算中心(合肥),40,2019/10/22,4.4 点对点通信(通信模式),通信模式(Communication Mode)指的是缓冲管理,以及发送方和接收方之间的同步方式。 共有下面四种通信模式同步(synchronous)通信模式缓冲(buffered)通信模式标准(standard)通信模式就绪(ready)通信模式,国家高性能计算中心(合肥),41,2019/10/22,4.4 点对点通信(通信模式),同步通信模式:只有相应的接收过程已经启动,发送过程才正确返回。因此,同步发送返回后,表示发送缓冲区中的数据已经全部被系统缓冲区缓存,并且已经开始发送。当同步发送返回后,发送缓冲区可以被释放或者重新使用。,国家高性能计算中心(合肥),42,2019/10/22,4.4 点对点通信(通信模式),缓冲通信模式:缓冲通信模式的发送不管接收操作是否已经启动都可以执行。但是需要用户程序事先申请一块足够大的缓冲区,通过MPI_Buffer_attch实现,通过MPI_Buffer_detach来回收申请的缓冲区。,国家高性能计算中心(合肥),43,2019/10/22,4.4 点对点通信(通信模式),标准通信模式:是否对发送的数据进行缓冲由MPI的实现来决定,而不是由用户程序来控制。发送可以是同步的或缓冲的,取决于实现,国家高性能计算中心(合肥),44,2019/10/22,4.4 点对点通信(通信模式),就绪通信模式:发送操作只有在接收进程相应的接收操作已经开始才进行发送。当发送操作启动而相应的接收还没有启动,发送操作将出错。就绪通信模式的特殊之处就是接收操作必须先于发送操作启动。,国家高性能计算中心(合肥),45,2019/10/22,4.4 点对点通信(通信模式),阻塞和非阻塞通信的主要区别在于返回后的资源可用性阻塞通信返回的条件:通信操作已经完成,即消息已经发送或接收调用的缓冲区可用。若是发送操作,则该缓冲区可以被其它的操作更新;若是接收操作,该缓冲区的数据已经完整,可以被正确引用。,国家高性能计算中心(合肥),46,2019/10/22,4.4 点对点通信(通信模式),MPI的发送操作支持四种通信模式,它们与阻塞属性一起产生了MPI中的8种发送操作。而MPI的接收操作只有两种:阻塞接收和非阻塞接收。非阻塞通信返回后并不意味着通信操作的完成,MPI还提供了对非阻塞通信完成的检测,主要的有两种:MPI_Wait函数和MPI_Test函数。,国家高性能计算中心(合肥),47,2019/10/22,4.4 点对点通信(通信模式),MPI的点对点通信操作,国家高性能计算中心(合肥),48,2019/10/22,4.4 点对点通信(通信模式),在阻塞通信的情况下,通信还没有结束的时候,处理器只能等待,浪费了计算资源。 一种常见的技术就是设法使计算与通信重叠,非阻塞通信可以用来实现这一目的。 一条三进程的流水线,一个进程连续地从左边的进程接收一个输入数据流,计算一个新的值,然后将它发送给右边的进程。 while (Not_Done) MPI_Irevc(NextX, );MPI_Isend(PreviousY, );CurrentY=Q(CurrentX);,国家高性能计算中心(合肥),49,2019/10/22,4.4 点对点通信(通信模式),非阻塞通信中,双缓冲是一种常用的方法。我们需要为X和Y各自准备两个单独的缓冲,当接收进程向缓冲中放下一个X时,计算进程可能从另一个缓冲中读当前的X。 我们需要确信缓冲中的数据在缓冲被更新之前使用 。代码如下while (Not_Done)if (X=Xbuf0) X=Xbuf1; Y=Ybuf1; Xin=Xbuf0; Yout=Ybuf0;else X=Xbuf0; Y=Ybuf0; Xin=Xbuf1; Yout=Ybuf1;MPI_Irevc(Xin, , recv_handle);MPI_Isend(Yout, , send_handle);Y=Q(X); /* 重叠计算*/MPI_Wait(recv_handle,recv_status);MPI_Wait(send_handle,send_status);,国家高性能计算中心(合肥),50,2019/10/22,4.4 点对点通信(通信模式),send_handle和revc_handle分别用于检查发送接收是否完成。检查发送接收通过调用MPI_Wait(Handle, Status)来实现,它直到Handle指示的发送或接收操作已经完成才返回 。另一个函数MPI_Test(Handle, Flag, Status)只测试由Handle指示的发送或接收操作是否完成,如果完成,就对Flag赋值True,这个函数不像MPI_Wait,它不会被阻塞。,国家高性能计算中心(合肥),51,2019/10/22,4.4 点对点通信Send-Recv,给一个进程发送消息,从另一个进程接收消息;特别适用于在进程链(环)中进行“移位”操作,而避免在通讯为阻塞方式时出现死锁。 MPI_Sendrecv( sendbuf, sendcount, sendtype, dest, sendtag, /以上为消息发送的描述 recvbuf, recvcount, recvtype, source, recvtag, / 以上为消息接收的描述 comm, status),国家高性能计算中心(合肥),52,2019/10/22,4.5 群集通信,群集通信(Collective Communications)是一个进程组中的所有进程都参加的全局通信操作。 群集通信一般实现三个功能:通信、聚集和同步。 通信功能主要完成组内数据的传输 聚集功能在通信的基础上对给定的数据完成一定的操作 同步功能实现组内所有进程在执行进度上取得一致,国家高性能计算中心(合肥),53,2019/10/22,4.5 群集通信,群集通信,按照通信方向的不同,又可以分为三种:一对多通信,多对一通信和多对多通信。 一对多通信:一个进程向其它所有的进程发送消息,这个负责发送消息的进程叫做Root进程。多对一通信:一个进程负责从其它所有的进程接收消息,这个接收的进程也叫做Root进程。 多对多通信:每一个进程都向其它所有的进程发送或者接收消息。,国家高性能计算中心(合肥),54,2019/10/22,4.5 群集通信,国家高性能计算中心(合肥),55,2019/10/22,4.5 群集通信,广播是一对多通信的典型例子,其调用格式如下:MPI_Bcast(Address, Count, Datatype, Root, Comm),国家高性能计算中心(合肥),56,2019/10/22,4.5 群集通信,广播的特点标号为Root的进程发送相同的消息给通信域Comm中的所有进程。消息的内容如同点对点通信一样由三元组标识。对Root进程来说,这个三元组既定义了发送缓冲也定义了接收缓冲。对其它进程来说,这个三元组只定义了接收缓冲,国家高性能计算中心(合肥),57,2019/10/22,4.5 群集通信,收集是多对一通信的典型例子,其调用格式下:MPI_Gather(SendAddress, SendCount, SendDatatype,RecvAddress, RecvCount, RecvDatatype, Root, Comm),国家高性能计算中心(合肥),58,2019/10/22,4.5 群集通信,收集的特点在收集操作中,Root进程从进程域Comm的所有进程(包括它自已)接收消息。这n个消息按照进程的标识rank排序进行拼接,然后存放在Root进程的接收缓冲中。接收缓冲由三元组标识,发送缓冲由三元组标识,所有非Root进程忽略接收缓冲。,国家高性能计算中心(合肥),59,2019/10/22,4.5 群集通信,散播也是一个一对多操作,其调用格式如下:MPI_Scatter(SendAddress, SendCount, SendDatatype,RecvAddress, RecvCount, RecvDatatype, Root, Comm),国家高性能计算中心(合肥),60,2019/10/22,4.5 群集通信,散播的特点Scatter执行与Gather相反的操作。Root进程给所有进程(包括它自已)发送一个不同的消息,这n (n为进程域comm包括的进程个数)个消息在Root进程的发送缓冲区中按进程标识的顺序有序地存放。每个接收缓冲由三元组标识,所有的非Root进程忽略发送缓冲。对Root进程,发送缓冲由三元组标识。,国家高性能计算中心(合肥),61,2019/10/22,4.5 群集通信,全局收集多对多通信的典型例子,其调用格式如下:MPI_Allgather(SendAddress, SendCount, SendDatatype,RecvAddress, RecvCount, RecvDatatype, Comm)Allgather操作相当于每个进程都作为ROOT进程执行了一次Gather调用,即每一个进程都按照Gather的方式收集来自所有进程(包括自己)的数据。,国家高性能计算中心(合肥),62,2019/10/22,4.5 群集通信,全局交换也是一个多对多操作,其调用格式如下:MPI_Alltoall(SendAddress, SendCount, SendDatatype,RecvAddress, RecvCount, RecvDatatype, Comm),国家高性能计算中心(合肥),63,2019/10/22,4.5 群集通信,全局交换的特点在全局交换中,每个进程发送一个消息给所有进程(包括它自已)。这n (n为进程域comm包括的进程个数)个消息在它的发送缓冲中以进程标识的顺序有序地存放。从另一个角度来看这个通信,每个进程都从所有进程接收一个消息,这n个消息以标号的顺序被连接起来,存放在接收缓冲中。全局交换等价于每个进程作为Root进程执行了一次散播操作。,国家高性能计算中心(合肥),64,2019/10/22,4.5 群集通信,同步功能用来协调各个进程之间的进度和步伐 。目前MPI的实现中支持一个同步操作,即路障同步(Barrier)。路障同步的调用格式如下:MPI_Barrier(Comm)在路障同步操作MPI_Barrier(Comm)中,通信域Comm中的所有进程相互同步。在该操作调用返回后,可以保证组内所有的进程都已经执行完了调用之前的所有操作,可以开始该调用后的操作。,国家高性能计算中心(合肥),65,2019/10/22,4.5 群集通信,群集通信的聚合功能使得MPI进行通信的同时完成一定的计算。 MPI聚合的功能分三步实现首先是通信的功能,即消息根据要求发送到目标进程,目标进程也已经收到了各自需要的消息;然后是对消息的处理,即执行计算功能;最后把处理结果放入指定的接收缓冲区。MPI提供了两种类型的聚合操作: 归约和扫描。,国家高性能计算中心(合肥),66,2019/10/22,4.5 群集通信,归约的调用格式如下:MPI_Reduce(SendAddress, RecvAddress, Count, Datatype, Op, Root, Comm)归约的特点归约操作对每个进程的发送缓冲区(SendAddress)中的数据按给定的操作进行运算,并将最终结果存放在Root进程的接收缓冲区(RecvAddress)中。参与计算操作的数据项的数据类型在Datatype域中定义,归约操作由Op域定义。归约操作可以是MPI预定义的,也可以是用户自定义的。归约操作允许每个进程贡献向量值,而不只是标量值,向量的长度由Count定义。,国家高性能计算中心(合肥),67,2019/10/22,4.5 群集通信,MPI_Reduce: root0,OpMPI_SUMMPI_Allreduce: OpMPI_SUM归约前的发送缓冲区,A0,A1,A2,B0,B1,B2,C0,C1,C2,P0:,P1:,P2:,国家高性能计算中心(合肥),68,2019/10/22,4.5 群集通信,MPI_Reduce: rootP0,OpMPI_SUM归约后的接收缓冲区,A0+B0+C0,A1+B1+C1,A2+B2+C2,P0:,P1:,P2:,国家高性能计算中心(合肥),69,2019/10/22,4.5 群集通信,MPI_Allreduce: OpMPI_SUM归约后的接收缓冲区,A0+B0+C0,A1+B1+C1,A2+B2+C2,P0:,A0+B0+C0,A1+B1+C1,A2+B2+C2,P1:,A0+B0+C0,A1+B1+C1,A2+B2+C2,P2:,国家高性能计算中心(合肥),70,2019/10/22,4.5 群集通信,MPI预定义的归约操作,国家高性能计算中心(合肥),71,2019/10/22,4.5 群集通信,用户自定义的归约操作int MPI_Op_create( /用户自定义归约函数 MPI_User_function *function, / if (commute=true) Op是可交换且可结合 / else 按进程号升序进行Op操作 int commute, MPI_Op *op),国家高性能计算中心(合肥),72,2019/10/22,4.5 群集通信,用户自定义的归约操作函数须有如下形式:typedef void MPI_User_function( void *invec, void *inoutvec, int *len, /从MPI_Reduce调用中传入的count MPI_Datatype *datatype); 函数语义如下:for(i=0;i<*len;i+) *inoutvec = *invec USER_OP *inouvec; inoutvec+; invec+;,国家高性能计算中心(合肥),73,2019/10/22,4.5 群集通信,用户自定义归约示例: (1)复数乘法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; ireal*in->real - inout->imag*in->imag; c.imag = inout->real*in->imag + inout->imag*in->real; *inout = c; in+; inout+;,国家高性能计算中心(合肥),74,2019/10/22,4.5 群集通信,用户自定义归约示例: (1)复数乘法/* explain to MPI how type Complex is defined */ MPI_Type_contiguous( 2, MPI_DOUBLE, ,国家高性能计算中心(合肥),75,2019/10/22,4.5 群集通信,用户自定义归约示例: (2)矩阵“乘法”(cij = aij*bij)void myProd( double *in, double *inout, int *len, MPI_Datatype *dptr ) int i,j; for (i=0; i< *len; +i) for(j=0;j<LEN*LEN;j+) *inout = (*inout) * (*in); in+; inout+; ,国家高性能计算中心(合肥),76,2019/10/22,4.5 群集通信,用户自定义归约示例: (2)矩阵“乘法” MPI_Type_contiguous( LEN*LEN, MPI_DOUBLE, ,国家高性能计算中心(合肥),77,2019/10/22,4.5 群集通信,扫描的调用格式如下:MPI_scan(SendAddress, RecvAddress, Count, Datatype, Op, Comm)扫描的特点可以把扫描操作看作是一种特殊的归约,即每一个进程都对排在它前面的进程进行归约操作。MPI_SCAN调用的结果是,对于每一个进程i,它对进程0,1,i的发送缓冲区的数据进行了指定的归约操作。扫描操作也允许每个进程贡献向量值,而不只是标量值。向量的长度由Count定义。,国家高性能计算中心(合肥),78,2019/10/22,4.5 群集通信,MPI_scan:OpMPI_SUM扫描前发送缓冲区:,A0,A1,A2,B0,B1,B2,C0,C1,C2,P0:,P1:,P2:,国家高性能计算中心(合肥),79,2019/10/22,4.5 群集通信,MPI_scan:OpMPI_SUM扫描后接收缓冲区:,A0,A1,A2,A0+B0,A1+B1,A2+B2,A0+B0+C0,A1+B1+C1,A2+B2+C2,P0:,P1:,P2:,国家高性能计算中心(合肥),80,2019/10/22,4.5 群集通信,所有的MPI群集通信操作都具有如下的特点:通信域中的所有进程必须调用群集通信函数。如果只有通信域中的一部分成员调用了群集通信函数而其它没有调用,则是错误的。除MPI_Barrier以外,每个群集通信函数使用类似于点对点通信中的标准、阻塞的通信模式。也就是说,一个进程一旦结束了它所参与的群集操作就从群集函数中返回,但是并不保证其它进程执行该群集函数已经完成。一个群集通信操作是不是同步操作取决于实现。MPI要求用户负责保证他的代码无论实现是否同步都必须是正确的。 所有参与群集操作的进程中,Count和Datatype必须是兼容的。群集通信中的消息没有消息标签参数,消息信封由通信域和源/目标定义。例如在MPI_Bcast中,消息的源是Root进程,而目标是所有进程(包括Root)。,