1 / 63

MPI 分布内存并行 程序开发

MPI 分布内存并行 程序开发. 第四章 点对点通信函数. 传送机制(两种): 阻塞方式 ,它必须等到消息从本地 送出 之后才可以执行后续的语句,保证了缓冲区等资源的可再用性; 非阻塞方式 ,它不须等到消息从本地送出就可以执行后续的语句,但非阻塞调用的返回并不保证资源的可再用性。. 阻塞通信正确返回后,其后果是: - 该调用要求的通信操作已正确完成 - 该调用的缓冲区可用 消息信封要匹配 接收到的消息是最早发送的 非阻塞通信主要用于计算和通信的重叠,从而提高整个程序执行的效率。. MPI 消息传递函数参数.

naomi
Download Presentation

MPI 分布内存并行 程序开发

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. MPI分布内存并行程序开发

  2. 第四章 点对点通信函数 传送机制(两种): 阻塞方式,它必须等到消息从本地送出之后才可以执行后续的语句,保证了缓冲区等资源的可再用性; 非阻塞方式,它不须等到消息从本地送出就可以执行后续的语句,但非阻塞调用的返回并不保证资源的可再用性。

  3. 阻塞通信正确返回后,其后果是: - 该调用要求的通信操作已正确完成 - 该调用的缓冲区可用 • 消息信封要匹配 • 接收到的消息是最早发送的 • 非阻塞通信主要用于计算和通信的重叠,从而提高整个程序执行的效率。

  4. MPI消息传递函数参数 MPI点对点通信函数的参数格式一般如下所示:

  5. 请求(request) • 这个参数用于非阻塞发送和非阻塞接收操作。由于非阻塞操作返回后,数据可能继续存在缓冲中,由此需要一种机制来检测资源是否可用。根据该变量调用其它函数完成消息的实际发送和接收。在C程序中,这个参数是指向MPI_Request结构的指针。

  6. 通讯模式(4种): • 标准通信模式(MPI_SEND) • 缓存通信模式(MPI_BSEND) • 同步通信模式(MPI_SSEND) • 就绪通信模式(MPI_RSEND)

  7. 标准(standard)模式:对数据的缓冲由具体MPI实现决定,与用户程序无关;标准(standard)模式:对数据的缓冲由具体MPI实现决定,与用户程序无关; 发送操作的正确返回而不要求接收操作收到发送的数据。 S R 1

  8. 缓冲区(buffered)模式:用户定义,使用和回收缓冲区,不管接收操作是否启动,发送操作都可以执行,但是必须保证缓冲区可用。缓冲区(buffered)模式:用户定义,使用和回收缓冲区,不管接收操作是否启动,发送操作都可以执行,但是必须保证缓冲区可用。 S R 1 2 缓冲区

  9. 同步(synchronous)模式:开始不依赖于接收进程相应的操作是否启动,但必须等到接受开始启动发送才可以返回同步(synchronous)模式:开始不依赖于接收进程相应的操作是否启动,但必须等到接受开始启动发送才可以返回 1 2 3 S R

  10. 就绪(ready)模式:只有当接收操作已经启动时,才可以在发送进程启动发送操作,否则发送将出错。就绪(ready)模式:只有当接收操作已经启动时,才可以在发送进程启动发送操作,否则发送将出错。 S R 1 2

  11. 例3、死锁的发送接收序列 CALL MPI_COMM_RANK(comm,rank,ierr) IF (rank.EQ.0) THEN CALL MPI_RECV(recvbuf,count,MPI_REAL,1, tag,comm,status,ierr) CALL MPI_SEND(sendbuf,count,MPI_REAL,1, tag,comm,ierr) ELSE IF (rank.EQ.1) CALL MPI_RECV(recvbuf,count,MPI_REAL,0, tag,comm,status,ierr) CALL MPI_SEND(sendbuf,count,MPI_REAL,0, tag,comm,ierr) ENDIF

  12. 进程 0 进程1 从进程1接收消息A 从进程0接收消息B 向进程1发送消息C 向进程0发送消息D A D C B

  13. 例4、不安全的发送接收序列 CALL MPI_COMM_RANK(comm,rank,ierr) IF (rank.EQ.0) THEN CALL MPI_SEND(sendbuf,count,MPI_REAL,1, tag,comm,ierr) CALL MPI_RECV(recvbuf,count,MPI_REAL,1, tag,comm,status,ierr) ELSE IF (rank.EQ.1) CALL MPI_SEND(sendbuf,count,MPI_REAL,0, tag,comm,ierr) CALL MPI_RECV(recvbuf,count,MPI_REAL,0, tag,comm,status,ierr) ENDIF

  14. 进程 0 进程1 从进程1发送消息A 从进程0发送消息B 向进程1接收消息C 向进程0接收消息D A D C 系统缓冲区 B

  15. 程序5、安全的发送接收序列 CALL MPI_COMM_RANK(comm,rank,ierr) IF (rank.EQ.0) THEN CALL MPI_SEND(sendbuf,count,MPI_REAL,1, tag,comm,ierr) CALL MPI_RECV(recvbuf,count,MPI_REAL,1, tag,comm,status,ierr) ELSE IF (rank.EQ.1) CALL MPI_RECV(recvbuf,count,MPI_REAL,0, tag,comm,status,ierr) CALL MPI_SEND(sendbuf,count,MPI_REAL,0, tag,comm,ierr) ENDIF

  16. 进程 0 进程1 从进程1发送消息A 从进程0接收消息B 向进程1接收消息C 向进程0发送消息D A D C B

  17. 例子6 • clock=(myrank+1)%groupsize; • anticlock=(myrank+groupsize-1)%groupsize; • MPI_Send(buf1,LENGTH,MPI_CHAR,clock,tag,MPI_COMM_WORLD); • MPI_Recv(buf2,LENGTH,MPI_CHAR,anticlock,tag,MPI_COMM_WORLD,&status); 0 1 2

  18. 改进: • MPI_Isend(buf1,LENGTH,MPI_CHAR,clock,tag,MPI_COMM_WORLD,&request); • MPI_Recv(buf2,LENGTH,MPI_CHAR,anticlock,tag,MPI_COMM_WORLD,&status); • MPI_Wait(&request,&status); • --------------------------------- • MPI_Irecv(buf2,LENGTH,MPI_CHAR,anticlock,tag,MPI_COMM_WORLD,&request); • MPI_Send(buf2,LENGTH,MPI_CHAR,clock,tag,MPI_COMM_WORLD); • MPI_Wait(&request,&status);

  19. 第五章 集合通信函数 • 集合通信是包含在通信因子中的所有进程都 参加操作。 • 集合通信一般实现三个功能 通信:组内数据的传输 同步:组内所有进程在特定的地点在执行 进度上取得一致 计算:对给定的数据完成一定的操作

  20. 集合操作的三种类型: • 同步(barrier):集合中所有进程都到达后,每个进程再接着运行; • 数据传递:广播(broadcast)、分散(scatter)、收集(gather)、全部到全部(alltoall); • 规约(reduction):集合中的其中一个进程收集所有进程的数据并计算(如:求最大值、求最小值、加、乘等);

  21. 集合通信函数 • MPI_Barrier • MPI_Bcast • MPI_Scatter • MPI_Gather • MPI_Scan • MPI_Reduce

  22. MPI_Barrier() • 在组中建立一个同步栅栏。当每个进程都到达MPI_Barrier调用后,程序才接着往下执行: • MPI_Barrier (comm)

  23. 程序7、同步示例 #include “mpi.h” #include “test.h” #include <stdlib.h> #include <stdio.h> int main(int argc,char * * argv) { int rank,size,I; int *table; int errors=0; MPI_Aint address; MPI_Datatype type,newtype; int lens; MPI_Init( &argc,&argv); MPI_Comm_rank (MPI_COMM_WORLD,&rank); MPI_Comm_size (MPI_COMM_WORLD,&size);

  24. /*Make data table */ table =(int *)calloc(size,sizeof(int)); table[rank]=rank+1; /*准备要广播的数据*/ MPI_Barrier (MPI_COMM_WORLD); /*将数据广播出去*/ for (i=0;i<size,i++) MPI_Bcast( &table[i],1,MPI_INT,i,MPI_COMM_WORLD); /*检查接收到的数据的正确性*/ for (i=0;i<size,i++) if (table[i]!=i+1) errors++; MPI_Barrier(MPI_COMM_WORLD); /*检查完毕后执行一次同步*/ …… /*其他的计算*/ MPI_Finalize(); }

  25. MPI_Bcast() • 从指定的一个根进程中把数据广播发送给组中的所有其它进程: • MPI_Bcast (*buffer,count,datatype,root,comm) • 对于root进程:buffer既是接收缓冲又是发送缓冲;对于其他进程:buffer就是接收缓冲。

  26. 程序8、广播程序示例 #include <stdio.h> #include “mpi.h” int main (argc,argv) int argc; Char * * argv; { int rank,value; MPI_Init(&argc,&argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank);

  27. do{ if (rank==0) /*进程0读入需要广播的数据*/ scanf(“%d”,&value); /*将该数据广播出去*/ MPI_Bcast(&value,1,MPI_INT,0,MPI_COMM_WORLD); /*各进程打印收到的数据*/ printf(“Process %d got %d \n”,rank,value); }while(value>=0); MPI_Finalize(); return 0; }

  28. MPI_Scatter() • 把根进程中的数据分散发送给组中的所有进程(包括自己): • MPI_Scatter (*sendbuf,sendcnt,sendtype, *recvbuf, recvcnt,recvtype,root,comm) root用MPI_Send(sendbuf, sendcount·n, sendtype, …)发送一个消息。这个消息分成n个相等的段,第i个段发送到进程组的第i个进程,sendcnt必须要和recvcnt相同。

  29. MPI_Gather() • 在组中指定一个进程收集组中所有进程发送来的消息,这个函数操作与MPI_Scatter函数操作相反: • MPI_Gather (*sendbuf,sendcnt,sendtype, *recvbuf, ecvcount,recvtype,root,comm)

  30. MPI_Reduce() • 在组内所有的进程中,执行一个规约操作,并把结果存放在指定的一个进程中: • MPI_Reduce (*sendbuf,*recvbuf,count,datatype, op,root, comm) • MPI缺省定义了如下的规约操作,用户可根据自己的需要用MPI_Op_create函数创建新的规约操作:

  31. 程序9、规约示例 #include “mpi.h” #include <stdio.h> #include <math.h> double f(double x);/*定义函数f(x) */ { return(4.0/(1.0+x*x)); } int main (int argc,char * argv[]) { int done =0,n,myid,numprocs,i; double PI25DT=3.141592653589793238462643; double mypi,pi,h,sum,x; double startwtime=0.0,endwtime; int namelen; char processor_name[MPI_MAXPROCESSOR_NAME];

  32. MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Get_processor_name(processor_name,&namelen); fprint(stdout,”Process %d of %d on % s\n”,myid,numprocs, processor_name); n=0; if (myid==0) { printf(“Please give N=”); scanf(&n); startwtime=MPI_Wtime(); } /*将n值广播出去*/ MPI_Bcast(&n,1,MPI_INT,0,MPI_COMM_WORLD);

  33. h=1.0/(double) n; sum=0.0; for(i=myid+1;i<=n;i+=numprocs) /* 每一个进程计算一部分矩形的面积,若进程总数numprocs为4, 将 0-1区间划分为100个矩形,则各个进程分别计算矩形块 0进程 1,5,9,13,……,97 1进程 2,6,10,14,……,98 2进程 3,7,11,15,……,99 3进程 4,8,12,16,……,100 */ { x=h*((double)i-0.5); sum+=f(x); } mypi=h*sum; /*各进程并行计算得到的部分和*/

  34. /*将部分和累加得到所有矩形的面积,该面积和即为近似PI值*/ MPI_Reduce(&mypi,&pi,1,MPI_DOUBLE,MPI_SUM,0, MPI_COMM_WORLD); if(myid==0) { printf(“pi is approximately %.16f,Error is %.16f\n”, pi,fabs(pi-PI25DT)); endwtime=MPI_Wtime(); printf(“wall clock time=% f\n”,endwtime-startwtime); fflush(stdout); } MPI_Finalize(); }

  35. MPI_Scan() • 用来对分布在进程组上的数据执行前缀归约: • MPI_Scan (*sendbuf,*recvbuf,count, datatype,op,comm)

  36. 进程数据缓冲区的变化情况

  37. 群集函数的特点: • 通讯因子中所有进程都要调用 • 除了MPI_Barrier(),其他函数使用类似标准阻塞的通信模式。一个进程一旦结束了它所参与的群集操作就从群集例程中返回,并不保证其他进程执行该群集例程已经完成。 • 一个群集例程是不是同步操作取决于实现。

  38. MPI并行程序的两种基本模式 • 对等模式的MPI程序设计 • 主从模式的MPI程序设计

  39. 一.对等模式的MPI程序设计 1.问题描述——Jacobi迭代 Jacobi迭代是一种比较常见的迭代方法,其核心部分可以用程序1来表示。简单的说,Jacobi迭代得到的新值是原来旧值点相邻数值点的平均。 Jacobi迭代的局部性很好,可以取得很高的并行性。将参加迭代的数据按块分割后,各块之间除了相邻的元素需要通信外,在各块的内部可以完全独立的并行计算。

  40. 程序10 串行表示的Jacobi迭代 …… REAL A(N+1,N+1),B(N+1,N+1) …… DO K=1,STEP DO J=1,N DO I=1,N B(I,J)=0.25*(A(I-1,J)+A(I+1,J)+A(I,J+1)+A(I,J-1)) END DO END DO DO J=1,N DO I=1,N A(I,J)=B(I,J) END DO END DO END DO

  41. 2.用MPI程序实现Jacobi迭代 为了并行求解,这里将参加迭代的数据按列进行分割,假设有4个进程同时并行计算,数据的分割结果如图:

  42. 假设需要迭代的数据是M*M的二维数组A(M,M),令M=4*N,按上 图进行数据划分,则分布在4个不同进程上的数据分别是: 进程0:A(M,1:N); 进程1:A(M,N+1:2*N); 进程2:A(M,2*N+1:3*N); 进程3:A(M,3*N+1:4*N). 由于在迭代过程中,边界点新值的计算需要相邻边界其他块的 数据,因此在每一个数据块的两侧各增加1列的数据空间,用于 存放从相邻数据块通信得到的数据。每个数据块的大小就从M*N 扩大到M*(N+2)。 计算和通信过程是这样的:首先对数组赋初值,边界赋为8, 内部为0。然后开始迭代,迭代之前,每个进程都需要从相邻的 进程得到数据块,同时也向相邻的进程提供数据块(FORTRAN数 组在内存中是按列优先排列的)。

  43. 发送 接收 发送 接收 发送 接收 接收 发送 接收 发送 接收 发送 进程3 进程0 进程1 进程2

  44. 程序11、 并行的Jacobi迭代 program main include ‘mpif.h’ integer totalsize,mysize,steps Parameter (totalsize=16) (定义全局数组的规模) parameter (mysize=totalsize/4,steps=10) integer n,myid,numprocs,i,j,rc Real a(totalsize,mysize+2),b(totalsize,mysize+2) Integer begin_col,end_col,ierr Integer status(MPI_STATUS_SIZE)

  45. call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD,myid,ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD,numprocs,ierr) print *,”Process”,myid,” of”,numprocs,” is alive” (数组初始化) do j=1,mysize+2 do i=1,totalsize a(i,j)=0.0 end do end do If (myid.eq.0) then do i=1,totalsize a(i,2)=8.0 end do end if

More Related