四章OpenMP多线程编程.ppt

上传人:本田雅阁 文档编号:3193151 上传时间:2019-07-28 格式:PPT 页数:85 大小:1.63MB
返回 下载 相关 举报
四章OpenMP多线程编程.ppt_第1页
第1页 / 共85页
四章OpenMP多线程编程.ppt_第2页
第2页 / 共85页
四章OpenMP多线程编程.ppt_第3页
第3页 / 共85页
四章OpenMP多线程编程.ppt_第4页
第4页 / 共85页
四章OpenMP多线程编程.ppt_第5页
第5页 / 共85页
点击查看更多>>
资源描述

《四章OpenMP多线程编程.ppt》由会员分享,可在线阅读,更多相关《四章OpenMP多线程编程.ppt(85页珍藏版)》请在三一文库上搜索。

1、第四章 OpenMP多线程编程 主要内容 OpenMP编程简介 OpenMP多线程应用程序编程技术 1. OpenMP编程简介 1.1 OpenMP多线程编程发展概况 OpenMP是一种面向共享内存多线程 并行编程技术 OpenMP具有良好的可移植性 支持多种编程语言 Fortran C/C+ 支持多种平台 www.openmp.org OpenMP最初是为共享内存的多 处理器系统设计的并行编程方法, 这与通过消息传递进行并行编程模 型有很大的不同。 OpenMP的支持环境 Intel的C+和Fortran编译器 Microsoft的Visual Studio 2005 gcc4.2以上版本

2、1.2 OpenMP多线程编程基础 OpenMP的编程模型以线程为基础,通过编 译制导语句来显示地指导并行化 OpenMP的执行模型采用Fork-Join的形式 ,在开始时,只有一个叫做主线程的运行线 程存在;在运行过程中,当遇到需要进行并 行计算的时候,派生出(Fork)线程来执行 并行任务;在并行代码结束执行,派生线程 退出或挂起,控制流程回到单独的主线程中 (Join) Fork-Join模型 并行区域 OpenMP的实现 编译制导语句(精髓) 运行时库函数 环境变量 编译指 导语句 运行时函 数库 环境变 量 编译制导语句 在编译器编译程序的时候,会识别特定的注 释,而这些注释就包含着

3、OpenMP程序的一 些语义 在一个无法识别OpenMP语意的普通编译器 中,这些注释会被当作普通的注释而被忽略 在C/C+程序中,OpenMP所有编译制导 语句以#pragma omp开始,后面跟具体功 能指令 编译制导语句 #pragma omp directive-nameclause, .newline 制导指令前缀 。对所有的 OpenMP语句 都需要这样的 前缀。 OpenMP制导 指令。在制导 指令前缀和子 句之间必须有 一个正确的 OpenMP制导 指令. 子句。在没有 其它约束条件 下,子句可以 无序,也可以 任意的选择。 这一部分也可 以没有。 换行符。表明 这条制导语句

4、的终止。 编译制导语句 Directive parallel, for, parallel for, section, sections, single, master, critical, flush, ordered, atomic 运行时库函数 OpenMP运行时函数库主要用以设置 和获取执行环境相关的信息,它们当中 也包含一系列用以同步的API 运行时函数库 “omp.h” omp_get_thread_num() 返回当前 线程的号码 通过编译制导语句,可以将串行的程序逐步地 改造成一个并行程序,达到增量更新程序的目 的,减少程序编写人员一定的负担. 串行程序和并行程序保持在同一个源

5、代码文 件当中,减少了维护负担. 编译制导语句,优势体现在编译阶段 运行时库函数,支持运行时对并行环境的改变 和优化 1.3 编写OpenMP程序的准备 工作 当前的Visual Studio .Net 2005完 全支持OpenMP 2.0标准 通过新的编译器选项 /openmp来支持 OpenMP程序的编译和链接 建立一个新的项目 配置项目属性 设置环境变量 在OpenMP中,主要通过对循 环或一段结构化代码定义并行区 域的方式来实现多线程并行。 #include “omp.h“ int _tmain(int argc, _TCHAR* argv) printf(“Hello from s

6、erial.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“); getchar(); return 0; 2. OpenMP多线程应用程序 编程技术 2.1 循环并行化 循环并行化是使用OpenMP来并行化程序的最 重要的部分 在C/C+语言中,循环并行化语句的编译制导 语句格式如下:

7、 #pragma omp parallel for clauseclause for( i = first ; ilast ; i+) body of the loop; 另一种格式: #pragma omp parallel clauseclause #pragma omp for clauseclause for( i = first ; ilast ; i+) body of the loop; 如果并行的线程需要在循环的开始,或结束时作些工作 的话,就只能用parallel与for子句分离的版本。 Parallel 将紧跟的程序块扩展为若干完全等同的并行 区域,每个线程拥有完全相同的并

8、行区域; For 将循环中工作分配到线程组中,线程组中的每一个 线程完成循环中的一部分。 子句用来控制编译制导语句的具体行为。 循环并行化语句的限制 并行化的语句必须是for循环语句并具有规范格式 能够推测出循环的次数 for (index = start ; index end ; increment_expr) 在循环过程中不能使用break语句 不能使用goto和return语句从循环中跳出 可以使用continue语句 简单循环并行化 各个分量之间没有数据相关性 循环计算的过程也没有循环依赖型 循环依赖性 循环迭代相关 循环迭代相关 循环分块技术创建无循环迭代相关的循环m 循环并行化编

9、译制导语句的子句 循环并行化子句可以包含一个或者多个子句 来控制循环并行化的实际执行 常见子句有: 作用域子句(变量是共享的share还是私有的 private) 控制线程的调度(schedule )子句 动态控制是否并行化(if )子句 进行同步的子句(ordered )子句 控制变量在串行部分与并行部分传递(copyin )子句 循环嵌套 可以将嵌套循环的任意一个循环体进行 并行化 循环并行化编译指导语句可以加在任意 一个循环之前,则对应的最近的循环语 句被并行化,其它部分保持不变 控制数据的共享属性 OpenMP程序在同一个共享内存空间 上执行 可以任意使用这个共享内存空间上的变 量进行

10、线程间的数据传递 OpenMP还允许线程保留自己的私有 变量不能让其它线程访问到 分配在栈上的数据都是 私有的 全局变量及代码是共享 的 动态分配的堆空间是共 享的 threadprivate指明 某数据结构是私有的全 局变量 控制数据的共享属性 shared用来指示一个变量的作用域是共享 的。 privare用来指示一个变量的作用域是私有 的。 firstprivate对私有变量进行初始化,把串 行变量值拷贝到私有变量中(线程开始) lastprivate对私有变量最后终结的操作, 把私有变量拷贝到同名串行变量中 使用作用域子句的一些规则 作用域子句中的变量是已经声明的有名变量 作用域子句在

11、作用到类或者结构的时候,只能作用到类 或者结构的整体,而不能只作用域类或者结构的一个部 分 一个编译指导语句能够包含多个数据作用域子句 作用域子句只能出现在编译制导语句起作用的语句变量 部分 默认情况下,并行区域中的所有变量是共享的,三种例 外: parallel for 循环中,循环变量是私有的 并行区域中的局部变量是私有的 Private,firstprivate,lastprivate, reduction子句列出的变量是私有的 规约操作的并行化 在规约操作中,会反复将一个二元运算符应 用在一个变量和另外一个值上,并把结果保 存在原变量中 在使用规约操作时,只需在变量前指明规约 操作的类

12、型以及规约的变量 # pragma omp parallel for private(arx,ary,n ) reduction(+:a,b) for(i=0;in;i+) a=a+arxi; b=b+aryi; 规约操作并行化的限制 能够在OpenMP的C/C+语言中出现的规约操 作 运算符数据类类型默认认初始值值 +整数,浮点0 *整数,浮点1 -整数,浮点0 myid=omp_get_thread_num(); get_my_work_done(myid,nthreads); 使用循环语句分配任务 工作分区编码 2.3 线程同步 OpenMP支持两种不同类型的线程同步 机制 : 互斥锁:

13、可以用来保护一块共享的存储 空间,使得每一次访问这块共享内存空 间的线程最多一个,保证了数据的完整 性 事件通知:这种机制保证了多个线程之 间的执行顺序 数据竞争 互斥锁机制 用来对一块内存进行保护 OpenMP提供了三种不同的互斥锁机 制 : 临界区(critical) 原子操作(atomic) 由库函数来提供同步操作(互斥函数) 临界区(critical) 临界区通过编译指导语句对产生数据竞争的 内存变量进行保护 在程序需要访问可能产生竞争的内存数据的 时候,都需要插入相应的临界区代码 #pragma omp critical (name) block 在执行上述的程序块block之前,必

14、须首先 要获得临界区的控制权 正整数组最大的元素 原子操作 现代体系结构的多处理计算机提供了原子更 新一个单一内存单元的方法,提供了一种更 高效率的互斥锁机制。 通过编译制导可以调用上述方法 #pragma omp atomic x + 运行时库函数的互斥锁支持 函数名称描述 void omp_init_lock( omp_lock_t *) 初始化一个互斥锁锁 void omp_destroy_lock( omp_lock_t*) 结结束一个互斥锁锁的使用并释释 放内存 void omp_set_lock( omp_lock_t *) 获获得一个互斥锁锁 void omp_unset_loc

15、k( omp_lock_t *) 释释放一个互斥锁锁 int omp_test_lock( omp_lock_t *) 试图获试图获 得一个互斥锁锁,并在 成功是返回真(true),失 败败是返回假(false) 事件同步机制 锁:维护一块代码或者一块内存的一致性; 事件:用来控制代码的执行顺序,使得某一 部分代码必须在其它的代码执行完毕之后才 能执行。 OpenMP中的事件同步主要包括: 同步屏障(barrier) 定序区段(ordered sections) 主线程执行(master) 隐含的同步屏障(barrier) 在每一个并行区域都会有一个隐含的同步屏障 一个同步屏障要求所有的线程执

16、行到此屏障, 然后才能够继续执行下面的代码 #pragma omp for,#pragma omp single, #pragma omp sections程序块 都包含自己的隐含的同步屏障 为了避免在循环过程中不必要的同步屏障,可 以增加nowait子句到相应的编译指导语句中 明确的同步屏障语句 在有些情况下,隐含的同步屏障并不能提供 有效的同步措施 程序员可以在需要的地方插入明确的同步屏 障语句#pragma omp barrier 在并行区域的执行过程中,所有的执行线程 都会在同步屏障语句上进行同步 #pragma omp parallel initialization(); #prag

17、ma omp barrier process(); 循环并行化中的顺序语句( ordered) 对于循环并行化中的某些处理需要规定 执行的顺序 典型的情况:在一次循环的过程中 一大部分的工作是可以并行执行的,而其 余的工作需要等到前面的工作全部完成之 后才能够执行 在循环并行化的过程中,可以使用 ordered子句使得顺序执行的语句直 到前面的循环都执行完毕之后再执行 OpenMP任务调度 OpenMP中,任务调度主要用于并行 的for循环中,当循环中每次迭代的计 算量不相等时,如果简单地给各个线程 分配相同次数的迭代的话,会造成各个 线程计算负载不均衡,这会使得有些线 程先执行完,有些后执行

18、完,造成某些 CPU核空闲,影响程序性能。 int i, j; int a100100 = 0; for ( i =0; i 100; i+) for( j = i; j 100; j+ ) aij = i*j; 如果将最外层循环并行化的话,比如使用4 个线程,如果给每个线程平均分配25次循环 迭代计算的话,显然i0和i99的计算量 相差了100倍,那么各个线程间可能出现较 大的负载不平衡情况。 for循环并行化的任务调度方案 schedule子句的使用格式为: schedule(type,size) type参数 表示调度类型,有四种调度类型如下: static dynamic guided

19、 runtime (实际上是根据环境变量来选择前三种中的某 中类型。) size参数 (可选) size参数表示循环迭代次数,size参数必须是整数。 静态调度(static) 当parallel for编译制导语句没有带 schedule子句时,大部分系统中默认 采用static调度方式,这种调度方式非 常简单。假设有n次循环迭代,t个线程 ,那么给每个线程静态分配大约n/t次 迭代计算。 不使用size参数时,分配给每个线程的 是n/t次连续的迭代 #pragma omp parallel for schedule(static) for(i = 0; i 10; i+ ) printf(

20、“i=%d, thread_id=%dn“, i, omp_get_thread_num(); i=0, thread_id=0 i=1, thread_id=0 i=2, thread_id=0 i=3, thread_id=0 i=4, thread_id=0 i=5, thread_id=1 i=6, thread_id=1 i=7, thread_id=1 i=8, thread_id=1 i=9, thread_id=1 可以看出线程0得到了04次连续迭代,线程1得到59次连续迭代。 #pragma omp parallel for schedule(static, 2) for(i

21、 = 0; i 10; i+ ) printf(“i=%d, thread_id=%dn“, i, omp_get_thread_num(); i=0, thread_id=0 i=1, thread_id=0 i=4, thread_id=0 i=5, thread_id=0 i=8, thread_id=0 i=9, thread_id=0 i=2, thread_id=1 i=3, thread_id=1 i=6, thread_id=1 i=7, thread_id=1 0、1次迭代分配给线程0,每个线程依次分配到2次连续的迭代计算。 动态调度(dynamic) 动态调度是动态地将迭代

22、分配到各个线程, 动态调度可以使用size参数也可以不使用 size参数,不使用size参数时是将迭代逐个 地分配到各个线程,使用size参数时,每次 分配给线程的迭代次数为指定的size次。 使用一个内部任务队列,采用先来先服务的 方式进行调度。当某个线程闲下来,就为其 分配一个循环块。由此可见,动态策略可以 极大地保证线程组的负载平衡,但是需要额 外的开销,不能达到最佳性能。 guided调度(guided) guided调度是一种采用指导性的启发 式自调度方法。开始时每个线程会分配 到较大的迭代块,之后分配到的迭代块 会逐渐递减。迭代块的大小会按指数级 下降到指定的size大小,如果没有指定 size参数,那么迭代块大小最小会降到 1。 runtime调度(rumtime) runtime调度并不是和前面三种调度方式似 的真实调度方式,它是在运行时根据环境变 量OMP_SCHEDULE来确定调度类型 例如在unix系统中,可以使用setenv命令 来设置OMP_SCHEDULE环境变量: setenv OMP_SCHEDULE “dynamic, 2” 上述命令设置调度类型为动态调度,动态调度 的迭代次数为2。 在windows环境中,可以在”系统属性|高级 |环境变量”对话框中进行设置环境变量。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1