《MPI并行程序开发基本》由会员分享,可在线阅读,更多相关《MPI并行程序开发基本(44页珍藏版)》请在金锄头文库上搜索。
1、MPI分布内存并行分布内存并行程序开发程序开发 第一章第一章并行计算概述并行计算概述2 2 为什麽要采用并行计算为什麽要采用并行计算?串行程序速度提升缓慢可以加快速度更短的时间内解决相同的问题;相同的时间内解决更多更复杂的问题可以加大规模计算更大规模的问题3 3 并行计算设计的分类并行计算设计的分类 共享内存ccNUMA; SMPccNUMA; SMP 分布式内存MPP; ClusterMPP; Cluster4 4 三种计算模型三种计算模型5 5 并行化分解方法并行化分解方法任务分解多任务并发执行功能分解分解被执行的计算区域分解分解被执行的数据6 6 分布内存并行方式分布内存并行方式任务并行
2、任务并行不同参数的大量工况计算不同参数的大量工况计算不同参数的大量工况计算不同参数的大量工况计算区域分解并行区域分解并行大规模多节点分块并行计算大规模多节点分块并行计算大规模多节点分块并行计算大规模多节点分块并行计算7 7第二章第二章 MPIMPI简介简介MPI(Message Passing Interface )是1994年5月发布的一种消息传递接口。它实际上是一个消息传递函数库的标准说明,以语言独立的形式来定义这个接口库, 并提供了与C和Fortran语言的绑定.。 8 8 MPIMPI的历史的历史MPI初稿:美国并行计算中心工作会议(92年4月)MPI-1公布:第一届MPI大会(93年
3、1月);MPI标准正式发布:1994年5月;MPI-2发布:MPI论坛(97年)。9 9MPICH:最重要的MPI实现(www-/ mpich),与MPI-1规范同步发展的版本,支持部分MPI-2的特征如动态生成进程等。CHIMP:EPCC(Edinburgh Parallel Computing Center)开发。ftp:/pub/packages/chimp/release下载。LAM(Local Area Multicomputer):Ohio State University开发。/下载。 MPI的实现1010 第三章第三章 MPIMPI编程编程1111 MPI为程序员提供一个并行环
4、境库,程序员通过调用为程序员提供一个并行环境库,程序员通过调用MPIMPI的库的库程序来达到程序员所要达到的并行目的,可以只使用其中的程序来达到程序员所要达到的并行目的,可以只使用其中的6个最基本的函数就能编写一个完整的个最基本的函数就能编写一个完整的MPI程序去求解很多问题。程序去求解很多问题。这这6个基本函数,包括启动和结束个基本函数,包括启动和结束MPI环境,识别进程以及发环境,识别进程以及发送和接收消息:送和接收消息:MPI_INIT: MPI_INIT: MPI_INIT: MPI_INIT: 启动启动启动启动MPIMPIMPIMPI环境环境环境环境MPI_COMM_SIZE: MP
5、I_COMM_SIZE: MPI_COMM_SIZE: MPI_COMM_SIZE: 确定进程数确定进程数确定进程数确定进程数MPI_COMM_RANK: MPI_COMM_RANK: MPI_COMM_RANK: MPI_COMM_RANK: 确定自己的进程标识符确定自己的进程标识符确定自己的进程标识符确定自己的进程标识符MPI_SEND: MPI_SEND: MPI_SEND: MPI_SEND: 发送一条消息发送一条消息发送一条消息发送一条消息MPI_RECV: MPI_RECV: MPI_RECV: MPI_RECV: 接收一条消息接收一条消息接收一条消息接收一条消息MPI_FINAL
6、IZE: MPI_FINALIZE: MPI_FINALIZE: MPI_FINALIZE: 结束结束结束结束MPIMPIMPIMPI环境环境环境环境1212 程序程序1、 简单例子简单例子 program maininclude mpif.h character * (MPI_MAX_PROCESSOR_NAME) processor_nameinteger myid,numprocs,namelen,rc,ierr call MPI_INIT(ierr)call MPI_COMM_RANK(MPI_COMM_WORLD,myid,ierr)call MPI_COMM_SIZE(MPI_CO
7、MM_WORLD,numprocs,ierr)call MPI_GET_PROCESSOR_NAME(processor_name,namelen,ierr)write (*,10) myid,numprocs,processor_name10 FORMAT(Hello World! Process,I2, of ,I1, on ,20A)call MPI_FINALIZE(rc) end1313 程序程序1、 简单例子简单例子#include “mpi.h”main(int argc, char *argv) int numprocs,myrank,i,j,k; MPI_Status sta
8、tus; char msg20; MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank = 0) strcpy(msg,”Hello World”); MPI_Send(msg,strlen(msg) + 1,MPI_CHAR,1,99,MPI_COMM_WORLD); else if(myrank =1) MPI_Recv(msg,20,MPI_CHAR,0,99,MPI_COMM_WORLD,&status); prin
9、tf(“Receive message = %sn”,msg); MPI_Finalize();1414 MPI程序的一般结构程序的一般结构包含MPI头文件初始化MPI环境消息交换处理及计算等退出MPI环境1515 头文件头文件MPIMPI程序要求:所有包含程序要求:所有包含程序要求:所有包含程序要求:所有包含MPIMPI调用的程序文调用的程序文调用的程序文调用的程序文件头应加入:件头应加入:件头应加入:件头应加入:1616 编译,执行:编译,执行:mpicc -o hello hello.c:生成执行文件hellompirun -np n hello: 加载n个进程运行,0号进程发送,1号进
10、程接受并打印字符串1717 通信因子和组通信因子和组MPIMPI通过指定通信因子和组来对进程进行一种逻通过指定通信因子和组来对进程进行一种逻辑上的划分,通讯因子定义了进程组内或组间辑上的划分,通讯因子定义了进程组内或组间通讯的上下文(具体就是指明通讯链路的数据通讯的上下文(具体就是指明通讯链路的数据结构指针)。结构指针)。MPI_COMM_WORLDMPI_COMM_WORLD通信因子是在通信因子是在MPIMPI环境初环境初始化过程中创建始化过程中创建1818 进程号进程号(rank)在一个通信因子中,每个进程都有一个唯一的在一个通信因子中,每个进程都有一个唯一的整数标识符,称作整数标识符,称
11、作“ “进程进程ID”ID”,进程号是从进程号是从0 0开开始的连续整数。始的连续整数。if (rank =0) 第0进程运行的程序段 else if(rank = 1) 第1进程运行的程序段1919 MPI消息消息MPI消息包括信封和数据两个部分,信封指消息包括信封和数据两个部分,信封指出了发送或接收消息的对象及相关信息,而出了发送或接收消息的对象及相关信息,而数据是本消息将要传递的内容。数据是本消息将要传递的内容。数据:数据:信封:信封:2020 MPIMPI的基本函数的基本函数MPI_InitMPI_Comm_sizeMPI_Comm_rankMPI_SendMPI_RecvMPI_Fi
12、nalize2121 MPI_Init() MPI_Init()MPI的初始化例行函数,用于初始化MPI运行环境。必须调用;首先调用;调用一次。MPI_Init (*argc,*argv) 。每个进程都有一个参数表。2222 MPI_Comm_size() MPI_Comm_size()该函数返回与该组通信因子相关的进程数,显该函数返回与该组通信因子相关的进程数,显然这里的通讯因子必须是组内通讯因子。然这里的通讯因子必须是组内通讯因子。MPI_Comm_size (comm,*size) MPI_Comm_size (comm,*size) 2323 MPI_Comm_rank() MPI_C
13、omm_rank()该该函函数数返返回回该该进进程程在在指指定定通通信信因因子子中中的的进进程程号号(0 0 进进程程数数-1-1),一一个个进进程程在在不不同同通通信信因因子子中中的进程号可能不同:的进程号可能不同:MPI_Comm_rank (comm,*rank) MPI_Comm_rank (comm,*rank) 2424 MPI_Send() MPI_Send()该函数将发送缓冲区中的count个datatype数据类型的数据发送到目的进程MPI_Send(buf,count,datatype,dest,tag,comm)2525 MPI_Recv() MPI_Recv()该函数从
14、指定的进程source接收消息,并且该消息的数据类型和消息标识和本接收进程指定的数据类型和消息标识相一致,收到的消息所包含的数据元素的个数最多不能超过count.MPI_Recv(buf,count,datatype,source,comm,status)2626 MPI_Finalize() MPI_Finalize()结结束束MPIMPI执执行行环环境境。该该函函数数一一旦旦被被应应用用程程序序调调用用时时,就就不不能能调调用用MPIMPI的的其其它它例例行行函函数数(包包括括MPI_InitMPI_Init) , 用用 户户 必必 须须 保保 证证 在在 进进 程程 调调 用用MPI_F
15、inalizeMPI_Finalize之之前前把把与与完完成成进进程程有有关关的的所所有有通通信信结束。结束。MPI_Finalize ()MPI_Finalize ()2727 参数说明参数说明缓冲区(缓冲区(bufferbuffer)数据个数(数据个数(countcount)数据类型(数据类型(typetype)目的地目的地 (destdest)源源 (sourcesource)标识符(标识符(tagtag)通信因子(通信因子(commcomm)状态(状态(statusstatus)2828 缓冲区(缓冲区(bufferbuffer)指指应应用用程程序序定定义义地地用用于于发发送送或或接接
16、收收数数据据的的消消息息缓冲区。缓冲区。2929 数据个数(数据个数(countcount)指发送或接收指定数据类型的个数。指发送或接收指定数据类型的个数。数数据据类类型型的的长长度度 * * 数数据据个个数数的的值值为为用用户户实实际际传传递的消息长度。递的消息长度。3030 数据类型(数据类型(typetype)MPIMPI定定义义了了一一些些缺缺省省的的数数据据类类型型,用用户户也也可可以以根根据需要建立自己的数据类型据需要建立自己的数据类型3131续上页3232 目的地目的地 (destdest)发发送送进进程程指指定定的的接接收收该该消消息息的的目目的的进进程程,也也就就是接收进程的
17、进程号(注意组间通讯的情况)。是接收进程的进程号(注意组间通讯的情况)。3333 源源 (sourcesource)接接收收进进程程指指定定的的发发送送该该消消息息的的源源进进程程,也也就就是是发发 送送 进进 程程 的的 进进 程程 号号 。 如如 果果 该该 值值 为为MPI_ANY_SOURCEMPI_ANY_SOURCE表表示示接接收收任任意意源源进进程程发发来来的消息。的消息。3434 标识符(标识符(tagtag)由由程程序序员员指指定定地地为为标标识识一一个个消消息息的的唯唯一一非非负负整整数数值值(0-327670-32767),发发送送操操作作和和接接收收操操作作的的标标识识
18、符符一一定定要要匹匹配配,但但对对于于接接收收操操作作来来说说,如如果果tagtag指指定定为为MPI_ANY_TAGMPI_ANY_TAG则则可可与与任任何何发发送送操操作作的的tagtag相匹配。相匹配。3535 通信因子(通信因子(commcomm)包包含含源源与与目目的的进进程程的的一一组组上上下下文文相相关关的的进进程程集集合合,除除非非用用户户自自己己定定义义(创创建建)了了新新的的通通信信因因子子,否否则则一一般般使使用用系系统统预预先先定定义义的的全全局局通通信信因因子子MPI_COMM_WORLDMPI_COMM_WORLD。3636 状态(状态(statusstatus)对
19、对 于于 接接 收收 操操 作作 , 包包 含含 了了 接接 收收 消消 息息 的的 源源 进进 程程( sourcesource) 和和 消消 息息 的的 标标 识识 符符 ( tagtag) 。 在在FORTRANFORTRAN程程 序序 中中 , 这这 个个 参参 数数 是是 包包 含含MPI_STATUS_SIZEMPI_STATUS_SIZE个个整整数数的的数数组组,statusstatus(MPI_SOURCEMPI_SOURCE)、statusstatus(MPI_TAGMPI_TAG)和和statusstatus(MPI_ERRORMPI_ERROR)分分别别表表示示数数据据的
20、的进进程程标标识识、发发送送数数据据使使用用tagtag 标标识识和和接接收收操操作作返返回回的的错错误误代码。代码。相相当当于于一一种种在在接接受受方方对对消消息息的的监监测测机机制制,并并且且以以其其为为依依据据对对消消息息作作出出不不同同的的处处理理(当当用用通通配配符符接接受消息时)。受消息时)。3737 程序程序1、简单例子、简单例子#include “mpi.h”main(int argc, char *argv) int numprocs,myrank,i,j,k; MPI_Status status; char msg20; MPI_Init(&argc,&argv); MPI
21、_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank = 0) strcpy(msg,”Hello World”); MPI_Send(msg,strlen(msg) + 1,MPI_CHAR,1,99,MPI_COMM_WORLD); else if(myrank =1) MPI_Recv(msg,20,MPI_CHAR,0,99,MPI_COMM_WORLD,&status); printf(“Receive message = %sn”,msg); MPI_Finali
22、ze();3838 程序程序2、 的计算的计算 # #include “mpi.h”include “mpi.h” #include #include #include #include double f(double x);/* double f(double x);/*定义函数定义函数f(x) */f(x) */ return(4.0/(1.0+x*x); return(4.0/(1.0+x*x); int main (int argc,char * argv) int main (int argc,char * argv) int done =0,n,myid,numprocs,i; i
23、nt done =0,n,myid,numprocs,i; double mypi,pi,h,sum,x; double mypi,pi,h,sum,x; double startwtime=0.0,endwtime;double startwtime=0.0,endwtime; int namelen; int namelen; char processor_nameMPI_MAXPROCESSOR_NAME; char processor_nameMPI_MAXPROCESSOR_NAME; MPI_Status status; MPI_Status status; 01-1-113939
24、 MPI_Init(&argc,&argv); MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_size(MPI_COMM_WORLD,&numprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Comm_rank(MPI_COMM_WORLD,&myid); MPI_Get_processor_name(processor_name,&namelen); MPI_Get_processor_name(processor_name,&namelen);
25、 fprint(stdout,”Process %d of %d on % sn”,myid,numprocs, fprint(stdout,”Process %d of %d on % sn”,myid,numprocs, processor_name); processor_name); n=0; n=0; if (myid=0) if (myid=0) printf(“Please give N=”); printf(“Please give N=”); scanf(&n); scanf(&n); startwtime=MPI_Wtime(); startwtime=MPI_Wtime(
26、); for (j=1;jnumprocs;j+) for (j=1;jnumprocs;j+) MPI_Send(&n,1,MPI_INT,j,99,MPI_COMM_WORLD); MPI_Send(&n,1,MPI_INT,j,99,MPI_COMM_WORLD); 4040 else else MPI_Recv(&n,1,MPI_INT,MPI_ANY_SOURCE,99,MPI_COMM_MPI_Recv(&n,1,MPI_INT,MPI_ANY_SOURCE,99,MPI_COMM_WORLD,&status);WORLD,&status); h=1.0/(double) n; h
27、=1.0/(double) n; sum=0.0; sum=0.0; for(i=myid+1;i=n;i+=numprocs) for(i=myid+1;i=n;i+=numprocs)/* /* 每一个进程计算一部分矩形的面积,若进程总数每一个进程计算一部分矩形的面积,若进程总数numprocsnumprocs为为4 4, 将将 0-1 0-1区间划分为区间划分为100100个矩形,则各个进程分别计算矩形块个矩形,则各个进程分别计算矩形块 0 0进程进程 1 1,5 5,9 9,1313,9797 1 1进程进程 2 2,6 6,1010,1414,9898 2 2进程进程 3 3,7 7
28、,1111,1515,9999 3 3进程进程 4 4,8 8,1212,1616,100 */100 */4141 x=h*(double)i-0.5);x=h*(double)i-0.5); sum+=f(x); sum+=f(x); mypi=h*sum; /*mypi=h*sum; /*各进程并行计算得到的部分和各进程并行计算得到的部分和*/ */ /* /*将部分和累加得到所有矩形的面积,该面积和即为近似将部分和累加得到所有矩形的面积,该面积和即为近似PIPI值值*/ */ if (myid != 0) if (myid != 0) MPI_Send(&mypi,1,MPI_DOUB
29、LE,0,myid,MPI_COMM_WORLD) MPI_Send(&mypi,1,MPI_DOUBLE,0,myid,MPI_COMM_WORLD) ; else else pi=0.0; pi=0.0;pi=pi+mypi;pi=pi+mypi;4242 for (j=1;jnumprocs;j+) for (j=1;jnumprocs;j+) MPI_Recv(&mypi,1,MPI_DOUBLE,MPI_ANY_SOURCE, MPI_Recv(&mypi,1,MPI_DOUBLE,MPI_ANY_SOURCE, MPI_ANY_TAG,MPI_COMM_WORLD,&status)
30、; MPI_ANY_TAG,MPI_COMM_WORLD,&status); pi=pi+mypi; pi=pi+mypi; printf(“pi is approximately %.16f,Error is %.16fn”, printf(“pi is approximately %.16f,Error is %.16fn”, pi,fabs(pi-PI25DT); pi,fabs(pi-PI25DT); endwtime=MPI_Wtime(); endwtime=MPI_Wtime(); printf(“wall clock time=% fn”,endwtime-startwtime); printf(“wall clock time=% fn”,endwtime-startwtime); fflush(stdout); fflush(stdout); MPI_Finalize(); MPI_Finalize(); 4343 谢谢!4444