并行程序设计.ppt
2 并行程序设计,2.1 三种并行程序设计模型,1.数据并行模型(适用于SIMD并行机)代表编程工具:Fortran 90,HPF2.共享存储模型(适于共享存储多处理器)代表编程工具:Pthread,OpenMP3.消息传递模型(适于多计算机)代表编程工具:MPI,PVM,1 数据并行模型(Data Parallel),概况:数据并行即将相同的操作同时作用于不同的数据,数据并行编程模型提供给编程者一个全局的地址空间.特点:单线程;并行操作于密集的数据结构松散同步;全局命名空间隐式相互作用;隐式/半隐式数据分布,编程工具代表:Fortran90是一种流行的数据并行语言,对Fortran做修改,增加了并行性支持。支持元素的并行数据操作,将整个数组成数组的一部分,视为一个操作数。高性能FORTRAN(HPF),HPF是一个语言标准,作为Fortran90的扩展。,一个简单的HPF程序实例,PROGRAM EXAMPLE!DECLARE VARIABLE INTEGER a(1024),b(1024),c(1024)INTEGER:result=0!DECLARE DISTRIBUTE!HPF$DISTRIBUTE a(block)!HPF$DISTRIBUTE b(block)!HPF$DISTRIBUTE c(block)!ALIGN VALUES TO DISTRIBUTED VARIABLES a=1 b=2!ADD THEM c=a+b!CALCULATE THE SUM IN ALL ELEMENTS OF c result=SUM(c)!PRINT THE RESULT PRINT*,“The last element is”,c(1024)PRINT*,“The sum in all elements is”,resultEND,分布数组的赋值,分布数组间的运算,分布数组的归约,打印分布数组元素,The last element is 3The sum in all elements is 3072,The last element is 3The sum in all elements is 3072,第一步:程序声明了三个整型数组,大小为1024。第二步:通过数据分布指令DISTRIBUTE指示将数组a按块分布方式block分配到各处理器上。上图给出的就是3个分布数组在4个处理器上的分布。第三步:是分布数组的赋值。采用属主规则(每个处理器尽量只访问分配给自己的数据段)。第四步:多个分布数组之间进行运算。(a和b中相应元素相加后送给c的相应元素)。第五步:分布数组的归约是将c数组中所有元素求和,赋给顺序变量result。(根据属主规则,各处理器并行地将属于自己的元素局部求和,然后调用全局操作归约并返回,将各处理器的局部和相加,再广播给所有处理器的result变量。)最后一步:打印分布数组元素。HPF中I/O语句在非并行代码段中,在处理器0上执行。由于分布数组可以看成是分布共享的,因此具有共享属性,处理器可以访问任意一个分布数组的元素。例程中:处理器0访问了处理器3上的分布数组元素,引起了处理期间通信。,2 共享存储器模型,共享存储器编程的主要特征是共享存储器提供了创建出能够直接被所有的处理器访问而不用消息传递环境中那样用消息来传递数据的变量和数据结构。描述并发进程结构的最早例子是FORK-JOIN结构一个FORK语句产生一个新的并发进程的路程,并行进程在其结尾使用JOIN语句。当原进程和新产生的进程都达到JOIN语句后,代码的执行继续以顺序方式进行。,UNIX重量级进程,UNIX操作系统基于进程的概念设计。单处理器时处理器被多个进程分时共享;处理器的使用从一个进程切换到另一个进程。(切换过程中可能出现停滞等问题)在多处理机系统中,进程的执行可以真正地并发。UNIX系统调用fork()来创建新进程。除了进程ID外,被创建的新进程是调用进程的完全拷贝。,线程,UNIX fork创建的进程是一个“重量级”进程;它是一个完全独立的新进程,拥有自己的变量、栈和存储器分配。更高效的机制是指定并发例程共享相同的存储器空间和全局变量的机制。通过thread或lightweight process实现。,(a)进程,(b)线程,进程和线程之间的区别,编程工具代表:Pthread:POS thread 简称为pthread,Posix线程是一个POS标准线程。该标准定义内部API创建和操纵线程。POSIX线程具有良好的可移植性,Solaris、Linux、Windows等平台均可应用。Pthreads定义了一套C程序语言类型、函数与常量。以pthread.h头文件和一个线程库实现。OpenMP:用于共享内存并行系统的多线程程序设计。支持编程语言C、C+和Fortran。OpenMP提供对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可自动将程序并行化,并在必要处加入同步互斥以及通信。(若不加入pragma或编译器不支持,则程序退化为串行程序,可正常执行,但无法利用多线程加速程序执行),3消息传递编程(Message Passing),概况:在消息传递模型中,驻留在不同节点上的进程可以通过网络显式地传递消息相互通信,实现进程之间的信息交换、协调步伐、控制执行等。MPP,COW的自然模型,也可应用于共享变量多机系统,适合开发大粒度的并行性特点:多线程异步并行性分开的地址空间显式相互作用显式数据映射和负载分配常采用SPMD形式编码,编程工具代表:PVM:并行虚拟机(Parallel Virtual Machine)是一种常用的基于消息传递的并行编程环境,它把工作站网络构建成一个虚拟的并行机系统,为并行应用提供了运行平台。支持WindowsNT等非Unix平台,程序设计语言支持C、Fortran和Java。MPI:消息传递接口(Message Passing Interface)是一个基于消息传递的并行编程工具。它具有移植性好、功能强大、效率高等优点,且有多种免费、使用的实现版本。MPI是1994年5月发布的一种消息传递接口。它实际上是一个消息传递函数库的标准说明,以语言独立的形式来定义这个接口库,并提供了与C、Fortran以及JAVA语言的绑定。,共享存储和消息传递程序设计模型对比实例:计算的样本程序,计算的串行程序(C语言),#include#define N 1000000main()double local,pi=0.0,w;long i;w=1.0/N;for(i=0;iN;i+)temp=(i-0.5)*w;pi=4.0/(1.0+temp*temp)+pi;printf(“pi is%f n”,pi*w);,N次循环,每次对pi累加,计算的并行程序(共享存储模型),#include static long num_steps=100000;double step;#define NUM_THREADS 2;void main()int i;double x,pi,sumNUM_THREADS;step=1.0/(double)num_steps;omp_set_num_threads(NUM_THREADS)#pragma omp parallel double x;int id;id=omp_get_thread_num();for(i=id,sumid=0.0;i=num_steps;i=i+NUM_THREADS)x=(i-0.5)*step;sumid+=4.0/(1.0+x*x);for(i=0,pi=0.0;iNUM_THREADS;i+)pi=sumi*step;,/开始线程分配,并行执行,/头文件,嵌入C或C+,当一个线程运行到parallel指令时,它会创建一个线程组并成为该组进程的主线程。主线程线程号为0。当并行域开始时,程序代码被复制,每个线程都会执行该代码。到并行部分结束时,只有主线程越过路障继续执行。,OpenMP是基于线程的并行编程模型,一个共享存储的进程由多个线程组成。OpenMPI使用FORK-JOIN并行执行模型。,所有的OPENMP程序开始于一个单独的主线程(Master Thread)。主线程会一直串行地执行,直到遇见第一个并行域(Parallel Region)才开始执行。接下来过程如下(1)FORK:主线程创建一队并行的线程,然后并行域中的代码在不同的线程队中并行执行;(2)JOIN:当诸线程在并行域中执行完之后,它们或被同步或被中断,最后只有主线程执行。,编写OpenMP程序,开发工具已增加了对OpenMP的支持,Visual Studio 2005完全支持OpenMP编写OpenMP程序的必要步骤:(1)生成项目(2)配置项目,支持OpenMP(3)加入头文件#include“omp.h”(4)编写源程序;(5)配置环境变量,确定线程的数目(6)执行程序,计算的并行程序(消息传递模型),#include#define N 100000 main()double local=0.0,pi,w,temp=0.0;long i,taskid,numtask;w=1.0/N;MPI_ Init(&argc,&argv);MPI _Comm _rank(MPI_COMM_WORLD,&taskid);MPI _Comm _Size(MPI_COMM_WORLD,&numtask);for(i=taskid;i N;i=i+numtask)temp=(i-0.5)*w;local=4.0/(1.0+temp*temp)+local;MPI_Reduce(&local,&pi,1,MPI_Double,MPI_SUM,0,MPI_COMM_WORLD);if(taskid=0)printf(“pi is%f n”,pi*w);MPI_Finalize();/*main()*/,/头文件,嵌入C或C+等,/初始化MPI环境,/MPI进程个数和编号,/MPI归约操作,消息传递模型中各个进程只能直接访问其局部内存空间,而对其他进程的局部内存空间的访问只能通过消息传递来实现。MPI初始化操作通过程序运行命令获取派生进程的数量。然后每个进程针对分配的不同的数据段执行相同的操作。最后使用reduce归约操作将结果在第0个进程中进行求和。(归约操作属于群集通信操作的一种形式),三种计算模型特征比较,2.2 消息传递编程基础,(1)编程选择 对于消息传递计算机编程可用以下方法进行:(1)设计一种专用的编程语言。如occam语言,专为Transputer消息处理器设计(2)对于现有的一种顺序高级语言的语法/保留字加以扩展来处理消息传递。如CC 和Fortran M(3)使用现在的一种顺序高级语言,但为它配备一个能进行消息传递外部过程库。常用方法C Fortran等均可.,采用C语言,以能进行消息传递的消息传递库调用对其加以扩充,以完成进程对进程的直接消息传递。这种方法中,必须显式地说明执行哪些进程,何时在并发进程间传递消息,以及传递什么消息。这类消息传递系统必须使用两个基本方法:(1)创建分离进程以使它们能在不同的计算机上执行的方法(2)发送和接收消息的方法,2)进程创建方法 创建进程有静态和动态两种方法。在静态进程创建时,所有进程在执行前必须加以制定,系统将执行固定数据的进程。程序员通常需在进程或程序执行前用命令行显式标识它们。,在大多数应用中,进程不会全相同也不会全不同;通常有一个称为主进程(master)的控制进程,其余的进程为从进程(slave),这些进程本质上相同,融合在SPMD模型中。SPMD模式的编程是最常用的消息传递系统之一MPI中最常采用的方法。,动态进程创建最通用的模型是MPMD,需要为不同的处理器编写完全独立的不同程序。通常采用主从方法是一个处理器执行主进程,而其余进程由主进程启动。pvm中的spawn(name_of_process)函数。调用后立即启动另一个进程,此后调用进程和被调用进程两者一起向前执行。派生的进程必须是事先编译好的,且可执行的程序。,在动态进程创建方法中,可在其它进程的执行期间创建进程并启动执行它们。通常用进程创建构造或库调用创建进程,也可对进程撤销。动态进程创建比静态进程创建功能更强大,但会导致显著的开销。,(3)消息传递方法 发送和接收消息传递库的调用基本形式如下:send(parameter_list)recv(parameter_list)其中send()出现在源进程中,由它发送消息;而recv()出现在目的进程中收集已被发出的消息;括号中的参数依赖于软件。简单的调用如:send(recv(&y,source_id),我们已经了解如何从一个执行的源进程向一个特定的目的进程发送消息,这里的目的进程的发送例程中是作为一个参数给定的,而源是在接收例程中作为一个参数给定的。目的进程中的recv()将只接收recv()中以参数指明的源进程所发来的消息,对其他消息将不予接收。也可以使用1作为源地址的通配符,允许目的进程接收来自任何源进程的消息。为了提供更大的灵活性,可以附于消息的消息标记(message tag)对发送得来消息加以选择。消息标记msgtag是一个典型的由用户选定的正整数,对发送来的不同类型的消息加以区分。send(,消息传递的其他方法,对拥有消息源的进程通常要求它将相同消息发送给多个目的进程。广播(broadcast):指向所有与求解问题有关的进程发送相同的消息。汇集(gather):指一个进程从一组进程中的每一个进程收集一个值。散播(scatter):用于描述根进程中数据数组中的每个元素分别发送给各个进程。,广播操作,汇集操作,散播操作,2.3 MPI编程,2.3.1 最简单的MPI并行程序MPI(Message Passing Interface)是1994年5月发布的一种消息传递接口,即基于消息传递的并行编程工具。它具有移植性好、功能强大、效率高等优点,且有多种免费、使用的实现版本如MPICH、LAM、CHIMP等。它实际上是一个消息传递函数库的标准说明,以语言独立的形式来定义这个接口库,并提供了与C,JAVA,FORTRAN语言的绑定。MPI不是一个独立的自包含系统,而是建立在本地并行程序设计环境之上,其进程管理和I/O均由本地并行程序设计环境提供。参考文献:高性能计算之并行编程技术 MPI并行程序设计 都志辉 编著,MPI程序的一般结构,MPI程序的执行过程,Hello Parallel World-最简单的MPI程序,/*文件名 hello.c*/#include“mpi.h”#include Int main(int argc,char*argv)MPI_Init(/并行部分结束,两个节点运行结果:hello parallel world!hello parallel world!,(1)并行初始化函数:int MPI_Init(int*argc,char*argv)通过MPI_Init函数进入MPI环境并完成所有的初始化工作。argc为变量数目;argv为变量数组,两个参数来自main函数的参数,因此并行程序中main函数的参数不可缺少。必须调用;首先调用;调用一次。,MPI的函数一般都以MPI_开头,所有的MPI并行程序必须包含mpi.h头文件。,(2)并行结束函数:int MPI_Finalize(void)通过MPI_Finalize函数从MPI环境中退出,释放MPI的数据结构及操作。该语句之后仍然可以进行串行程序的运行。该函数一旦被应用程序调用时,就不能调用MPI的其它例行函数(包括MPI_Init),用户必须保证在进程调用MPI_Finalize之前把与完成进程有关的所有通信结束,所有并行程序均为这样的结构,其中main函数的参数argc和argv分别为程序输入参数个数及输入参数数组。开始和结束函数之间就是程序的并行部分,将在所有节点上获得执行。,#include int main()printf(“hello parallel world!n”);这种环境下进程没有MPI环境的支持,无法对自己进行识别和进行节点间的数据通信,因此无法完成真正的并行计算。只有开始和结束函数之间的程序才具备进行消息传递模式的并行程序设计的能力。,2.3.2 获取进程标志和机器名,并行程序设计需要协调大量的计算节点参与计算,且需要将任务分配到各个节点并实现节点间的数据和信息交换。因此各个进程需要对自己和其它进程进行识别和管理,每个进程都需要有一个唯一的ID,从而实现大量计算节点的管理和控制,有效完成并行计算任务。因此获取进程标识和机器名是MPI需要完成的基本任务,各节点根据自己的进程ID判断哪些任务需要自己完成。,(1)获取当前进程标识函数,int MPI_Comm_rank(MPI_Comm comm,int*rank)调用该函数获得当前进程在指定通信域中的进程号(0 进程数-1)。一个进程在不同通信因子中的进程号可能不同。Comm为该进程所在的通信域句柄,rank为调用该函数返回的进程在通信域中的标识号。一般对于comm参数,采用MPI_COMM_WORLD通信域。它是MPI提供的一个基本通信域(默认),在这个通信域中每个进程之间能相互通信,也可以建立自己的通信域。,(2)获取通信域包含的进程总数函数,int MPI_Comm_size(MPI_Comm comm,int*size)不同进程通过调用该函数获取指定通信域的进程个数,确定自身完成任务比例,显然这里的通讯因子必须是组内通讯因子。Comm为通信域句柄,size为函数返回的通信域comm内包含的进程总数。,(3)获取本进程的机器名函数,int MPI_Get_processor_name(char*name,int*resultlen)name为返回的机器名字符串,resultlen为返回的机器名长度。,/*例程分析:who.c*/#include mpi.h#include int main(int argc,char*argv)int myid,numprocs;int namelen;char processor_nameMPI_MAX_PROCESSOR_NAME;MPI_Init(,三个节点运行结果:Hello world!Process 0 of 3 on wang1Hello world!Process 1 of 3 on wang2Hello world!Process 2 of 3 on wang3,运行命令:mpirun np 3./who,Who.c程序说明,(1)程序启动后各节点同时执行,各节点通过MPI_Comm_rank()函数取得自己的进程标识myid。不同的进程执行MPI_Comm_rank()函数后返回的值不同。(2)通过MPI_Comm_size函数获得本通信域中的进程总数numprocs。(3)通过MPI_Get_processor_name函数获得本进程所在的机器名。各进程调用自己的打印语句将结果打印出来,MPI中变量的分布式存储方式,每个节点有自己独立的存储地址空间,因此相同的变量名它们的值是可以不同的。程序副本存在于所有节点并分别得到执行,各节点计算时的地位是平行的。,在3个节点的情况下若运行命令:mpirun np 6./who,则每个节点启动两个进程,程序输出为:Hello world!Process 0 of 6 on wang1Hello world!Process 1 of 6 on wang2Hello world!Process 2 of 6 on wang3Hello world!Process 3 of 6 on wang1Hello world!Process 4 of 6 on wang2Hello world!Process 5 of 6 on wang3(执行顺序可能不同。),#include int main()printf(“hello parallel world!n”);这种环境下进程没有MPI环境的支持,无法对自己进行识别和进行节点间的数据通信,因此无法完成真正的并行计算。只有开始和结束函数之间的程序才具备进行消息传递模式的并行程序设计的能力。,2.2.3 有消息传递功能的并行程序,节点间由于变量地址空间是相互独立的,信息无法交换。消息传递时MPI编程的核心功能。(1)消息发送函数:用于发送一个消息到目标进程。int MPI_Send(void*buf,int count,MPI_Datatype dataytpe,int dest,int tag,MPI_Comm comm)(2)消息接收函数:用于从指定进程接收一个消息 int MPI_Recv(void*buf,int count,MPI_Datatype datatyepe,int source,int tag,MPI_Comm comm,MPI_Status*status),MPI消息,一个消息好比一封信!消息的内容即信的内容,在MPI中称为消息缓冲(Message Buffer)消息缓冲由三元组标识消息的收发者即信的地址,在MPI中称为消息封装(Message Envelop)消息封装由三元组标识,datatype:消息数据类型,MPI的消息类型分为两种:预定义类型和派生数据类型(1)预定义类型:MPI支持异构计算,它指在不同计算机系统上运行程序,每台计算可能有不同生产厂商,不同操作系统。通过预定义数据类型解决异构计算中的互操作问题。MPI提供了两个附加类型:MPI_BYTE和MPI_PACKED。MPI_BYTE表示一个字节,所有的计算系统中一个字节都代表8个二进制位。MPI_PACKED预定义数据类型被用来实现传输地址空间不连续的数据项。,(2)派生数据类型:MPI引入派生数据类型来定义由数据类型不同且地址空间不连续的数据项组成的消息。派生数据类型是一种通用的类型描述方法,它是一系列二元组的集合,可以表示成:,在派生数据类型中,基类型可以是任何MPI预定义数据类型,也可以是其它的派生数据类型,即支持数据类型的嵌套定义。,MPI提供了构造函数来定义派生数据类型。,节点间自定义复杂数据结构的传输,MPI提供了自定义的数据类型,这种数据类型可以使结构体数据类型,不一定是单一的数据类型。这种自定义的数据类型可以在消息传递函数中直接说明并传输,但未说明并提交的数据类型是不能在消息传递时使用的。,MPI自定义数据结构函数说明(1),(1)获取变量相对于MPI_BOTTOM偏移地址函数:MPI_Address(void*location,MPI_Aint*address)参数说明:location为需要获取位置的变量地址,是函数的输入参数;address为获取相对于MPI_BOTTOM的偏移地址,是函数的输出函数。该函数调用后通过address参数返回变量在内存中相对于预定义地址MPI_BOTTOM偏移地址。,MPI自定义数据结构函数说明(2),(2)生成新的数据结构:int MPI_Type_struct(int count,int blocklens.MPI_Aint indices,MPI_Datatype old_types,MPI_Datatype*newtype)参数说明:count参数指定数据块的个数,即结构体中有多少个类型说明;blocklens为一个数组,分别指定每个块的数据个数,若某个数据块为数组其对应的数据块个数就为数组长度;indices为数组,分别制定每个数据块的相对偏移地址;old_types分别制定每个数据块对应的MPI已定义的数据类型;newtype为信得数据块指针。调用该函数后系统将根据函数参数的描述生成新的数据类型,函数中的参数描述信息一定要与定义的数据结构相应,否则会出现严重的数据错误。,MPI自定义数据结构函数说明(3),(3)数据类型提交函数:int MPI_Type_commit(MPI_Datatype*datatype)参数说明:datatype为新数据类型的指针。该函数调用后向系统提交由MPI_Type_struct函数生成的新数据结构,提交完成后消息传递操作中就可以使用这一新的数据结构。(4)释放新数据类型函数:int MPI_Type_free(MPI_Datatype*datatype)调用该函数将释放新数据结构的空间。,/*文件名datastru.c*/,#includempi.h#include main(int argc,char*argv)int myid,numprocs;int i;MPI_Status status;/*定义自己新的数据结构,共四个数据块。*/struct Data int count1;double data16;int count2;char data250;struct Data datastruct;MPI_Datatype MYtype;/定义新的数据结构标识 int blocklenth4;/定义数据块长度描述数组 MPI_Aint offset4;/定义数据块偏移量描述数组 MPI_Datatype MPItype4;/定义数据块MPI数据类型描述数组 MPI_Init(,blocklenth0=1;/第一个数据块长度 blocklenth1=6;/第二个数据块长度,数组程度 blocklenth2=1;/第三个数据块长度 blocklenth3=50;/第四个数据块长度,字符串长度 MPItype0=MPI_INT;/第一个数据块对应的MPI类型 MPItype1=MPI_DOUBLE;/第二个数据块对应的MPI类型 MPItype2=MPI_INT;/第三个数据块对应的MPI类型 MPItype3=MPI_CHAR;/第四个数据块对应的MPI类型 MPI_Address(/第一个数据块的存储相对偏移地址,MPI_Type_struct(4,blocklenth,offset,MPItype,/0进程采用MPI_Send函数传送新的数据结构,else if(myid=1)/1进程采用MPI_Recv函数接收新的数据结构 MPI_Recv(,Myid is 1.I have received the new datatypeint count1=6double data10=0.000000double data11=1.000000double data12=2.000000double data13=3.000000double data14=4.000000double data15=5.000000int count2=50char data2=hello MPI!This is my new datatype,消息标签 tag,当发送者连续发送两个相同类型消息给同一个接收者,如果没有消息标签,接收者将无法区分这两个消息。这段代码打算传送A的前32个字节进入X,传送B的前16个字节进入Y。但是,尽管消息B后发送,但可能先到达进程Q,就会被第一个接收函数接收在X中。使用标签可以避免这个错误。,此时保证先接收的是消息A(带有tag1)。如果消息B先到达Q,它将缓冲直到执行recv(Y,16,P,tag2),添加标签使得服务进程可以对两个不同的用户进程分别处理,提高灵活性,comm:通信域,通信域(Communicator)包括进程组(Process Group)和通信上下文(Communication Context)等内容,用于描述通信进程间的通信关系。进程组是进程的有限、有序集。有限意味着,在一个进程组中,进程的个数n是有限的,这里的n称为进程组大小(Group Size)。有序意味着,进程的编号是按0,1,n-1排列的 一个进程用它在一个通信域(组)中的编号进行标识。组的大小和进程编号可以通过调用以下的MPI函数获得:MPI_Comm_size(communicator,&group_size)MPI_Comm_rank(communicator,&my_rank),通信上下文(用于通信的系统超级标签):安全的区别不同的通信以免相互干扰。通信上下文不是显式的对象,只是作为通信域的一部分出现 进程组和通信上下文结合形成了通信域。MPI_COMM_WORLD是所有进程的集合 通信域分为组内通信域和组间通信域,分别用来实现MPI的组内通信(Intra-communication)和组间通信(Inter-communication)。,(1)管理通信域 MPI提供丰富的函数用于管理通信域,MPI中创建新通信域的例子,MPI_Comm MyWorld,SplitWorld;int my_rank,group_size,Color,Key;MPI_Init(,MPI_Comm_dup(MPI_COMM_WORLD,&MyWorld)创建了一个新的通信域MyWorld,它包含了与原通信域MPI_COMM_ WORLD相同的进程组,但具有不同的通信上下文。MPI_Comm_split(MyWorld,Color,Key,&SplitWorld)函数调用则在通信域MyWorld的基础上产生了几个分割的子通信域。原通信域MyWorld中的进程按照不同的Color值处在不同的分割通信域中,每个进程在不同分割通信域中的进程编号则由Key值来标识。,MPI_Comm_dup(MPI_COMM_WORLD,MPI_COMM_SPLIT对于通信域comm中的进程都要执行每一个进程都要指定一个color值根据color值的不同此调用。首先将具有相同color值的进程形成一个新的进程组新产生的通信域与这些进程组一一对应。而新通信域中各个进程的顺序编号是根据key的大小决定的,即key越小则该进程在新通信域中的进程编号也越小。若一个进程中的key相同则根据这两个进程在原来通信域中的顺序编号决定新的编号。一个进程可能提供color值MPI_UNDEFINED。在这种情况下newcomm返回MPI_COMM_NULL。实质上,将相同color内的所有进程中的关键字的值置为同一个值导致的结果是,在新通信域中进程的相对先后次序和原来的相同。,(2)组间通信域 它是一种特殊的通信域,该通信域包括了两个进程组,分属于两个进程组的进程之间通过组间通信域实现通信。一般把调用进程所在的进程组称为本地进程组,而把另外一个称为远程进程组。,status:消息状态,消息接收函数多了一个status参数,返回状态变量。它MPI定义的一个数据类型,至少由3个域组成的结构类型,使用之前需要用户为它分配空间。例如:MPI_SOURCE,MPI_TAG,MPI_ERROR。引用时通过status.MPI_SOURCE,status.MPI_TAG,status.MPI_ERROR得到返回状态中包含的发送数据进程,使用的tag标签或该接收操作返回的错误代码。,假设多个客户进程发送消息给服务进程请求服务,通过消息标签来标识客户进程,从而服务进程采取不同的服务。while(true)MPI_Recv(received_request,100,MPI_BYTE,MPI_Any_source,MPI_Any_tag,comm,MPI的6个基本函数,从理论上说,MPI所有的通信功能可以用它的6个基本的调用来实现:MPI_INIT:启动MPI环境 MPI_COMM_SIZE:确定进程数 MPI_COMM_RANK:确定自己的进程标识符 MPI_SEND:发送一条消息 MPI_RECV:接收一条消息 MPI_FINALIZE:结束MPI环境,例程分析:message.c,#include#include mpi.hint main(int argc,char*argv)int myid,numprocs,source;MPI_Status status;char message100;MPI_Init(,else/0进程接收来自于除0进程外其他进程的字符串数据for(source=1;source numprocs;source+)MPI_Recv(message,100,MPI_CHAR,source,99,MPI_COMM_WORLD,四个节点运行结果:I am process 0.I recv string hello world from process 1.I am process 0.I recv string hello world from process 2.I am process 0.I recv string hello world from process 3.,2.2.4 MPI编译和执行,以message.c程序为例:MPI编译(输入编译命令,生成可执行文件message):mpicc-o message message.c MPI执行(输入运行命令,加载n个进程运行)mpirun-np n message,一个MPI并行程序由若干个并发进程组成,这些进程可以相同也可以不同。MPI只支持静态进程创建,即:每个进程在执行前必须在MPI环境中登记,且它们必须一起启动。通常启动可执行的MPI程序是通过命令行来实现的。启动方法由具体实现确定。,例如:mpirun np 4 message 则加载四个进程执行message程序操作。13号进程生成“hello world”字符串,0号进程接收并输出。,四个节点运行结果:I am process 0.I recv string hello world from process 1.I am process 0.I recv string hello world from process 2.I am process 0.I recv string hello world from process 3.,2.2.5 计算与通信并行,MPI编程中数据通信所需的时间会成为系统效率的瓶颈,特别是节点个数比较多时,通信会成为影响系统性能的重要因素。消息通信函数是一种阻塞式的通信方式,通信函数只有在通信成功完成后才会返回,在数据传输期间系统处于等待状态,大大浪费了系统的计算资源。解决网络通信的问题出了采用计算向存储迁移的方法外,还可以采用计算与通信并行的方法。例如:数据传输的同时让系统继续工作。因此并行程序不但要计算并行还有计算和通信并行。MPI为实现计算和通信的并行专门提供了丰富的非阻塞消息传递函数,从而挖掘程序计算和通信的并行性。,MPI通信,(1)MPI点到点通信(2)群集通信,(1)MPI点对点通信,MPI的点对点通信(Point-to-Point Communication)同时提供了阻塞和非阻塞两种通信机制,还支持多种通信模式。不同通信模式和不同通信机制的结合,便产生了非常丰富的点对点通信函数。,阻塞和非阻塞通信的主要区别在于返回后的资源可用性。阻塞通信返回的条件:通信操作已经完成,即消息已经发送或接收调用的缓冲区可用。若是发送操作,则该缓冲区可以被其它的操作更新;若是接收操作,该缓冲区的数据已经完整,可以被正确引用。非阻塞通信返回后并不意味着通信操作的完成,MPI还提供了对非阻塞通信完成的检测,主要的有两种:MPI_Wait函数和MPI_Test函数,通信模式(Communication Mode),通信模式指的是缓冲管理,以及发送方和接收方之间的同步方式。共有下面四种通信模式标准(standard)通信模式缓冲(buffered)通信模式同步(synchronous)通信模式就绪(ready)通信模式 MPI的发送操作支持四种通信模式,它们与阻塞属性一起产生了MPI中的8种发送操作。而MPI的接收操作只有两种:阻塞接收和非阻塞接收。,标准通信模式,是否对发送的数据进行缓冲由MPI的实现来决定,而不是由用户程序来控制。发送可以是同步的或缓冲的,取决于实现过程。,缓冲通信模式,缓冲通信模式的发送不管接收操作是否已经启