ImageVerifierCode 换一换
格式:DOC , 页数:9 ,大小:80.50KB ,
资源ID:9010376      下载积分:10 金币
验证码下载
登录下载
邮箱/手机:
图形码:
验证码: 获取验证码
温馨提示:
支付成功后,系统会自动生成账号(用户名为邮箱或者手机号,密码是验证码),方便下次登录下载和查询订单;
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/9010376.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请


权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:4009-655-100;投诉/维权电话:18658249818。

注意事项

本文(OpenMP发展与优势.doc)为本站上传会员【pc****0】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

OpenMP发展与优势.doc

1、OpenMP发展与优势 OpenMP的规范由SGI发起,它是一种面向共享内存以及分布式共享内存的多处理器多线程并行编程语言。OpenMP是一种共享内存并行的应用程序编程接口。所有的处理器都被连接到一个共享的内存单元上,处理器在访问内存的时候使用的是相同的内存编址空间。由于内存是共享的,因此,某一处理器写入内存的数据会立刻被其它处理器访问到。 OpenMP具有良好的可移植性,支持Fortran和C/C++编程语言,操作系统平台方面则支持UNIX系统以及Windows 系统。OpenMP的重要性在于,它能够为编写多线程程序提供一种简单的方法,而无需程序员进行复杂的线程创建、同步、负载平

2、衡和销毁工作[1]。 2.2 OpenMP多线程编程基础 OpenMP的编程模型以线程为基础,通过编译指导语句来显式地指导并行化,为编程人员提供了对并行化的完整的控制。在并行执行的时候,主线程和派生线程共同工作。在并行代码结束执行后,派生线程退出或者挂起,不再工作,控制流回到单独的主线程中。OpenMP的功能由两种形式提供:编译指导语句和运行时库函数,下面分别介绍。 2.2.1编译指导语句 编译指导语句的含义是在编译器编译程序的时候,会识别特定的注释,而这些特定的注释就包含着OpenMP程序的一些语意。例如在C/C++程序中,用#pragma opm parallel来标示一段并行程序

3、块。在一个无法识别OpenMP语意的编译器中,会将这些特定的注释当作普通的程序注释而被忽略。因此,仅使用编译指导语句编写的OpenMP程序就能够同时被普通编译器和支持OpenMP的编译器处理。这种性质带来的好处就是用户可以用同一份代码来编写串行和并行程序,或者在把串行程序改编成并行程序的时候,保持串行源代码部分不变,从而极大地方便了程序编写人员。 编译指导语句的形式为: #pragam omp [clause[[,] clause]. . .] 其中directive部分就包含了具体的编译指导语句,包括parallel, for, parallel for, se

4、ction, sections, single, master, critical, flush, ordered和atomic。这些编译指导语句或者用来分配任务,或者用来同步。后面可选的子句clause给出了相应的编译指导语句的参数,子句可以影响到编译指导语句的具体行为,每一个编译指导语句都有一系列适合它的子句。 2.2.2运行时库函数 另外一种提供OpenMP功能的形式就是OpenMP的运行时库函数,它用于设置和获取执行环境的相关信息,它们当中也包含一系列用以同步的API。要使用运行时库函数所包含的库函数,必须在相应的源文件中包含头文件omp.h。OpenMP库函数类似于相应编程语言内

5、部的函数调用,因此在没有库支持的编译器上就无法正确识别OpenMP程序,这是库函数与编译指导语句不同的地方。 编译指导语句的优势体现在编译阶段,对于运行阶段则支持较少。OpenMP提供了运行时库函数来支持运行时对并行环境的改编和优化,但这种方式打破了源代码在串行和并行之间的一致性。 2.2.3使用Visual Studio.Net 2005编写OpenMP程序 Microsoft Visual Studio 2005完全支持OpenMP编程[5]。Visual Studio. Net 2005 Professional安装之后,即可编写OpenMP程序,无须另外安装其它软件。当前

6、的Visual Studio. Net 2005完全支持OpenMP 2.0标准。通过新的编译器选项/openmp来支持OpenMP程序的编译和连接,编译器会自动地将用户的代码和OpenMP在Windows下实现的库vcomp.dll连接在一起。程序在运行的时候会自动地寻找vcomp.dll。下面用Visual Studio.Net 2005生成一个新OpenMP项目OpenMP1。 启动Visual Studio.Net 2005,新建一个Win32 控制台程序,并命名OpenMP1。然后在项目上用鼠标右键单击,在弹出的菜单中选择“属性”,在弹出的对话框中如图2-1所示设置,完成项

7、目对OpenMP的支持。在OpenMP1.cpp输入如下代码: #include "omp.h" #include "conio.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 paral

8、lel. thread number=%d/n", omp_get_thread_num());} printf("Hello from serial again"); // 串行执行 getche(); return 0; }   程序运行结果如下: Hello from serial. Thread number=0 Hello from parallel. Thread number=0 Hello from parallel. Thread number=1 Hello from serial again   #p

9、ragma omp parallel标志着一个并行区域的开始。在支持OpenMP的编译器中,根据线程的数目,随后的程序块会被编译到不同的线程中执行。omp_get_thread_num()函数用来获得当前线程号码。在OpenMP程序中,每一个线程会被分配给一个唯一的线程号码,用来标识不同的线程,在并行部分执行完毕后,程序又回到串行部分,打印最后一个语句。默认情况下,系统会根据逻辑CPU的数量来确定线程数,运行本程序的计算机CPU为单核超线程,所以运行时分配了两个线程。   图2-1 配置项目属性以支持OpenMP程序 2.3 OpenMP多线程应用程序编程技术 2.3.1循环并行化

10、 循环并行化是使用OpenMP并行化程序的最重要部分。由于大量科学计算程序将很大一部分的的时间用在处理循环计算上,而对于循环并行化处理来说,这一部分的应用非常关键,因此循环并行化在OpenMP应用程序中是一个相对独立且非常重要的组成部分。在C/C++中,循环并行化语句的编译指导语句的格式为: #pragma omp parallel for [clause[clause…]] for (index = first ; test_expression ; increment_expr) {body of the loop; } 使用这个编译指导语句能将for循环中工作分配到一个线程组中,

11、而线程组中的每一个线程将完成循环中的一部分内容。 需要并行化的语句有一定的限制。首先,并行化的语句必须是for循环语句,且能够确定循环次数。其次,循环语句快应该是单出口单入口的。即在循环的过程中不允许没有执行完所有的循环就跳出循环,也不能从循环的外面跳到循环中。 由于多个线程同时执行循环语句中的功能指令,这就涉及到数据的作用域问题。这里所说的作用域是用来控制某一变量是否是在各个线程之间共享或者是某一个线程私有的。数据的作用域子句用shared来表示变量是在各个线程之间共享的,而用private来表示变量是一个线程私有的。在OpenMP中,如果没有指定变量的作用域,则默认的变量作用域是共享的

12、为了对一个循环进行并行化操作,必须要保证数据两次循环之间不存在数据相关性。数据相关性又称为竞争。当两个线程对一个数据进行操作,并且有一个操作为写操作时,就说明这两个线程存在数据竞争。此时读出的数据不一定就是前一次写操作写入的数据,而写入的数据也可能并不是程序所需要的。如下面这段代码必须使用private声明变量j是私有的,否则将发生数据竞争。 #pragma omp parallel for private(j) for(i=0; i

13、时执行。在C/C++语言中,并行区域编写的格式为: #pragma omp parallel [clause[clause]…] block 其中block是需要在多个线程中执行的代码块,每一个线程在遇到并行区域的编译指导语句时,都会同时执行跟随其后的程序代码块。在编译指导语句后面也可以跟随一些子句,包括private,reduction等子句都可以在并行区域指导语句中出现。parallel与parallel for语句类似,在使用时也有一定的限制。程序块必须是单一入口和单一出口的,不能从外面转入到程序块内部,也不能从程序块内部有多个出口转到程序块之外。下面用两个示例程序来说明

14、编译指导语句parallel的使用,例3_1使用的是并行区域编译指导语句,例3_2使用的循环并行化的编译指导语句。 例2_1 使用并行区域编译指导语句 #pragma omp parallel // 并行执行 for(int i=0; i<2; i++) printf("Hello world i=%d/n", i); 输出结果为: Hello world i=0 Hello world i=1 Hello world i=0 Hello world i=1 例2_2 使用循环并行化的编译指导语句 #pragma om

15、p parallel for // 并行执行 for(int i=0; i<2; i++) printf("Hello world i=%d/n", i); 输出结果为: Hello world i=0 Hello world i=1 两个程序的唯一区别是使用的编译指导语句不同,一个使用的是parallel另一个使用的是parallel for。从结果可以看出并行区域与循环并行化的区别,即并行区域采用复制执行方式,代码在所有线程都执行一遍(环境变量OMP_NUM_THREADS=2);而循环并行化则采用了工作分配的执行方式,将循环的所有工作分配到各个

16、线程中执行,所有线程工作的总和等于原来串行时的工作量。 小结:在程序遇到编译指导语句#pragma omp parallel时,会根据环境变量OMP_NUM_THREADS的值生成相应数量的线程,将代码复制到各个线程中执行。 上述parallel编译指导语句提供了一种简单的并行方法,能够将工作在多个线程的代码重复运行。但是这并不能提高程序的效率,我们希望的是工作被分配到多个线程中,由各个线程合作完成。在OpenMP中,每一个线程都可以调用omp_get_thread_num()函数来获得自己唯一的线程号,并可以利用这个线程号来获得不同的工作任务。在OpenMP语法中可以通过编译指导语句#p

17、ragma omp parallel for进行循环并行化达到并行的目的,也可以用sections编译指导语句和section子句将不同的任务编写成不同的代码片段并行执行,下面将举例说明。   1.    工作分区编码 例2_3 #pragma omp parallel sections { #pragma omp section printf("section 1 thread=%d/n", omp_get_thread_num()); #pragma omp section printf("section 2 thread=%d/n",

18、omp_get_thread_num()); #pragma omp section printf("section 3 thread=%d/n", omp_get_thread_num()); } 输出结果为: section 1 thread=0 section 2 thread=1 section 3 thread=0 可以看到,在使用工作分区编码的时候,各个线程自动从各个分区中获得任务执行。并且在执行完一个分区的时候,如果分区里还有未完成的工作,则继续取得任务执行。   2.    线程私有数据与threadprivate子句 除了pri

19、vate子句能够产生线程私有的变量之外,还需要考虑一些全局的数据。这些全局的数据可能是整个程序运行过程中都需要的数据,或是在源程序中跨多个文件所需要的变量。在通常情况下,这些数据都是共享的数据,所有线程访问的都是共享内存空间中的同一内存地址内容。然而,有些时候,对于每一个线程来说,可能需要生成自己私有的线程数据,此时,就需要使用threadprivate子句来标明某一个变量是线程私有数据,在程序运行的过程中,不能够被其它线程访问到。 例2_4 int counter=0; #pragma omp threadprivate(counter) //使用threadprivate子句

20、 void inc_counter() {counter++;} int _tmain(int argc, _TCHAR* argv[]){ #pragma omp parallel for(int i=0;i<10000;i++) inc_counter(); printf("counter=%d/n",counter); } 输出结果为: counter=10000 程序中使用了threadprivate子句,将counter变量变成一个私有变量。若将#pragma omp threadprivate(counter)语句去掉,则全局变量coun

21、ter变为共享,此时下面区域并行部分会产生数据冲突,执行结果将不可知。 2.3.3线程同步 在OpenMP应用程序中,由于是多线程执行,所以必须具有必要的线程同步机制来保证程序在出现数据竞争的时候能够得出正确的结果,并且在适当的时候控制线程的执行顺序。OpenMP支持两种不同类型的线程同步机制,一种是互斥锁的机制,另外一种同步机制是事件通知机制。互斥的操作针对需要保护的数据而言,在产生了数据竞争的内存区域加入互斥,包括critical、atomic等语句以及函数库中的互斥函数。而事件机制则控制规定线程执行顺序时所需要的同步屏障。 1.    互斥锁机制 在OpenMP中,提供了三种不同

22、的互斥锁机制,用来对一块内存进行保护,它们分别是临界区、原子操作(atomic)以及由库函数提供的同步操作。 (1)    临界区 临界区通过编译指导语句对产生数据竞争的内存变量进行保护。在程序需要访问可能产生竞争的内存数据时,都需要插入相应的临界区代码。临界区编译指导语句的格式为: #pragma omp critical [(name)] block 在执行程序块block之前,必须先获得临界区的控制权,在多线程执行时,OpenMP会保证每次最多只有一个线程执行临界区,name是临界区的名字。临界区的使用如下例所示。 代码片段2_1 // 将数组a

23、r中的最大值取出赋值给变量max #pragma omp parallel for for(i=0;i<100;i++) { #pragma omp critical if(ar[i]>max) max = ar[i]; } (2)    原子操作 原子操作是OpenMP编程方式给同步编程带来的特殊的功能。通过一条指令就能够完成数据的读取与更新操作,原子操作在执行过程中是不会被打断的。因此这种方式提供了一种更高效的互斥锁机制。在OpenMP这种功能是通过编译指导语句#pragma omp atomic提供的,原子操作如下所示: 代码片段2_2 #pra

24、gma omp parallel { for(int i=0;i<10000;i++) #pragma omp atomic counter++; } 输出结果为: counter=20000 当使用atomic语句时,执行结果是20000(两个线程进行并行区域运算,counter自加20000次);当不使用atomic语句时,程序会产生数据竞争,例如counter当前值为100,当线程0执行counter+1操作后,线程0挂起,线程1执行counter+1并赋值操作,这时counter值是101,现在继续线程0的操作,它将刚执行完的加法

25、操作的结果(101)赋值给counter,counter最后的值为101,显然结果是不正确的。 (3)        运行时库函数的互斥锁支持 除了critical、atomic编译语句外,OpenMP还通过一系列库函数支持更加细致的互斥操作。表2-1列出了OpenMP函数库提供的互斥锁函数。   函数名称 描述 void omp_init_lock(omp_lock_t *) 初始化一个互斥锁 void omp_destroy_lock(omp_lock_t *) 结束一个互斥锁的使用并释放内存 void omp_set_lock(omp_lock_t *) 获得一个互

26、斥锁 void omp_unset_lock(omp_lock_t *) 释放一个互斥锁 int omp_test_lock(omp_lock_t *) 试图获得一个互斥锁,并在成功时返回真,失败时返回假 表 2-1 互斥锁函数的使用比起编译指导语句更加灵活。编译指导语句进行的互斥锁支持只能放置在一段代码之前,作用在这段代码之上,使用函数的互斥锁支持则可以将函数放置在程序员所需的任意位置。   2.    同步屏障语句 在并行执行的时候,有些情况下需要程序员插入同步屏障语句#pragma omp barrier。此时,在并行区域的执行过程中,所有的执行线程都会在同步屏障语句上

27、进行同步。 #pragma omp parallel { initialization(); #pragma omp barrier process(); } 在上面例子中,只有等所有线程都完成初始化操作后,才能进行下一步的处理。 2.3.4 OpenMP多线程应用程序性能分析 影响性能的主要因素有 (1)    OpenMP本身的开销 OpenMP获得应用程序多线程并行化的能力不是凭空而来的,它需要一定程序库的支持,库中代码的运行必然会带来一定的开销。实际上并不是所有的代码都需要并行化,有些代码在并行化后执行的效率反而不如串行时高,原因就是加上

28、了并行化所带来的开销后,代码的执行效率降低。因此,只有在并行执行代码段负担足够大,而引入OpenMP本身的开销又足够小时,引入并行化操作才能提高程序执行效率。 (2)    负载均衡 已知一个OpenMP应用程序在执行的过程中,有很多的同步点,线程只有在进行同步之后才能继续执行下面的代码。因此某一个线程在执行到同步点之后,若没有进一步的工作需要完成,此线程只有等待其它线程执行完毕后才能继续执行。此时,如果各个线程之间的负载不均衡,就有可能出现某些线程“空等”,而另外一些线程因负担沉重,要很长事件才能完成任务。看下面的例子: 示例程序见例2_5 程序运行结果为: count=93506

29、09 考虑负载均衡修改程序后的运行结果 count=5191944 上面程序中的QueryPerformanceCounter()函数用于获得CPU内部的计数器的值,在代码前后分别获得的计数器的值的差越大,说明这段代码执行的时间越长。该值和CPU的主频有关,在不同的计算机上,该值是没有可比性的。 可以看到,这两个循环的工作量是一样的,但是运行时间几乎差了一倍。由于使用了循环并行化,第一个循环OpenMP将前50个循环任务分配给0号线程,将后面50个循环任务分配给1号线程。前面50循环执行的是50个空操作,而后面的50个循环执行的负担沉重的任务,造成了负载不均衡,0号线程很快执行完毕,用

30、很长的时间等待1号线程执行。在第二个循环中,OpenMP仍然将前面50个循环分配给0号线程,将后面50个循环分配给1号线程,,但是负载的分配发生了很大的变化,负载被均衡地分配到两个线程,两个线程几乎同时完成工作,这样可获得执行效率的提高。 (3)    线程同步带来的开销 线程之间存在同步开销是多线程应用程序的特点,在进行同步时候必然会带来一定的开销。很多情况下,不合适的同步机制或算法会使代码的运行效率下降。 示例程序见例2_6 程序运行结果为: sum=1000000, count=10548 sum=1000000, count=935416 程序的第一个循环是串行的,第二个

31、循环是在第一个循环的基础上加上了并行化支持,为了消除数据竞争又加入了同步操作,由于对内存单元的操作是同步的,产生的实际运行过程是串行的,并且加上了并行化负担,使用运行效率比串行的效率还要低的多。 2.4 开发实例 下面我将使用VS 2005 + OpenMP编写一个实现选择排序算法的程序实例。选择排序的基本思想是:每一趟在n-i+1(i=1,2,...,n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。它的时间复杂度为O(n ),核心算法[6]可以表述为: void SelectionSort(SqList &L){ for(i=1; i

32、h; i++){ // 选择第i小的记录 // 在L.r[i…L.length]中选择key最小的记录(使用for循环完成) j = SelectionMinKey(L,i); if(i!=j) L.r[i]<—>L.r[j]; } }// SelectionSort 经过对OpenMP的学习,可以将这个算法并行化。我采取的办法是用条件编译语句#pragma omp parallel sections使两个并行区共同完成最外层的循环,这样可以将循环次数减半,当然也可以是四个或八个,但是考虑到在双核处理器上运行,过多

33、的分区、线程反而会带来额外的开销,降低程序运行效率。附件中的“SelectionSort_UnOpt”和“SelectionSort_Opt”分别是用传统串行方法编写的和采用并行工作区编码方式进行优化后的程序。经过编译,两种方法编写的程序运行时间相差很大,采取并行算法后,程序的运行效率显著提高了。图2-2和2-3是两个程序在双核处理器上的运行结果。                         前者266ms,后者78ms.   2.5 小结 本章介绍了OpenMP的开发环境和开发OpenMP程序的基本方法。实现OpenMP程序有两种方式:编译指导语句和运行时库函数。库函数的使用类似于编程语言的内部函数调用,因此在没有库支持的编译器上是无法正确识别OpenMP程序的,这是库函数和编译指导语句的区别。在并行方式上也有两种不同的形式:循环并行化和并行区域编程。前者是以利用不同线程分别完成循环中的一部分的方式实现并行,而并行区域编程则是每个线程均完全地执行循环部分的代码。

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服