MPI并行程序设计基础一、并行编程模型与并行语言1、并行编程模型目前最重要的模型数据并行即将相同的操作同时作用于不同的数据,因此适合在SIMD及SPMD并行计算机上运行,在向量机上通过数据并行求解问题的实践也说明数据并行是可以高效地解决一大类科学与工程计算问题的。消息传递即各个并行执行的部分之间通过传递消息来交换信息、协调步伐、控制执行。消息传递一般是面向分布式内存的,但是它也可适用于共享内存的并行机。消息传递为编程者提供了更灵活的控制手段和表达并行的方法。一些用数据并行方法很难表达的并行算法都可以用消息传递模型来实现,灵活性和控制手段的多样化是消息传递并行程序能提供高的执行效率的重要原因。数据并行、消息传递、共享变量模型、函数式模型等。对比内容数据并行消息传递编程级别高低适用的并行机类型SIMD/SPMDSIMD/MIMD/SPMD/MPMD执行效率依赖编译器高地址空间单一多个存储空间共享内存分布式或共享内存通讯类型编程器负责程序员负责问题类数据并行类问题数据并行任务并行目前状况缺乏高速编程器的支持使用广泛数据并行与消息传递并行编程模型性能对比并行程序是通过并行语言来表达的,产生主要有三种方式:2、并行语言提供并行库扩充语法成分新语言改动多少并行语言的实现方式和实现难度之间的关系3)不改变串行语言仅为串行语言提供可调用的并行库。1)设计全新的并行语言;2)扩展原来的串行语言的语法成分使它支持并行特征;二、并行算法1、并行算法分类根据运算的基本对象的不同可以将并行算法分为数值并行算法(数值计算)和非数值并行算法(符号计算)。根据进程之间的依赖关系可以分为同步并行算法(步调一致)、异步并行算法(步调进展互不相同)和纯并行算法(各部分之间没有关系)。根据并行计算任务的大小可以分为粗粒度并行算法(一个并行任务包含较长的程序段和较大的计算量)、细粒度并行算法(一个并行任务包含较短的程序段和较小的计算量)以及介于二者之间的中粒度并行算法。2、并行算法的设计针对机群系统重点介绍SPMD和MPMD并行算法的设计。对于机群计算可以是数值或非数值的计算,并不是影响性能的关键;也可以是同步、松同步或异步的,但以同步和松同步为主;并行的粒度一般是大粒度或中粒度的。对于MPMD并行算法各并行部分一般是异步执行的。只要能够大大降低通信次数,增大计算相对于通信的比重,则该MPMD算法就可以取得较高的效率.三、MPI简介1、MPIMPI是一个库而不是一门语言。按照并行语言的分类,可以把FORTRAN+MPI或C+MPI看作是一种在原来串行语言基础之上扩展后得到的并行语言。从语法上说它遵守所有对库函数/过程的调用规则和一般的函数/过程没有什么区别。MPI是一种基于消息传递的并行程序设计标准,而不特指某一个对它的具体实现。(MessagePassingInterface)2、MPI的目标较高的通信性能、较好的程序可移植性、强大的功能•提供应用程序编程接口;•提高通信;•可在异构环境下提供实现;•提供的接口可以方便C语言和Fortran77的调用;•提供可靠的通信接口,即用户不必处理通信失败;•定义的接口和现在已有接口,如PVM,NX,Express,p4等差别不能太大,但是允许扩展以提供更大的灵活性;•定义的接口能在基本的通信和系统软件无重大改变时,在许多并行计算机生产商的平台上实现,即接口的语义是独立于语言的;•接口设计应是线程安全的。实用、可移植、高效、灵活3、MPI的产生有关的工作及其组织包括:Venus(IBM)、NX/2(Intel)、Express(Parasoft)、Vertex(nCUBE)、P4(ANL)、PARMACS(ANL)、Zipcode(MSU)、Chimp(EdinburghUniversity)、PVM(ORNL,UTK,EmoryU.)、Chameleon(ANL)、PICL(ANL)等。1)开始于1992.4分布存储环境中消息传递标准的讨论会;2)1992.11推出初始草案;3)1993.2完成修订版(MPI1.0);4)1995.6MPI1.1;5)1997.7对原有MPI作出重大扩充,并推出扩展部分MPI-2;4、MPI的语言绑定MPI-1提出了MPI和FORTRAN77与C语言的绑定。绑定示例:MPI_COMM_RANK(comm,rank)INcommOUTrankintMPI_Comm_rank(MPI_Commcomm,int*rank)该函数用于得到当前进程标识。MPI-2加入了对Fortran90和C++语言的绑定说明。5、主要的MPI实现实现名称研制单位网址MPICHArgonneandMSU://ftp.epcc.ed.ac.uk/pub/packages/chimp/LAMOhioStateUniversityMPICH是一种最重要的MPI实现。MPICH对MPI标准的支持基本与官方MPI标准的发表同步。除了必要的MPI功能外,MPICH包还包括了一系列的工具。MPICH提供源码,它还被广泛的用于研究。四、常用的MPI子集MPI-1中共有128个调用接口,在MPI-2中有287个。MPI所有的通信功能可以用6个基本的调用来实现1、MPI调用的参数说明1)IN:输入调用部分传递给MPI的参数。MPI除了使用该参数外不允许对这一参数做任何修改;MPI的定义在最大范围内避免INOUT参数的使用,因为这些使用易于出错。2)OUT:输出MPI返回给调用部分的结果参数。该参数的初始值对MPI没有任何意义;3)INOUT:输入输出调用部分。首先将该参数传递给MPI,MPI对该参数引用修改后将结果返回给外部调用,该参数的初始值和返回结果都有意义。在MPI中OUT或INOUT类型的参数不能被其它参数作为别名使用。voidcopyIntBuffer(int*pin,int*pout,intlen){inti;for(i=0;ilen;++i)*pout++=*pin++;}示例:inta[10];copyIntBuffer(a,a+3,7);过程:调用:在C语言中允许这样,但除非特别说明,MPI调用禁止这样使用。2、MPI初始化intMPI_Init(int*argc,char***argv)MPI_INIT是MPI程序的第一个调用,它完成MPI程序所有的初始化工作,所有MPI程序的第一条可执行语句都是这条语句。3、MPI结束intMPI_Finalize(void)MPI_FINALIZE是MPI程序的最后一个调用,它结束MPI程序的运行,它是MPI程序的最后一条可执行语句,否则程序的运行结果是不可预知的。4、当前进程标识该调用返回调用进程在给定的通信域中的进程标识号,有了该标识号,不同的进程就可以将自身和其它的进程区别开来,以实现各进程的并行和协作。5、通信域包含的进程数intMPI_Comm_rank(MPI_Commcomm,int*rank)INcomm:该进程所在的通信域句柄;OUTrank:调用进程在comm中的标识号。intMPI_Comm_size(MPI_Commcomm,int*size)INcomm:通信域句柄OUTsize:通信域comm内包括的进程数(整数)该调用返回给定通信域中所包括的进程的个数,不同的进程通过这一调用得知在给定的通信域中共有多少个进程在并行执行。通信域(Communicator)MPI系统提供缺省的通信域MPI_COMM_WORLD,所有启动的MPI进程通过调用函数MPI_Init()包含在该通信域内。对MPI的C语言绑定来说,进程号从0开始。理解为一类进程的集合,且在该集合内,进程间可以相互通信;任何MPI通信函数均必须在某个通信域内发生;6、消息发送intMPI_Send(void*buf,intcount,MPI_Datatypedatatype,intdest,inttag,MPI_Commcomm)INbuf:发送缓冲区(可选类型)INcount:发送数据个数(非负整数)INdatatype:发送数据类型(句柄)INdest:目的进程号(整数)INtag:消息标签(整型)INcomm:通信域(句柄)将发送缓冲区中的count个datatype数据类型的数据发送到目的进程,目的进程在通信域中的标识号是dest,本次发送的消息标志是tag,使用该标志就可以把本次发送的消息和本进程向同一目的进程发送的其它消息区别开来。7、消息接收intMPI_Recv(void*buf,intcount,MPI_Datatypedatatype,intsource,inttag,MPI_Commcomm,MPI_Status*status)OUTbuf:接收缓冲区的起始地址(可选数据类型)INcount:最多可接收的数据的个数(整型)INdatatype:接收数据的数据类型(句柄)INsource:发送数据的进程的进程标识号(整型)INtag:消息标识,与发送操作的标识相匹配(整型)INcomm:本进程和发送进程所在的通信域(句柄)OUTstatus:返回状态(状态类型)从指定的进程source接收消息,并且该消息的数据类型和消息标识和本接收进程指定的相一致,接收到的消息所包含的数据元素的个数最多不能超过count。返回状态statusMPI_Status是MPI定义的一种数据类型,使用之前需要用户为它分配空间。在MPI_Recv函数中它用来给调用者提供关于接收到的消息的相关信息。在C语言中它是一个由至少三个域组成的结构类型:typedefstruct{intMPI_SOURCE;//消息发送者的进程号intMPI_TAG;//消息标志intMPI_ERROR;//接收操作的错误代码}MPI_Status;8、计时函数doubleMPI_Wtime(void)用法:starttime=MPI_Wtime();//需计时部分endtime=MPI_Wtime();runtime=endtime-starttime;#includestdio.h#includempi.hintmain(intargc,char**argv){intrank,size;MPI_Init(&argc,&argv);MPI_Comm_size(MPI_COMM_WORLD,&size);MPI_Comm_rank(MPI_COMM_WORLD,&rank);printf(“Helloworldfromprocess%dof%d.\n,rank,size);MPI_Finalize();return0;}五、MPI程序示例执行结果:Helloworldfromprocess0of4.Helloworldfromprocess1of4.Helloworldfromprocess3of4.Helloworldfromprocess2of4.1、Helloworld-1#includempi.h#includestdio.h#includemath.hvoidmain(intargc,char**argv){intrank,size,namelen;charprocessor_name[MPI_MAX_PROCESSOR_NAME];MPI_Init(&argc,&argv);MPI_Comm_rank(MPI_COMM_WORLD,&rank);MPI_Comm_size(MPI_COMM_WORLD,&size);MPI_Get_processor_name(processor_name,&namelen);fprintf(stderr,Helloworldfromprocess%dof%don%s.\n,rank,size,processor_name)