版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
机群应用开发
并行编程原理及
程序设计
ParallelProgramming:
FundamentalsandImplementation戴荣dair@曙光信息产业有限公司2006.42006年4月1共享存储编程参考文献黄铠,徐志伟著,陆鑫达等译.可扩展并行计算技术,结构与编程.北京:机械工业出版社,P.33~56,P.227~237,2000.陈国良著.并行计算—结构、算法、编程.北京:高等教育出版社,1999.BarryWilkinsonandMichaelAllen.ParallelProgramming(TechniquesandApplicationsusingNetworkedWorkstationsandParallelComputers).PrenticeHall,1999.李晓梅,莫则尧等著.可扩展并行算法的设计与分析.北京:国防工业出版社,2000.张宝琳,谷同祥等著.数值并行计算原理与方法.北京:国防工业出版社,1999.都志辉著.高性能计算并行编程技术—MPI并行程序设计.北京:清华大学出版社,2001.
2006年4月2共享存储编程相关网址MPI:,
http:///mpiPthreads:http://PVM:http:///pvm/
OpemMP:http://网上搜索:2006年4月3共享存储编程共享存储编程
ProgrammingwithSharedMemory2006年4月4共享存储编程共享存储并行机模型体系结构特点:多台处理机通过互联网络共享一个统一的内存空间,通过单一内存地址来实现处理机间的协调.内存空间也可由多个存储器模块构成.每台处理机可以执行相同或不同的指令流,每台处理机可以直接访问到所有数据.处理机间通信是借助于共享主存来实现的.可扩展性差,当处理机需要同时访问共享全局变量时,产生内存竞争现象而严重影响效率,比较适合中小规模应用问题的计算和事务处理.2006年4月5共享存储编程共享存储编程标准与特点共享存储器编程标准Pthreads(线程标准)
X3H5(线程标准)OpenMP(最常用的共享存储并行编程方式,是我们讨论的重点.)共享存储器编程特点显式多线程库调用.(Pthreads).编译制导语句,OpenMP等.语言C,Fortran77,Fortran90/95,C++…2006年4月6共享存储编程并行编程标准线程库标准(ThreadLibrary)–Win32API.–POSIXthreads线程模型.–X3H5:概念性线程模型编译制导(CompilerDirectives)–OpenMP-portablesharedmemoryparallelism.2006年4月7共享存储编程为什么流行多线程编程?线程:在进程的内部执行的指令序列.相对于进程,线程开销小:创建一个线程的时间大约是建立一个新进程的1/30。如在Sun4/75工作上站上,创建一个非绑定线程约为52微秒,而fork()一次的时间为1700微秒。线程同步时间约是进程同步时间的1/3.线程与RPC相结合,发挥多处理机的处理能力;发挥多处理器的处理能力;开发程序的并发性,改善程序的结构.容易实现数据共享:由于线程共用内存地址,因此可实现数据共享例:一高性能Web服务器可为每一打开链接的浏览器分配一个线程,所有线程即可共用同一cache来访问网站的热点话题统一的标准:以前各开发商提供互不兼容的线程库,结果导致多线程程序不能很好地移值。自1995年的POSIX线程标准实施之后,极大地促进多线程编程的统一。各系统都支持Pthreads,如Linux、SUN、IBMAIX等。2006年4月8共享存储编程Pthreads线程模型POSIX1003.4a小组研究多线程编程标准.当标准完成后,大多数支持多线程的系统都支持POSIX接口.很好的改善了多线程编程的可移植性.IEEEPortableOperatingSystemInterface,POSIX,1003.1-1995标准:POSIX线程模型:pthreads.2006年4月9共享存储编程线程管理(Pthread为例)创建:pthread_create终止:pthread_exit汇合:pthread_join分离:pthread_detach线程属性初始化:pthread_attr_init唯一执行:pthread_once2006年4月10共享存储编程同步对象在共享存储多处理器并行机上,线程通过全局变量通信,对于全局变量的操作必须进行同步。pthread提供两个线程同步原语:互斥和条件变量.2006年4月11共享存储编程互斥锁函数函数
操作Mutex_init() 初始化一个互斥锁Mutext_lock()
阻塞式加锁操作Mutex_trylock() 非阻塞式加锁操作Mutex_unlock()
解锁Mutex_destroy() 解除互斥状态2006年4月12共享存储编程条件变量的函数
函数
操作pthread_cond_init() 初始化条件变量pthread_cond_wait()
阻塞直至条件为真pthread_cond_signal()
强制条件为真,解除等待条件的线程的阻塞pthread_cond_timedwait() 阻塞直到指定条件为真或timeoutpthread_cond_broadcast() 解除所有等待条件的线程的阻塞pthread_cond_destroy()
销毁条件变量2006年4月13共享存储编程HelloWorld(1)#include<pthread.h>#include"stdio.h"void*worker();main(){pthread_tthread;pthread_create(&thread,NULL,worker,NULL);pthread_join(thread,NULL);}void*worker(){
printf("HelloWorld!\n");}编译命令gcchello.c–lpthread运行结果HelloWorld!2006年4月14共享存储编程pthread_t
线程数据类型pthread_create(&thread,NULL,worker,NULL);函数pthread_create()用于创建一新的线程,新线程一旦建立便进入运行状态参数:线程指针或句柄线程属性变量,属性参数:默认为NULL.属性对象一旦建立可以用于创建多个具有共同属性的线程,线程创建后,可删除属性对象.线程要执行的函数传入该执行函数的一个参数,无则NULL.可以是任意类型线程的终止线程函数正常终止返回;自调用pthear_exit()函数;线程被取消;线程接收到中止的信号;当主进程执行exit()后,进程及其全部线程全部终止.2006年4月15共享存储编程pthread_join(pthread_twait_for,void**status);
等待直到线程结束;执行该函数的线程发生阻塞,直到由wait_for指定的线程终止;等与被等的两线程必须是同一进程内部的线程(而且不是分离线程);返回值0 成功返回ESRCH 参数wait_for指定的线程不存在或是一分离线程;EINVAL 线程参数无效;EDEADLK 等待自身结束.不能有两个线程同时等待同一个线程的结束,否则其中一个线程正常返回,另外一个返回ESRCH错误.2006年4月16共享存储编程HelloWorld(2)#include<pthread.h>#include"stdio.h"#definenumthrds5pthread_t*tid;void*worker();main(){
inti;
tid=(pthread_t*)calloc(numthrds,sizeof(pthread_t)); for(i=0;i<numthrds;i++)pthread_create(&tid[i],NULL,worker,NULL); for(i=0;i<numthrds;i++)pthread_join(tid[i],NULL);}void*worker(){
int
myid;
myid=pthread_self()-tid[0];
printf("HelloWorldfromthread%d!\n",myid);}HelloWorldfromthread0!HelloWorldfromthread1!HelloWorldfromthread2!HelloWorldfromthread3!HelloWorldfromthread4!2006年4月17共享存储编程HelloWorld(3)#include<pthread.h>#include"stdio.h"#definenumthrds5pthread_t*tid;pthread_mutex_t
mutex;intsum=0;void*worker();main(){
inti;
tid=(pthread_t*)calloc(numthrds,sizeof(pthread_t));
pthread_mutex_init(&mutex,NULL); for(i=0;i<numthrds;i++)pthread_create(&tid[i],NULL,worker,NULL); for(i=0;i<numthrds;i++)pthread_join(tid[i],NULL);
printf(“Thesumis%d\n",sum); }void*worker(){
intmyid,num;
myid=pthread_self()-tid[0];
printf("%dwasaddedtothesuminthread%d\n",myid*10,myid);
pthread_mutex_lock(&mutex); sum+=myid*10;
pthread_mutex_unlock(&mutex); return;}2006年4月18共享存储编程运行结果0wasaddedtothesuminthread010wasaddedtothesuminthread120wasaddedtothesuminthread230wasaddedtothesuminthread340wasaddedtothesuminthread4Thesumis1002006年4月19共享存储编程基于多线程编程的PI求解222006年4月20共享存储编程#include<pthread.h>#include"stdio.h"pthread_mutex_t
reduction_mutex;pthread_t*tid;doublepi,w;intn;intnum_threads;doublef(a)doublea;{return(4.0/(1.0+a*a));}void*PIworker(void*arg){
inti,myid;doublesum,mypi,x;/*setindividualidtostartat0*/
myid=pthread_self()-tid[0];/*integratefunction*/sum=0.0;for(i=myid+1;i<=n;i+=num_threads){x=w*((double)i-0.5);sum+=f(x);}
mypi=w*sum;/*reducevalue*/
pthread_mutex_lock(&reduction_mutex);pi+=mypi;
pthread_mutex_unlock(&reduction_mutex);return(0);}2006年4月21共享存储编程voidmain(argc,argv)int
argc;char*argv[];{
inti;
/*checkcommandline*/
if(argc!=3){
printf("Usage:%sNum_intervalsNum_threads\n",argv[0]);exit(0);}
/*getnumintervalsandnumthreadsfromcommandline*/n=atoi(argv[1]);num_threads=atoi(argv[2]);w=1.0/(double)n;pi=0.0;
tid=(pthread_t*)calloc(num_threads,sizeof(pthread_t));/*initilizelock*/if(pthread_mutex_init(&reduction_mutex,NULL)){
fprintf(stderr,"Cannotinitlock\n");
exit(1);}/*createthethreads*/
for(i=0;i<num_threads;i++){
if(pthread_create(&tid[i],NULL,PIworker,NULL)){
fprintf(stderr,"Cannotcreatethread%d\n",i);
exit(1);}}/*jointhreads*/
for(i=0;i<num_threads;i++)
pthread_join(tid[i],NULL);
printf("computedpi=%.16f\n",pi);}gcchello.c–lpthread
a.out10005
computedpi=3.1415927369231271转去Openmp2006年4月22共享存储编程多线程并行编程特点pthread_create()创建一个新线程比重新启动一个线程花费的时间少:需要时创建+任务结束立刻杀掉vs.维护一大堆的空闲线程并且相互切换.在加锁的前提下访问共享资源不支持数据并行,适合于任务级并行,即一个线程单独执行一个任务;不支持增量并行化,对于一个串行程序,很难用Pthreads进行并行化Pthreads主要是面向操作系统,而不是为高性能计算设计的,因此不是并行计算程序设计的主流平台。但是“多线程并发执行”这种思想却被广泛地应用于高性能计算。这就是我们即将要讲的共享存储并行编程的另外一种被并行机制造商和广用并行计算用户广泛接受的平台:OpenMP2006年4月23共享存储编程并行编程标准线程库标准(ThreadLibrary)–Win32API.–POSIXthreads线程模型.–X3H5:概念性线程模型编译制导(CompilerDirectives)–
OpenMP-portablesharedmemoryparallelism.2006年4月24共享存储编程AnIndustryStandardAPIforSharedMemoryProgrammingAnAPIforWritingMultithreadedApplications一系列编译制导语句和库函数使得Fortran,CandC++的多线程编程更加容易2006年4月25共享存储编程与X3H5的关系X3H5是ANSI/X3授权的小组委员会,主要目的是在PCF(theParallelComputingForum)工作的基础上,发展并行计算的一个ANSI标准.PCF是一非正式的工业组织,虽在DO循环的并行化方法的标准化方面做一些工作,但在起草拟了一个标准后就草草收场.OpenMP专门针对这类并行化问题,并完成了这项工作,同时得到工业界的广泛支持.2006年4月26共享存储编程ANSIX3H5共享编程标准概念性的编程模型(ANSI标准(1993))没有任何商品化的共享存储器系统依附于X3H5,但X3H5的基本概念影响以后共享存储器系统的并行编程.(一些基本概念在OpenMP均出现!)X3H5支持C,Fortran77以及Fortran90语言.X3H5规定的基本的并行结构用于并行性表述:并行块(分散任务WorkSharing)并行循环单进程parallel{…}endparallelpsections{…}endpsectionspdo{…}endpdopsingle{…}endpsingle2006年4月27共享存储编程X3H5编程实例programmain !程序以顺序模式执行A !A只由基本线程执行parallel !转换成并行模式B !B为每个组员所复制psections !并行块开始section C !一个组员执行CsectionD !另一个组员执行D endpsections !等待C和D都结束psingle
暂时转换成顺序模式E !E只能被一个组员执行endpsingle !转回并行模式pdoI=1,6 !并行do循环开始F(i) !各线程分担循环任务endpdo
nowait !无隐式路障同步G !更多的复制代码endparallel !结束并行模式H !根进程执行H… !更多的并行构造end线程PQRBBECF(1:2)F(3:4)F(5:6)GGGH隐式barrier隐式barrier隐式barrier无隐式barrier隐式barrierBD各线程以负载平衡方式分担任务可能为:F(1:1),F(2:2),F(3:6)…2006年4月28共享存储编程X3H5例程执行过程描述程序以顺序方式启动,此时只有一个初始化线程,称为基本线程或主线程.当程序遇到parallel时,通过派生多个子线程转换为并行执行模式(线程数隐式决定).基本线程与它的子线程形成一个组.所有组员并行处理后继并行代码,直至endparallel.然后程序转为顺序模式,只有基本线程继续执行.子线程遇到内部并行或任务分担构造时,可以继续派生其子线程,从而成为一个新组的基本线程.线程间同步,通信与交互隐式路障:parallel,endparallel,endpdo或endpsingle处隐式barrier.如果不需,则加nowait;各处理机通过全局变量通信,通过私有变量封装数据fork…...barrier…顺序执行顺序执行并行执行2006年4月29共享存储编程OpenMP:并行模型Fork-Join并行模式:主线程根据需要创建一组子线程进行工作分担.可对串行程序进行逐步并行化.主线程并行执行区域2006年4月30共享存储编程如何应用OpenMP?OpenMP常用于循环并行化:–找出最耗时的循环.–将循环由多线程完成.在串行程序上加上编译制导语句,完成并行化,因此可先完成串行程序,然后再进行OpenMP并行化.voidmain(){doubleRes[1000];for(inti=0;i<1000;i++){do_huge_comp(Res[i]);}}voidmain(){doubleRes[1000];
#pragmaompparallelforfor(inti=0;i<1000;i++){do_huge_comp(Res[i]);}串行程序并行程序用OpenMP将该循环通过多线程进行任务分割2006年4月31共享存储编程线程间如何交互?OpenMP
是基于共享内存模型.线程通过共享变量通信.访问共享变量会导致racecondition(竞态状态)racecondition:是一种状态,在这种状态下两个实体(例如两个处理过程)对同一资源进行竞争,而系统没有一种机制来测定首先要执行的是哪一个。因此,由于系统不能保证数据的正确处理,其结果是不可预测的。为了避免线程进入竞态状态:通过同步对象来保护数据冲突.2006年4月32共享存储编程OpenMP术语大多OpenMP构造是制导语句或pragmas.C和C++的pragmas形式为:#pragma
omp
construct[clause[clause]…]Fortran中,制导语句形式为以下几种:C$OMPCONSTRUCT[clause[clause]…]!$OMPCONSTRUCT[clause[clause]…](自由书写格式唯一)*$OMPCONSTRUCT[clause[clause]…]例:以下三种等价(第一行为列数)C23456789!$OMPPARALLELDOSHARED(A,B,C)C$OMPPARALLELDOC$OMP+SHARED(A,B,C)C$OMPPARALLELDOSHARED(A,B,C)由于OpenMP构造为注释性语句,因此一个OpenMP程序在用不支持OpenMP的编译器编译后,仍为串行程序.2006年4月33共享存储编程Structuredblocks(结构化块)结构化块性质:仅在块顶有一个入口和块底有一个出口;块功能可通过构造的语法清晰地识别;块内除Fortran中的STOP语句和c/c++中的exit()语句外,不能有其它分支.大多OpenMP构造为结构化块.C$OMP PARALLEL10 … … if(…)goto10C$OMPENDPARALLEL print*,id
C$OMPPARALLEL10 …30 … if(…)goto20 goto10C$OMP ENDPARALLEL if(…)goto3020 print*,id一个结构化块一个非结构化块2006年4月34共享存储编程OpenMP结构化块类型OpenMP主要有五类结构化块:并行区ParallelRegions任务分割Worksharing数据环境DataEnvironment同步Synchronization运行时函数/环境变量在Fortran,C/C++中,OpenMP基本上是一样的.2006年4月35共享存储编程ParallelRegions(并行区)并行区是OpenMP的基本构造,并行区内的代码由各线程同时执行.当一个线程执行“ompparallel”后,建立一组线程,该线程成为新建立的线程组的主线程.所有线程构成一线程组,各线程以线程ID区分,主线程ID为0.线程组并行执行并行区内代码.如:建立一个4线程的并行区:doubleA[1000];omp_set_num_threads(4);#pragmaompparallel{
intID=omp_thread_num();worker(ID,A);}每一线程以不同的线程ID和相同的参数A执行并行区内代码的一拷贝.ID(=0,1,2,3).2006年4月36共享存储编程并行区的Lecical/dynamicextent
以及Orphaned制所语句bar.fsubroutinewhoamiexternalomp_get_thread_numintegeriam,omp_get_thread_numiam=omp_get_thread_num()C$OMPCRITICALprint*,’Hellofrom‘,iamC$OMPENDCRITICALreturnendpoo.fC$OMPPARALLELcallwhoamiC$OMPENDPARALLELStatic/lexicalextent:在书写上直接包含在并行区内的部分.+Dynamicextent:包括并行区内直接和间接(函数调用)包含的内容,也被称为region.Orphan制导语句:落在子程序中的制导语句,方便于子程序的并行化,免去传统的inline处理2006年4月37共享存储编程并行区代码流程doubleA[1000];omp_set_num_threads(4);#pragmaompparallel{
intID=omp_thread_num();worker(ID,A);}opm_set_num_threads(4)worker(2,A)worker(1,A)worker(3,A)DoubleA[1000]worker(0,A)所有线程在此处同步(如,隐式barrier同步)每一线程执行相同代码,不同数据.所以,并行区结构也被称为SPMD结构.2006年4月38共享存储编程HelloWorld(C)#include<omp.h>main(){intmyid,numthreads;
#pragmaompparallel
{
myid=omp_get_thread_num();
numthreads=omp_get_num_threads();
printf("HelloWorldfromthread%dof%d!\n",myid,numthreads);
}}2006年4月39共享存储编程HelloWorld(Fortran)
PROGRAMHELLO
integermyid,numthreads
integeromp_get_num_threads,omp_get_thread_num!$ompparallelprivate(numthreads,myid)
numthreads=omp_get_num_threads()
myid=omp_get_thread_num()
print*,'HelloWorldfromthread',myid,'of',numthreads!$ompendparallel
stop
end2006年4月40共享存储编程OpenMP并行程序编译支持编译OpenMP的编译器会提供编译器命令选项,以解释OpenMP编译制导语句.IBMAIXxlc编译器,编译器命令选项为-qsmpxlcfile.c–qsmpxlf_rfile.f-qsmp
(xlf_r为IBMAIX4.3为支持多线程编程的编译器)曙光3000:OS:AIX4.3-qsmpAIX4.3支持OpenMP编译选项IntelC/C++编译器icc,IntelFortran编译器选项为-openmpiccfile.c–openmpifcfile.f–openmp曙光4000L:OS:RedhatLinux8.0PGIC/C++编译器icc,PGIFortran编译器选项为-mppgccfile.c–mppgf77file.f–mppgf90file.f–mp曙光4000A:OS:SuSELinux8.0/TurboLinux2006年4月41共享存储编程一些细节(可先不关心)C:#pragma
ompparallel[clause[clause]...]new-linestructured-blockFortran:!$OMPPARALLEL[clause[[,]clause]...]block!$OMPENDPARALLEL子句clause是下列之一:if(expr):根据expr表达式执行结果决定是否并行执行private(list):变量私有化,默认为全部变量firstprivate(list):在并行区间之外引用变量首次赋值结果default(shared|none)(C)DEFAULT(PRIVATE|SHARED|NONE)(Fortran)shared(list):并行区间中的共享变量列表copyin(list):拷贝主线程的threadprivate公共区数据reduction(operator:list):归约操作2006年4月42共享存储编程OpenMP结构化块类型OpenMP主要有五类结构化块:并行区ParallelRegions任务分割WorksharingDO(Fortran)/for(C)结构:针对循环的并行化结构Sections:代码段间的并行Single:强制并行区中某些代码以串行方式执行(如:I/O)数据环境DataEnvironment同步Synchronization运行时函数/环境变量OpenMP最重要部分2006年4月43共享存储编程循环分割:DO(Fortran)/for(C)结构Fortran!$OMPDO[clause[[,]clause]...]do_loop[!$OMPENDDO[NOWAIT]](可选)C/C++#pragma
ompfor[clause[clause]...]new-linefor-loopC$OMPPARALLELC$OMPDODOi=0,n…}#pragmaompparallel#pragmaompforfor(i=0;i<n;i++){…}在DO/for结构之后有一隐式barrier同步操作,用NOWAIT/nowait可以禁止.2006年4月44共享存储编程比较parrallel构造与for构造for(i=0;I<N;i++){a[i]=a[i]+b[i];}#pragmaompparallel{
intid,i,Nthrds,istart,iend;id=omp_get_thread_num();
Nthrds=omp_get_num_threads();
istart=id*N/Nthrds;
iend=(id+1)*N/Nthrds;for(i=istart;I<iend;i++){a[i]=a[i]+b[i];}}#pragmaompparallel#pragmaompforschedule(static)for(i=0;I<N;i++){a[i]=a[i]+b[i];}串行代码用并行区实现并行化用任务分割构造实现并行化对于DO结构与PARALLEL结构的比较同理,且以后讨论若无特别说明均基于C描述.2006年4月45共享存储编程并行区与任务分割间的关系并行区和任务分割是OpenMP两类基本的并行性构造;并行区中的代码对组内的各线程是可见的,也即并行区内的代码由各线程同时执行;任务分割与并行区不同,它是将一个整体任务按负载平衡的方式分配给各线程来互相配合完成.并行区是并行的先决条件,任务分割必须要与并行区一起使用才能生效;并行区构造为!ompparallel;任务分割构造有:do/for,section,和single三种.根进程并行区2006年4月46共享存储编程更详细的for语法#pragma
ompfor[clause[clause]...]new-linefor-loopClause可是下列说明:private(list)firstprivate(list)lastprivate(list)reduction(operator:list)orderedschedule(kind[,chunk_size])nowait后面将详细说明2006年4月47共享存储编程更详细的DO语法!$OMPDO[clause[[,]clause]...]do_loop[!$OMPENDDO[NOWAIT]]PRIVATE(list)FIRSTPRIVATE(list)LASTPRIVATE(list)REDUCTION({operator|intrinsic_procedure_name}:list)SCHEDULE(type[,chunk])ORDERED2006年4月48共享存储编程DO/for使用注意事项循环体必须紧接在DO或for之后.For循环必须为一结构化块,且其执行不被break语句中断.在Fortran中,如果写上ENDDO制导语句,其必须要紧跟在DO循环的结束之后.循环变量必须为整形.Schedule,
ordered,nowait子句只能出现一次.2006年4月49共享存储编程scheduleSchedule子名决定循环如何在各线程中进行分配:schedule(dynamic[,chunk])各线程每次得到chunk_size大小的任务,执行完后继续取得任务,以此反复,直至任务完成(最后一任务可能会小于chunk_size).(任务池)当chunk_size未被指定时,默认为1.schedule(static[,chunk])如果chunk_size被指定,
则各线程按线程号顺序每人每得chunk次的循环任务,如果任务不能一次平分掉,则分配循环进行.如果chunk_size未被指定,则各线程任务数即为循环数除以所用线程数的结果.schedule(guided[,chunk])开始以一大的单位进行分配忆,逐渐减小到chunk指定的值.schedule(runtime)分配方式与chunk值大小取决于环境变量OMP_SCHEDULE的设置.chunk以循环次数为单位.示意图见下页.2006年4月50共享存储编程Schedule示意图WorkpoolWork........Dynamic方式Static方式执行时间Guided方式Work........2006年4月51共享存储编程适用条件静态:适用于大部分情形.特点:各线程任务明确,在任务分配时无需同步操作.运行快的线程需等慢的线程为:动态:适用于任务数量可变或不确定的情形(如条件收敛循环).特点:各线程将要执行的任务不可预见,任务分配需同步操作.Guided:线程异步到达for结构特点:首先到达的线程总是分得q=ceiling(n/p)次循环,然后n=max(n-q,p*k),循环分配,直到n=p*k为止.环境变量:无需重新编译程序,可根据原始输入数据的情况改变任务分配策略.2006年4月52共享存储编程NOWAIT子句(Fortran)C$OMPPARALLELC$OMPDOdoi=1,na(i)=cos(a(i))
enddoC$OMPENDDOC$OMPDOdoi=1,nb(i)=a(i)+b(i)
enddoC$OMPENDDOC$OMPENDPARALLELC$OMPPARALLELC$OMPDOdoi=1,na(i)=cos(a(i))
enddoC$OMPENDDONOWAITC$OMPDOdoi=1,nb(i)=a(i)+b(i)
enddoC$OMPENDDOC$OMPENDPARALLEL隐式BARRIERNoBARRIER默认循环变量i为私有线程私有类型变量ENDDO必须紧随enddo,可省略.2006年4月53共享存储编程nowait子句(C)#pragmaompparallel{#pragma
ompforfor(i=1;i<n;i++)a(i)=cos(a(i));#pragmaompforfor(i=1;i<n;i++)b(i)=a(i)+b(i);}
{…}为并行区.隐式BARRIER#pragmaompparallel{#pragma
ompfornowaitfor(i=1;i<n;i++)a(i)=cos(a(i));#pragmaompfornowaitfor(i=1;i<n;i++)b(i)=a(i)+b(i);}
NoBARRIER2006年4月54共享存储编程Sections构造(非循环并行)Sections用于程序中大范围的非迭代执行代码段间的并行化.(如前10行和后10行间代码间无依赖关系,可以并行.)缺省时每一个“ompsections”构造结束后有一个barrier同步操作.通过使用“nowait”子句禁止隐式barrier同步(在构造语句后直接加即可).与for结构相类似,OpenMP也提供parallelsections.#pragmaomp
sections[nowait]{ x_calculation(); #pragmaomp
section y_calculation(); #pragmaomp
section z_calculation();}x_calculation(),y_calculation()以及z_calculation()代表三部分之间无依赖关系的非循环代码段.实质上它们各代表很多行代码.2006年4月55共享存储编程SingleSingle:强制并行区中某些代码以串行方式执行#pragma
ompsingle[clause[clause]...]new-linestructured-blockClauseisoneofthefollowing:private(list)firstprivate(list)nowait2006年4月56共享存储编程Worksharing语句汇总FortranDOSECTIONSSINGLEWORKSHARECforsectionssingle2006年4月57共享存储编程Binding
并行结构的联合使用#pragmaomp
parallel
forfor(I=0;I<N;I++){ NEAT_STUFF(I);}#pragmaomp
parallel#pragma
ompforfor(I=0;I<N;I++){ NEAT_STUFF(I);}=for,sections,single,master,andbarrier如果不位于并行区内或不与并行区联合使用,便不起任何作用.2006年4月58共享存储编程Fortran!$OMPPARALLELSECTIONS[clause[[,]clause]...][!$OMPSECTION]block[!$OMPSECTIONblock]...!$OMPENDPARALLELSECTIONS!$OMPPARALLELDO[clause[[,]clause]...]do_loop[!$OMPENDPARALLELDO]!$OMPPARALLELWORKSHARE[clause[[,]clause]...]block!$OMPENDPARALLELWORKSHARE2006年4月59共享存储编程C#pragmaompparallelfor[clause[clause]...]new-linefor-loop#pragmaompparallelsections[clause[clause]...]new-line{[#pragmaompsectionnew-line]structured-block[#pragmaompsectionnew-linestructured-block..]}2006年4月60共享存储编程OpenMP结构化块类型OpenMP主要有五类结构化块:并行区ParallelRegions任务分割Worksharing数据环境DataEnvironment同步Synchronization运行时函数/环境变量2006年4月61共享存储编程默认数据类型共享变量编程模型:–大部分变量默认为共享类型各线程共享全局变量–Fortran:COMMON块,SAVE变量,MODULE变量–C:Filescopevariables,static但并不是所有变量全部共享...–在并行区内调用的子程序中的栈变量是私有–在语句块中的Auto变量为私有变量.2006年4月62共享存储编程举例说明变量类型subroutineworkcommon/input/A(10)realtemp(10)integercountsavecount…………programsortcommon/input/A(10)integerindex(10)callinputC$OMPPARALLELcallwork(index)C$OMPENDPARALLELprint*,index(1)变量A,index和cound为所有线程共享.temp为线程私有变量,各线程间到不可见.2006年4月63共享存储编程变量类型改变变量类型编译制导:
threadprivate:将某些全局变量或数据区变为线程私有.通过下列类型属性子句来改变变量类型:–shared(并行区结构)–private–firstprivate–lastprivate:循环体内的私有变量可以转变为全局变量,在循环结束后访问;默认数据类型状态可以改变:–default(private|shared|none)2006年4月64共享存储编程私有类型变量变量私有化是OpenMP采用的重要的编译技术.变量私有化;私有变量初始化:CopyinFirstprivate私有变量返回:ReductionLastprivateOpenMP私有类型变量是指并行区内的只能被线程组内的某一线程访问的变量.OpenMP的私有变量包括:并行区内定义的变量;由threadprivate
制导语句指明的变量;由private,firstprivate,lastprivate,orreduction子句指定的变量;循环控制变量.2006年4月65共享存储编程Threadprivate:
线程的全局私有变量
parameter(N=1000)realA(N,N)C$OMPTHREADPRIVATE(/buf/)common/buf/lft(N),rht(N)C$OMPPARALLELcallinitcallscalecallorderC$OMPENDPARALLEL
subroutinescaleparameter(N=1000)C$OMPTHREADPRIVATE(/buf/)common/buf/lft(N),rht(N)doi=1,N
lft(i)=const*A(i,iam)enddoreturnend
subroutineorderparameter(N=1000)C$OMPTHREADPRIVATE(/buf/)common/buf/lft(N),rht(N)doi=1,NA(i,iam)=lft(index)enddoreturnendbuf
是线程内的公共块2006年4月66共享存储编程Privateprivate(list)表明list中所列变量为组内线程私有变量.–对变量不进行初始化(有构造函数除外)–私有拷贝独立存储,不与原变量一致main(){
inti=10000;#pragmaompparallelprivate(i){i=-10000;}
printf("%d\n",i);}运行:10000main(){
inti=10000;#pragmaompparallel{i=-10000;}
printf("%d\n",i);}运行:-100002006年4月67共享存储编程Private变量将在组内每一线程中进行创建,如果只有如果变量有构造函数,则调用构造函数进行初始化,否则该私有变量的值不确定.在进入并行结构中,私有变量引用的原变量值是不确定的;也不能在并行构造对引用的变量进行修改,在结构中对这些对私有变量的修改不影响退出结构后的同名变量值.私有变量应在并行性构造中进行初始化;私有变量不能为引用类型(破坏了数据的独立性);2006年4月68共享存储编程?For结构中:main(){
inti,sum=0,myid;#pragmaompparallelfor\
private(i,sum)for(i=0;i<10;i++){
myid=omp_get_thread_num();
printf("%d\n",myid);sum=sum+1;
printf("%d\n",sum);}
printf("sum=%d\n",sum);}3804398705280439870528043987062804398707080439870508043987060804398707180439870518043987061804398707sum=10变量sum未初始化.2006年4月69共享存储编程FirstprivateFirstprivate是private的特殊情况.–在主线程中初始化每一个线程的私有拷贝变量.main(){
inti,sum=0,myid;#pragmaompparallelforfirstprivate(i,sum)for(i=0;i<10;i++){ sum=sum+1;
printf("%d\n",sum);}
printf("sum=%d\n",sum);}1231123123:2006年4月70共享存储编程LastprivateLastprivate将位于迭代末的私有变量转化为全局变量.#pragmaompparallel{#pragmaompforlastprivate(i)for(i=0;i<n-1;i++)a[i]=b[i]+b[i+1];}a[i]=b[0];for(i=1;i<n-1;i++) a(i)=b(i+1)a(i)=b(0)顺序执行i=n-12006年4月71共享存储编程默认情形=default(shared)(因此不显示使用)改变默认default(private):将并行区内变量的默认类型改为私有,而不是共享省去在每一并行区结构后加private(list)default(none):
指事先指定并行区中变量的默认类型Fortran支持default(private).C/C++无default(private),只有default(shared)和default(none).Default2006年4月72共享存储编程DEFAULT例(Fortran)itotal=1000C$OMPPARALLELDEFAULT(PRIVATE)SHARED(itotal)np=omp_get_num_threads()each=itotal/np………C$OMPENDPARALLELitotal=1000C$OMPPARALLELPRIVATE(np,each)np=omp_get_num_threads()each=itotal/np………C$OMPENDPARALLEL2006年4月73共享存储编程reduction归约操作(将各线程中处理结果经某种运算后送回到根进程)
reduction(op:list).List中所列变量必须为并行区内的共享变量.支持的运算操作:OperatorInitialization+ 0* 1&& 1|| 0仅支持标量的归约操作#pragmaompparallelforprivate(i)shared(x,y,n)reduction(+:a,b)for(i=0;i<n;i++){ a=a+x[i]; b=b+y[i];}2006年4月74共享存储编程实例:PI求解通过数值积分的方法串行求解PI很简单的(具体代码见下一页).基于OpenMP将该串行程序并行化.依据使用制导语句的不同,可有若干种方法(我们只讨论基于刚讲过的并行区与for结构):仅用并行区结构将该程序改为一SPMD并行程序.用任务分割结构进行并行化.归约.2006年4月75共享存储编程串行程序#include"stdio.h"main(intargc,char*argv[]){…n=atoi(argv[1]);w=1.0/(double)n;sum=0.0;for(i=0;i<n;i++){x=w*((double)i-0.5);sum=sum+4.0/(1.0+x*x);}pi=w*sum;
printf("Computedpi=%.16f\n",pi);}2006年4月76共享存储编程仅基于Parallel块结构的并行程序#include"stdio.h"
main(int
argc,char*argv[]){ …n=atoi(argv[1]);w=1.0/(double)n;sum=0.0;
#pragma
ompparallelprivate(x)shared(w)reduction(+:sum){
myid=omp_get_thread_num();
numthreads=omp_get_num_threads();for(i=myid;i<n;i+=numthreads){x=w*((double)i-0.5);sum=sum+4.0/(1.0+x*x);}
}….
类似于多线程编程2006年4月77共享存储编程基于for结构的并行化代码#include"stdio.h"
main(int
argc,char*argv[]){ …n=atoi(argv[1]);w=1.0/(double)n;sum=0.0;
#pragma
ompforreduction(+:sum)for(i=0;i<n;i++){x=w*((double)i-0.5);sum=sum+4.0/(1.0+x*x);}pi=w*sum;
printf("Computedpi=%.16f\n",pi);}与Pthreads比较2006年4月78共享存储编程OpenMP结构化块类型OpenMP主要有五类结构化块:并行区ParallelRegions任务分割Worksharing数据环境DataEnvironment同步Synchronization运行时函数/环境变量2006年4月79共享存储编程OpenMP同步共享变量读写单一文件I/OOpenMP提共下列同步结构:–atomic–criticalsection–barrier–flush–ordered–single–master这两个结构实质上不属于同步结构,是并行区结构和任务分割结构中的内容.2006年4月80共享存储编程criticalsection同时至多有一个线程进入临界区.#pragma
ompcritical[(name)]new-linestructured-blockC$OMPPARALLELDOPRIVATE(B)C$OMP&SHARED(RES)DO100I=1,NITERS B=DOIT(I)C$OMP
CRITICAL CALLCONSUME(B,RES)C$OMPENDCRITICAL100CONTINUE2006年4月81共享存储编程atomicAtomic是临界区的特例,主要用于某些简单的语句.#pragma
ompatomicnew-lineexpression-stmt其中expression-stmt只能为下列情形:xbinop
=expr
(bino只能是+*-/&^|<<>>)x++++xx----x仅用于内存变量的更新(在下面的例子中即对X的更新)C$OMPPARALLELPRIVATE(B)B=DOIT(I)C$OMPATOMICX=X+BC$OMPENDPARALLEL2006年4月82共享存储编程下面两段代码区别是什么?#pragmaompparallelfor\shared(x,y,index,n)for(i=0;i<n;i++){
#pragmaompatomic x[index[i]]+=work1(i); y[i]+=work2(i);}#pragmaompparallelfor\shared(x,y,index,n)for(i=0;i<n;i++){
#pragmaompcritical x[index[i]]+=work1(i); y[i]+=work2(i);}原子操作临界区2006年4月83共享存储编程原子操作和临界区比较
原子性是指操作的不可再分性,OpenMP利用原子结构主要是用于防止多线程对内存的同一地址的并发写.临界区可以完成所有的原子操作.原子结构可更好被编译优化:有硬件指令可用于实现原子操作,而且系统开销也很小.原子操作与并发并不
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 保安服务管理制度
- 甘肃省酒泉市2026年高三第二次模拟考试语文试卷含解析
- 【大单元-任务制】2026统编版语文三下第三单元第4课时 10 石峰 公开课一等奖创新教学设计
- 26年居家老人生理特点指引
- 医学26年:医疗质量安全监管 查房课件
- 26年银发电动沐浴床使用课件
- 医学26年:慢性呼吸衰竭诊疗进展 查房课件
- 26年基础护理家属沟通课件
- 语文01卷(广东专用)-(全解全析)七年级下册语文期末考试
- 就业指导实施目标解析
- 东北电网调度运行规程与操作策略解析
- 《金相检验》课件-第七单元 钢的化学热处理及表面淬火的金相检验
- 互联网银行课件
- 人教版高中高二《美术》选择性必修一-为眼睛做导游(建构画面)-教学设计
- ICD-9-CM-3手术编码6.0标准版-临床版新版字典库
- (高清版)DB34∕T 5244-2025 消防物联网系统技术规范
- 中望cad培训课件
- 桥梁伸缩缝破损更换工程全流程解析
- 2025至2030中国农药乳化剂市场深度研究与重点企业发展分析报告
- 《高频局部放电检测技术》课件
- 2025年人教版小学一年级下册趣味数学竞赛试题(附参考答案)
评论
0/150
提交评论