资源描述
单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,多核程序设计,OpenMP编程简介,一种面向共享内存以及分布式共享内存的多处理器多线程并行编程语言。,一种能够被用于显示指导多线程、共享内存并行的应用程序编程接口(API)。,OpenMP具有良好的可移植性,支持多种编程语言,OpenMP能够支持多种平台,包括大多数的类UNIX系统以及Windows NT系统(Windows 2000,Windows XP,Windows Vista等)。,用C/C+语言来实现OpenMP的多线程编程。,:/openmp.org,Current spec is OpenMP 2.5,250 Pages,(combined C/C+and Fortran),2025/11/9 周日,2,OpenMP多线程编程基础,OpenMP的编程模型以线程为基础,通过编译指导语句来显示地指导并行化,为编程人员提供了对并行化的完整的控制。,OpenMP程序的执行模型采用Fork-Join的形式,Fork,创建新线程或者唤醒已有线程;Join,即多线程的汇合,Fork-Join执行模式在开始执行时,只有一个叫主线程的运行线程存在。,主线程在运行中,需要进行并行计算时,派生出线程执行并行任务。,在并行执行时,主线程和派生线程共同工作。,在并行代码结束后,派生线程退出或挂起,控制流程回到单独的主线程中。,2025/11/9 周日,3,OpenMP多线程编程基础,共享内存多线程应用程序的Fork-Join模型如图,主线程运行中,遇到并行编译指导语句,根据环境变量派生出线程,某个派生线程遇到另一个编译指导语句,又派生出另外一组线程。,新线程组在通过一个隐含的同步屏障后,汇合成原有的线程。,Master,Thread,Paralll,Region,Nested,Parallel,Region,2025/11/9 周日,4,OpenMP多线程编程基础,OpenMP同时支持C/C+语言和Fortran语言,可选择任意一种语言及支持OpenMP的编译器编写OpenMP程序。,OpenMP应用程序的三个组成部分,编译指导语句,运行时库函数,环境变量:通过环境变量的方式可以灵活控制程序的运行。,例如:通过环境变量OMP_NUM_THREADS值控制运行的线程的数目,2025/11/9 周日,5,OpenMP多线程编程基础,编译指导语句,在编译器编译程序的时候,会识别特定的注释,而这些特定的注释就包含着OpenMP程序的一些语义。具体形式如下:,#pragma omp clause,clause,所有编译指导语句都以#pragma omp开始,后面跟具体的功能指令。,其中directive部分就包含了具体的编译指导语句,包括parallel,for,parallel for,section,sections,single,master,critical,flush,ordered和atomic。可选子句clause给出了相应的编译指导语句的参数。,2025/11/9 周日,6,OpenMP多线程编程基础,运行时库函数,使用运行时函数库所包含的函数,必须在相应的源文件中包含OpenMP头文件,即#include“omp.h”,四个最常用的OpenMP库函数,int omp_get_num_threads(void),返回当前使用的线程个数。,int omp_set_num_threads(int NumThreads),在进入并行区域前,该函,数设置将要使用的线程个数。,int omp_get_thread_num(void),返回当前线程号,值在0(主线程)到线,程总数减1之间。,int omp_get_num_procs(void),返回可用的处理核(处理器)个数。支持超,线程技术的处理核或处理器将被算作两个处理核(或两个处理器)。,2025/11/9 周日,7,使用Visual Studio 2005编写OpenMP程序,当前的Visual Studio.Net 2005完全支持OpenMP 2.0标准,通过新的编译器选项/openmp来支持OpenMP程序的编译和链接。,#include“omp.h”,2025/11/9 周日,8,环境变量的设置,2025/11/9 周日,9,OpenMP程序编写步骤,生成Console项目;,配置项目,使之支持OpenMP;,编写代码,加入#include“omp.h”;,编写源程序;,配置环境变量OMP_NUM_THREADS,确定线程数目;,执行程序。,2025/11/9 周日,10,用Visual Studio 2005编写简单的OpenMP程序,#include“stdafx.h”,#include“omp.h”,int _tmain(int argc,_TCHAR*argv),printf,(,“Hello from serial.n”,),;,printf,(,“Thread number=%dn”,omp_get_thread_num,(),;/,串行执行,#pragma omp parallel,/,开始并行执行,printf(“Hello from parallel.Thread number=%dn”,omp_get_thread_num();,printf(“Hello from serial again.n”);,return 0;,Example 1:Modify the“,Hello World,”serial code to run multithreaded using OpenMP,2025/11/9 周日,11,用Visual Studio 2005编写简单的OpenMP程序,OpenMP程序使用到的环境变量OMP_NUM_THREADS设置为4,三次执行的结果,2025/11/9 周日,12,OpenMP编程技术循环并行化,在C/C+语言中,循环并行化编译指导语句的格式,#pragma omp parallel for clauseclause,for(index=first;test_expression;increment_expr),body of the loop;,或,#pragma omp parallel,#pragma omp for,for(index=first;test_expression;increment_expr),body of the loop;,使用这个编译指导语句能将for循环中的工作分配到一个线程组中,线程组中的每一个线程将完成循环中的一部分内容。,for循环语句要紧跟在,parallel for,的编译指导语句后面,编译指导语句的功能区域一直延伸到for循环语句的结束部分。,编译指导语句后面的子句(clause)用来控制编译指导语句的具体行为。,2025/11/9 周日,13,OpenMP编程技术循环并行化,循环并行化语句的限制,循环并行化的语句必须具有如下的形式,for(index=start;index=。,循环语句块应该是单入口与单出口的,不能使用break语句,也不能使用goto和return从循环中跳出,会使循环次数无法确定。但可以使用continue,其不影响循环执行的次数。,2025/11/9 周日,14,OpenMP编程技术循环并行化,简单循环并行化(实验1),例将两个向量相加,并将结果保存到第三个向量中,,for(int i=0;in;i+),zi=xi+yi;,此向量加法没有数据相关性,循环过程的计算也没有循环依赖性(即某一次循环的结果依赖于其他次循环的结果)可以使用循环并行化编译指导语句直接对循环进行并行化。,#pragma omp parallel for,for(int i=0;in;i+),zi=xi+yi;,例 for(int i=0;in;i+),zi=zi-1+xi+yi;,此循环具有循环依赖性,进行并行化时必须考虑,不能简单进行。,2025/11/9 周日,15,循环并行化编译指导语句的子句,循环并行化编译指导语句的子句,数据作用域子句,作用域用来控制某一个变量是否是在各线程之间共享或者是某一个线程所私有。,数据作用域子句用shared表示一个变量在各线程之间共享,用private表示一个变量是某个线程私有的。,在OpenMP中,默认的变量作用域是共享的。,用来控制线程调度的子句(schedule子句),动态控制是否并行化子句(if子句),进行同步的子句(ordered子句),控制变量在串行部分与并行部分传递的子句(copyin子句),2025/11/9 周日,16,循环并行化循环嵌套,循环嵌套(实验2),循环并行化编译指导语句可以加在任意一个循环之前,则对应的最近的循环语句被并行化,其它部分保持不变。实际上并行化是作用于嵌套循环中的某一个循环。,int i,j;,#pragma omp parallel for,private(j),for(i=0;i2;i+),for(j=6;j10;j+),printf(“i=%d j=%dn”,i,j);,并行化作用于外层循环,int i,j;,for(i=0;i2;i+),#pragma omp parallel for private(j),for(j=6;j10;j+),printf(“i=%d j=%dn”,i,j);,并行化作用于内层循环,在执行过程中,并行执行的效果只与有作用的循环相关,在每一个并行执行线程的内部,程序继续按照串行执行。,2025/11/9 周日,17,循环并行化数据作用域子句,确定数据共享属性的数据作用域子句有以下4个,shared用来显示定义的一个变量作用域是共享的。,private用来显示定义的一个变量作用域是私有的。,firstprivate将串行的变量值复制到同名的私有变量中,且在每一个线程开始执行的时候初始化一次。,lastprivate将并行执行中的最后一次循环的私有变量值复制到同名的串行变量中。,default语句用来改变变量的默认私有属性。,只在Fortran里面有,在C/C+语言中没有default(shared),2025/11/9 周日,18,循环并行化中,firstprivate与lastprivate子句,私有变量的初始化和终结操作,OpenMP编译指导语句使用firstprivate在循环并行化开始时用主线程中变量的值初始化各线程的同名私有变量。,使用lastprivate将循环并行化的最后一次循环的变量结果返回给主线程的同名变量。,实验3,int val=8;,#pragma omp parallel for firstprivate(val)lastprivate(val),for(int i=0;i2;i+),printf(i=%d val=%dn,i,val);,if(i=1),val=10000;,printf(i=%d val=%dn,i,val);,printf(val=%dn,val);,2025/11/9 周日,19,并行区域编程,通过循环并行化编译指导语句使得一段代码能够在多个线程内部同时执行。循环并行化是并行区域编程的特例。,并行区域编译指导语句的格式与使用限制,#,pragma omp parallel clauseclause,block,parallel编译指导语句的执行过程,#pragma omp parallel,for,(,int i=0;i5;i+,),printf,(,hello world i=%dn,i,),;,程序的执行结果:,hello world i=0,hello world i=0,hello world i=1,hello world i=1,hello world i=2,#pragma omp parallel,for,for,(,int i=0;i5;i+,),printf,(,hello world i=%dn,i,),;,程序的执行结果:,hello world i=0,hello world i=3,hello world i=1,hello world i=4,hello world i=2,hello world i=2,hello world i=3,hello world i=3,hello world i=4,hello world i=4,2025/11/9 周日,20,并行区域编程,并行区域与循环并行化的区别,并行区域采用复制执行的方式,将代码在所有的线程内部都执行一次,循环并行化则采用工作分配的执行方式,将循环所需的工作量按照一定的方式分配到各个执行线程中,所有线程执行工作的总和是原先串行执行所完成的工作量。,并行区域parallel语句的作用,当程序遇到parallel编译指导语句时,就会生成相应数目(根据环境变量)的线程,且组成一个线程组,并将代码重复地在各个线程内部执行。,2025/11/9 周日,21,并行区域编程,copyin,threadprivate子句,使用copyin子句对线程私有的全局变量进行初始化。(实验4),使用threadprivate子句用来标明某一全局变量是线程私有数据,在程序运行的过程中,不能被其他线程访问到。,int global=0;,#pragma omp threadprivate,(,global,),int _tmain,(,int argc,TCHAR*argv,),global=1000;,#pragma omp parallel,copyin,(,global,),printf,(,global=%dn,global,),;,global=omp_get_thread_num,(),;,printf,(,global=%dn,global,),;,printf,(,parallel againn,),;,#pragma omp parallel,printf,(,global=%dn,global,),;,通过,copyin,的操作,确实将线程的私有变量初始化为主线程中相应的全局变量的值。在并行区域执行完毕退出后,主线程与子线程中的相应的全局变量继续有效,并在再一次进入并行区域时,使用上一次退出时所赋的值。,2025/11/9 周日,22,在OpenMP编程规范中已经对能够在不同的线程中执行不同的任务有所支持。,#pragma omp parallel copyin(global),global=omp_get_thread_num();,循环并行化编译指导语句的子句,hello world i=1,#pragma omp parallel,hello world i=0,OpenMP多线程编程基础,在编译器编译程序的时候,会识别特定的注释,而这些特定的注释就包含着OpenMP程序的一些语义。,global=omp_get_thread_num();,#pragma omp section,使用copyin子句对线程私有的全局变量进行初始化。,int omp_set_num_threads(int NumThreads)在进入并行区域前,该函,从总体上说,循环执行的次数与串行执行的次数一致。,循环并行化中firstprivate与lastprivate子句,#pragma omp for,数设置将要使用的线程个数。,并行区域之间的工作共享-,使用循环语句分配任务,使用循环语句分配任务(实验5),#pragma omp parallel,printf(outside loop thread=%dn,omp_get_thread_num();,#pragma omp for,for(int i=0;i”,#pragma omp atomic,x=expr,或者,#pragma omp atomic,x+/or x-,-x,+x,2025/11/9 周日,26,OpenMP线程同步原子操作,当对一个数据进行原子操作保护的时候,就不能对数据进行临界区的保护,因为这是两种完全不同的保护机制,OpenMP运行时并不能在这两种保护机制之间建立配合机制,所以用户在针对同一个内存单元使用原子操作的时候需要在程序所有涉及到的部位都加入原子操作的支持。,实验6,int counter=0;,#pragma omp parallel,for(int i=0;i10000;i+),#pragma omp atomic /原子操作,counter+;,printf(counter=%dn,counter);,2025/11/9 周日,27,OpenMP的四种调度方案,为了在多个处理器核之间调节工作负载,可使用schedule(type,chunksize)子句来提供循环调度信息,使编译器和运行时库能更好地划分迭代并将迭代分布到各个线程,从而实现更好的负载平衡。,#pragma omp parallel for,schedule(type,chunksize),其中type=static,|,dynamic,|,guided,|,runtime,chunksize指每次调度的块大小,OpenMP的四种调度方案,静态调度static,动态调度dynamic,指导性自调度guided,runtime在运行时根据环境变量确定调度类型,2025/11/9 周日,28,schedule(static),静态分配约n/t个连续迭代到每个线程;,schedule(static,C),将数据块静态地轮换分配给各个任务,每个数据块包括C个连续的迭代;(实验7),schedule(dynamic),动态地将迭代逐个分配到各个线程;,schedule(dynamic,C),给各个任务动态分配任务块,每个任务块包括C个迭代;(实验8),schedule(guided,C),开始时每个任务会分配到较大的迭代块,之后任务每次请求新的迭代时会被分配到大小递减的迭代块,迭代块大小将指数地下降到C;(实验9),schedule(guided),进行指导性自调度,块最小为1;,schedule(runtime),在运行时根据环境变量OMP_SCHEDULE确定调度类型。,OpenMP的四种调度方案,2025/11/9 周日,29,谢谢,谢谢观看,
展开阅读全文