850 likes | 1.18k Views
第四章 OpenMP 多线程编程. 主要内容. OpenMP 编程简介 OpenMP 多线程应用程序编程技术. 1. OpenMP 编程简介. 1.1 OpenMP 多线程编程发展概况. OpenMP 是一种面向共享内存多线程并行编程技术 OpenMP 具有良好的可移植性 支持多种编程语言 Fortran C/C++ 支持多种平台 www.openmp.org. OpenMP 最初是为共享内存的多处理器系统设计的并行编程方法,这与通过消息传递进行并行编程模型有很大的不同。. OpenMP 的支持环境. Intel 的 C++ 和 Fortran 编译器
E N D
主要内容 • OpenMP编程简介 • OpenMP多线程应用程序编程技术
1.1 OpenMP多线程编程发展概况 • OpenMP是一种面向共享内存多线程并行编程技术 • OpenMP具有良好的可移植性 • 支持多种编程语言 Fortran C/C++ • 支持多种平台 • www.openmp.org
OpenMP最初是为共享内存的多处理器系统设计的并行编程方法,这与通过消息传递进行并行编程模型有很大的不同。OpenMP最初是为共享内存的多处理器系统设计的并行编程方法,这与通过消息传递进行并行编程模型有很大的不同。
OpenMP的支持环境 • Intel的C++和Fortran编译器 • Microsoft的Visual Studio 2005 • gcc4.2以上版本
1.2 OpenMP多线程编程基础 • OpenMP的编程模型以线程为基础,通过编译制导语句来显示地指导并行化 • OpenMP的执行模型采用Fork-Join的形式,在开始时,只有一个叫做主线程的运行线程存在;在运行过程中,当遇到需要进行并行计算的时候,派生出(Fork)线程来执行并行任务;在并行代码结束执行,派生线程退出或挂起,控制流程回到单独的主线程中(Join)
Fork-Join模型 并行区域
编译指导语句 运行时函数库 环境变量 OpenMP的实现 • 编译制导语句(精髓) • 运行时库函数 • 环境变量
编译制导语句 • 在编译器编译程序的时候,会识别特定的注释,而这些注释就包含着OpenMP程序的一些语义 • 在一个无法识别OpenMP语意的普通编译器中,这些注释会被当作普通的注释而被忽略 • 在C/C++程序中,OpenMP所有编译制导语句以#pragma omp开始,后面跟具体功能指令
编译制导语句 • Directive • parallel, for, parallel for, section, sections, single, master, critical, flush, ordered, atomic
运行时库函数 • OpenMP运行时函数库主要用以设置和获取执行环境相关的信息,它们当中也包含一系列用以同步的API • 运行时函数库 “omp.h” • omp_get_thread_num() 返回当前线程的号码
通过编译制导语句,可以将串行的程序逐步地改造成一个并行程序,达到增量更新程序的目的,减少程序编写人员一定的负担.通过编译制导语句,可以将串行的程序逐步地改造成一个并行程序,达到增量更新程序的目的,减少程序编写人员一定的负担. • 串行程序和并行程序保持在同一个源代码文件当中,减少了维护负担. • 编译制导语句,优势体现在编译阶段 • 运行时库函数,支持运行时对并行环境的改变和优化
1.3 编写OpenMP程序的准备工作 • 当前的Visual Studio .Net 2005完全支持OpenMP 2.0标准 • 通过新的编译器选项 /openmp来支持OpenMP程序的编译和链接
在OpenMP中,主要通过对循环或一段结构化代码定义并行区域的方式来实现多线程并行。在OpenMP中,主要通过对循环或一段结构化代码定义并行区域的方式来实现多线程并行。
#include "omp.h" int _tmain(int argc, _TCHAR* argv[]) { printf("Hello from serial.\n"); printf("Thread number=%d\n",omp_get_thread_num()); #pragma omp parallel { printf("Hello from parallel. Thread number=%d\n",omp_get_thread_num()); } printf("Hello from serial again.\n"); getchar(); return 0; }
2.1 循环并行化 • 循环并行化是使用OpenMP来并行化程序的最重要的部分 • 在C/C++语言中,循环并行化语句的编译制导语句格式如下: #pragma omp parallel for [clause[clause…]] for( i = first ; i<last ; i++) { body of the loop; }
另一种格式: #pragma omp parallel [clause[clause…]] { #pragma omp for [clause[clause…]] for( i = first ; i<last ; i++) { body of the loop; } } • 如果并行的线程需要在循环的开始,或结束时作些工作的话,就只能用parallel与for子句分离的版本。 • Parallel 将紧跟的程序块扩展为若干完全等同的并行区域,每个线程拥有完全相同的并行区域; • For 将循环中工作分配到线程组中,线程组中的每一个线程完成循环中的一部分。 • 子句用来控制编译制导语句的具体行为。
循环并行化语句的限制 并行化的语句必须是for循环语句并具有规范格式 • 能够推测出循环的次数 • for (index = start ; index < end ; increment_expr) • 在循环过程中不能使用break语句 • 不能使用goto和return语句从循环中跳出 • 可以使用continue语句
简单循环并行化 • 各个分量之间没有数据相关性 • 循环计算的过程也没有循环依赖型
循环依赖性 • 循环迭代相关
循环并行化编译制导语句的子句 • 循环并行化子句可以包含一个或者多个子句来控制循环并行化的实际执行 • 常见子句有: • 作用域子句(变量是共享的share还是私有的private) • 控制线程的调度(schedule )子句 • 动态控制是否并行化(if )子句 • 进行同步的子句(ordered )子句 • 控制变量在串行部分与并行部分传递(copyin )子句
循环嵌套 • 可以将嵌套循环的任意一个循环体进行并行化 • 循环并行化编译指导语句可以加在任意一个循环之前,则对应的最近的循环语句被并行化,其它部分保持不变
控制数据的共享属性 • OpenMP程序在同一个共享内存空间上执行 • 可以任意使用这个共享内存空间上的变量进行线程间的数据传递 • OpenMP还允许线程保留自己的私有变量不能让其它线程访问到
分配在栈上的数据都是私有的 • 全局变量及代码是共享的 • 动态分配的堆空间是共享的 • threadprivate指明某数据结构是私有的全局变量
控制数据的共享属性 • shared用来指示一个变量的作用域是共享的。 • privare用来指示一个变量的作用域是私有的。 • firstprivate对私有变量进行初始化,把串行变量值拷贝到私有变量中(线程开始) • lastprivate对私有变量最后终结的操作,把私有变量拷贝到同名串行变量中
使用作用域子句的一些规则 • 作用域子句中的变量是已经声明的有名变量 • 作用域子句在作用到类或者结构的时候,只能作用到类或者结构的整体,而不能只作用域类或者结构的一个部分 • 一个编译指导语句能够包含多个数据作用域子句 • 作用域子句只能出现在编译制导语句起作用的语句变量部分 • 默认情况下,并行区域中的所有变量是共享的,三种例外: • parallel for 循环中,循环变量是私有的 • 并行区域中的局部变量是私有的 • Private,firstprivate,lastprivate,reduction子句列出的变量是私有的
规约操作的并行化 • 在规约操作中,会反复将一个二元运算符应用在一个变量和另外一个值上,并把结果保存在原变量中 • 在使用规约操作时,只需在变量前指明规约操作的类型以及规约的变量 # pragma omp parallel for private(arx,ary,n) reduction(+:a,b) for(i=0;i<n;i++){ a=a+arx[i]; b=b+ary[i]; }
规约操作并行化的限制 • 能够在OpenMP的C/C++语言中出现的规约操作
私有变量的初始化和终结操作 • 循环并行化开始的时候,私有变量具有主线程中的同名变量的值 • 循环并行化后将私有变量返回给主线程中的同名变量 • firstprivate lastprivate
数据相关性与并行化操作 • 并不是所有的循环都能够使用#pragma omp parallel for来进行并行化 • 必须要保证数据两次循环之间不存在数据相关性 • 数据相关性又被称为数据竞争(Data Race) • 当两个线程对同一个变量进行操作,并且有一个操作为写操作的时候,就说明这两个线程存在数据竞争
2.2 并行区域编程 • 循环并行化实际上是并行区域编程的一个特例 • 并行区域简单的说就是通过编译制导语句使得一段代码能够在多个线程内部同时执行 • 并行区域编写的格式如下: #pragma omp parallel [clause[clause]…] block
parallel编译制导语句的执行过程 • 当程序遇到parallel编译制导语句的时候,就会生成相应数目(根据环境变量)的线程组成一个线程组,并将代码重复地在各个线程内部执行 • parallel的末尾有一个隐含的同步屏障(barrier),所有线程完成所需的重复任务有,在这个同步屏障出会和(join)
线程私有数据threadprivate,copyin • threadprivate指明(全局)变量是线程私有数据 • copyin对线程私有的全局变量进行初始化