440 likes | 640 Views
MPI 分布内存并行 程序开发. 第一章. 并行计算概述. 为什麽要采用并行计算?. 串行程序速度提升缓慢 可以加快速度——更短的时间内解决相同的问题;相同的时间内解决更多更复杂的问题 可以加大规模——计算更大规模的问题. 并行计算设计的分类. 共享内存 ccNUMA; SMP 分布式内存 MPP; Cluster. 三种计算模型. 并行化分解方法. 任务分解 多任务并发执行 功能分解 分解被执行的计算 区域分解 分解被执行的数据. 分布内存并行方式. 任务并行
E N D
第一章 并行计算概述
为什麽要采用并行计算? • 串行程序速度提升缓慢 • 可以加快速度——更短的时间内解决相同的问题;相同的时间内解决更多更复杂的问题 • 可以加大规模——计算更大规模的问题
并行计算设计的分类 • 共享内存 • ccNUMA; SMP • 分布式内存 • MPP; Cluster
并行化分解方法 • 任务分解 • 多任务并发执行 • 功能分解 • 分解被执行的计算 • 区域分解 • 分解被执行的数据
分布内存并行方式 • 任务并行 • 不同参数的大量工况计算 • 区域分解并行 • 大规模多节点分块并行计算
第二章 MPI简介 MPI(Message Passing Interface )是1994年5月发布的一种消息传递接口。它实际上是一个消息传递函数库的标准说明,以语言独立的形式来定义这个接口库, 并提供了与C和Fortran语言的绑定.。
MPI的历史 • MPI初稿:美国并行计算中心工作会议(92年4月) • MPI-1公布:第一届MPI大会(93年1月); • MPI标准正式发布:1994年5月; • MPI-2发布:MPI论坛(97年)。
MPI的实现 MPICH:最重要的MPI实现(www-unix.mcs.anl.gov/mpi/ mpich),与MPI-1规范同步发展的版本,支持部分MPI-2的特征如动态生成进程等。 CHIMP:EPCC(Edinburgh Parallel Computing Center)开发。ftp://ftp.epcc.ed.ac.uk/pub/packages/chimp/release下载。 LAM(Local Area Multicomputer):Ohio State University开发。http://www.lam-mpi.org/download/下载。
MPI为程序员提供一个并行环境库,程序员通过调用MPI的库程序来达到程序员所要达到的并行目的,可以只使用其中的6个最基本的函数就能编写一个完整的MPI程序去求解很多问题。这6个基本函数,包括启动和结束MPI环境,识别进程以及发送和接收消息:MPI为程序员提供一个并行环境库,程序员通过调用MPI的库程序来达到程序员所要达到的并行目的,可以只使用其中的6个最基本的函数就能编写一个完整的MPI程序去求解很多问题。这6个基本函数,包括启动和结束MPI环境,识别进程以及发送和接收消息: • MPI_INIT: 启动MPI环境 • MPI_COMM_SIZE: 确定进程数 • MPI_COMM_RANK: 确定自己的进程标识符 • MPI_SEND: 发送一条消息 • MPI_RECV: 接收一条消息 • MPI_FINALIZE: 结束MPI环境
程序1、 简单例子 • program main • include 'mpif.h' • character * (MPI_MAX_PROCESSOR_NAME) processor_name • integer myid,numprocs,namelen,rc,ierr • call MPI_INIT(ierr) • call MPI_COMM_RANK(MPI_COMM_WORLD,myid,ierr) • call MPI_COMM_SIZE(MPI_COMM_WORLD,numprocs,ierr) • call MPI_GET_PROCESSOR_NAME(processor_name,namelen,ierr) • write (*,10) myid,numprocs,processor_name • 10 FORMAT('Hello World! Process',I2,' of ',I1,' on ',20A) • call MPI_FINALIZE(rc) • end
程序1、 简单例子 • #include “mpi.h” • main(int argc, char **argv) • { • int numprocs,myrank,i,j,k; • MPI_Status status; • char msg[20]; • 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); • printf(“Receive message = %s\n”,msg); • } • MPI_Finalize(); • }
MPI程序的一般结构 包含MPI头文件 初始化MPI环境 消息交换处理及计算等 退出MPI环境
头文件 • MPI程序要求:所有包含MPI调用的程序文件头应加入:
编译,执行: • mpicc -o hello hello.c:生成执行文件hello • mpirun -np n hello: 加载n个进程运行,0号进程发送,1号进程接受并打印字符串
通信因子和组 • MPI通过指定通信因子和组来对进程进行一种逻辑上的划分,通讯因子定义了进程组内或组间通讯的上下文(具体就是指明通讯链路的数据结构指针)。 • MPI_COMM_WORLD通信因子是在MPI环境初始化过程中创建
进程号(rank) • 在一个通信因子中,每个进程都有一个唯一的整数标识符,称作“进程ID”,进程号是从0开始的连续整数。 if (rank ==0) { 第0进程运行的程序段 } else if(rank == 1) { 第1进程运行的程序段 }
MPI消息 • MPI消息包括信封和数据两个部分,信封指出了发送或接收消息的对象及相关信息,而数据是本消息将要传递的内容。 • 数据:<起始地址,数据个数,数据类型> • 信封:<源/目,标识,通信域>
MPI的基本函数 • MPI_Init • MPI_Comm_size • MPI_Comm_rank • MPI_Send • MPI_Recv • MPI_Finalize
MPI_Init() • MPI的初始化例行函数,用于初始化MPI运行环境。 • 必须调用;首先调用;调用一次。 • MPI_Init (*argc,*argv) 。每个进程都有一个参数表。
MPI_Comm_size() • 该函数返回与该组通信因子相关的进程数,显然这里的通讯因子必须是组内通讯因子。 • MPI_Comm_size (comm,*size)
MPI_Comm_rank() • 该函数返回该进程在指定通信因子中的进程号(0 ~ 进程数-1),一个进程在不同通信因子中的进程号可能不同: • MPI_Comm_rank (comm,*rank)
MPI_Send() • 该函数将发送缓冲区中的count个datatype数据类型的数据发送到目的进程 • MPI_Send(buf,count,datatype,dest,tag,comm)
MPI_Recv() • 该函数从指定的进程source接收消息,并且该消息的数据类型和消息标识和本接收进程指定的数据类型和消息标识相一致,收到的消息所包含的数据元素的个数最多不能超过count. • MPI_Recv(buf,count,datatype,source,comm,status)
MPI_Finalize() • 结束MPI执行环境。该函数一旦被应用程序调用时,就不能调用MPI的其它例行函数(包括MPI_Init),用户必须保证在进程调用MPI_Finalize之前把与完成进程有关的所有通信结束。 • MPI_Finalize ()
参数说明 • 缓冲区(buffer) • 数据个数(count) • 数据类型(type) • 目的地 (dest) • 源 (source) • 标识符(tag) • 通信因子(comm) • 状态(status)
缓冲区(buffer) • 指应用程序定义地用于发送或接收数据的消息缓冲区。
数据个数(count) • 指发送或接收指定数据类型的个数。 • 数据类型的长度 * 数据个数的值为用户实际传递的消息长度。
数据类型(type) • MPI定义了一些缺省的数据类型,用户也可以根据需要建立自己的数据类型
目的地 (dest) • 发送进程指定的接收该消息的目的进程,也就是接收进程的进程号(注意组间通讯的情况)。
源 (source) • 接收进程指定的发送该消息的源进程,也就是发送进程的进程号。如果该值为MPI_ANY_SOURCE表示接收任意源进程发来的消息。
标识符(tag) • 由程序员指定地为标识一个消息的唯一非负整数值(0-32767),发送操作和接收操作的标识符一定要匹配,但对于接收操作来说,如果tag指定为MPI_ANY_TAG则可与任何发送操作的tag相匹配。
通信因子(comm) • 包含源与目的进程的一组上下文相关的进程集合,除非用户自己定义(创建)了新的通信因子,否则一般使用系统预先定义的全局通信因子MPI_COMM_WORLD。
状态(status) • 对于接收操作,包含了接收消息的源进程(source)和消息的标识符(tag)。在FORTRAN程序中,这个参数是包含MPI_STATUS_SIZE个整数的数组,status(MPI_SOURCE)、status(MPI_TAG)和status(MPI_ERROR)分别表示数据的进程标识、发送数据使用tag 标识和接收操作返回的错误代码。 • 相当于一种在接受方对消息的监测机制,并且以其为依据对消息作出不同的处理(当用通配符接受消息时)。
程序1、简单例子 • #include “mpi.h” • main(int argc, char **argv) • { • int numprocs,myrank,i,j,k; • MPI_Status status; • char msg[20]; • 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); • printf(“Receive message = %s\n”,msg); • } • MPI_Finalize(); • }
1 -1 0 1 -1 程序2、 π的计算 #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]; MPI_Status status;
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(); for (j=1;j<numprocs;j++) { MPI_Send(&n,1,MPI_INT,j,99,MPI_COMM_WORLD); } }
else { MPI_Recv(&n,1,MPI_INT,MPI_ANY_SOURCE,99,MPI_COMM_WORLD,&status); } 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; /*各进程并行计算得到的部分和*/ /*将部分和累加得到所有矩形的面积,该面积和即为近似PI值*/ if (myid != 0) MPI_Send(&mypi,1,MPI_DOUBLE,0,myid,MPI_COMM_WORLD) ; else { pi=0.0; pi=pi+mypi;
for (j=1;j<numprocs;j++) { MPI_Recv(&mypi,1,MPI_DOUBLE,MPI_ANY_SOURCE, MPI_ANY_TAG,MPI_COMM_WORLD,&status); pi=pi+mypi; } 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(); }