周伟明的专栏

<<软件测试实践>> 已上市

用户操作
[即时聊天] [发私信] [加为好友]
周伟明ID:drzhouweiming
195130次访问,排名375好友2人,关注者27
软件技术爱好及研究者
drzhouweiming的文章
原创 50 篇
翻译 0 篇
转载 0 篇
评论 334 篇
周伟明的公告
一书已上市, 欢迎大家提出意见!
最近评论
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
domemy:Linux 环境下的多核调试
— Intel + Totalview 强强联合!
目前,在软件开发行业,各种性能优异的调试工具层出不穷。但是,它们中的绝大部分都只支持windows环境。即使能支持linux平台,操作起来也很不方便。因此,对于长期在linux上编写程序的开发人员来说,如何调试就成了一个令人头痛的问题!Intel软件 和 Total……
文章分类
    收藏
      相册
      最近文章
      1、多核新观念-象使用内存一样使用CPU?
      2、多核编程中的任务随机竞争模式的概率分析
      3、OpenMP创建线程中的锁及原子操作性能比较
      4、多核编程中的任务分组竞争模式
      5、称球问题的测试解法
      90%程序员写不出无BUG的二分查找程序?
      C/C++代码检视实例
      多核编程中的负载平衡难题
      多核编程中的锁竞争难题
      微软过桥问题与测试人员素养
      接口关系稳定原理探索
      接口设计定理
      模块分解原理与三权分立
      模块分解原理的探索
      测试驱动需求分析
      筑一座坝治好中国的沙漠
      存档
      软件项目交易
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      原创 OpenMP并行程序设计(二)收藏

      新一篇: 只要筑一座坝就可以治理中国的沙漠? | 旧一篇: OpenMP并行程序设计(一)

       
      OpenMP并行程序设计(二)
      1、fork/join并行执行模式的概念
      OpenMP是一个编译器指令和库函数的集合,主要是为共享式存储计算机上的并行程序设计使用的。
      前面一篇文章中已经试用了OpenMP的一个Parallel for指令。从上篇文章中我们也可以发现OpenMP并行执行的程序要全部结束后才能执行后面的非并行部分的代码。这就是标准的并行模式fork/join式并行模式,共享存储式并行程序就是使用fork/join式并行的。
      标准并行模式执行代码的基本思想是,程序开始时只有一个主线程,程序中的串行部分都由主线程执行,并行的部分是通过派生其他线程来执行,但是如果并行部分没有结束时是不会执行串行部分的,如上一篇文章中的以下代码:
      int main(int argc, char* argv[])
      {
           clock_t t1 = clock();
      #pragma omp parallel for
           for ( int j = 0; j < 2; j++ ){
               test();
           }
           clock_t t2 = clock();
           printf("Total time = %d\n", t2-t1);
       
           test();
           return 0;
      }
      在没有执行完for循环中的代码之前,后面的clock_t t2 = clock();这行代码是不会执行的,如果和调用线程创建函数相比,它相当于先创建线程,并等待线程执行完,所以这种并行模式中在主线程里创建的线程并没有和主线程并行运行。
      2、OpenMP指令和库函数介绍
      下面来介绍OpenMP的基本指令和常用指令的用法,
      在C/C++中,OpenMP指令使用的格式为
             pragma omp 指令 [子句[子句]…]
      前面提到的parallel for就是一条指令,有些书中也将OpenMP的“指令”叫做“编译指导语句”,后面的子句是可选的。例如:
      #pragma omp parallel private(i, j)
      parallel 就是指令, private是子句
      为叙述方便把包含#pragma和OpenMP指令的一行叫做语句,如上面那行叫parallel语句。
       
      OpenMP的指令有以下一些:
             parallel,用在一个代码段之前,表示这段代码将被多个线程并行执行
             for,用于for循环之前,将循环分配到多个线程中并行执行,必须保证每次循环之间无相关性。
             parallel for, parallel 和 for语句的结合,也是用在一个for循环之前,表示for循环的代码将被多个线程并行执行。
             sections,用在可能会被并行执行的代码段之前
             parallel sections,parallel和sections两个语句的结合
             critical,用在一段代码临界区之前
             single,用在一段只被单个线程执行的代码段之前,表示后面的代码段将被单线程执行。
             flush
      barrier,用于并行区内代码的线程同步,所有线程执行到barrier时要停止,直到所有线程都执行到barrier时才继续往下执行。
      atomic,用于指定一块内存区域被制动更新
      master,用于指定一段代码块由主线程执行
      ordered, 用于指定并行区域的循环按顺序执行
      threadprivate, 用于指定一个变量是线程私有的。
      OpenMP除上述指令外,还有一些库函数,下面列出几个常用的库函数:
             omp_get_num_procs, 返回运行本线程的多处理机的处理器个数。
             omp_get_num_threads, 返回当前并行区域中的活动线程个数。
             omp_get_thread_num, 返回线程号
             omp_set_num_threads, 设置并行执行代码时的线程个数
      omp_init_lock, 初始化一个简单锁
      omp_set_lock, 上锁操作
      omp_unset_lock, 解锁操作,要和omp_set_lock函数配对使用。
      omp_destroy_lock, omp_init_lock函数的配对操作函数,关闭一个锁
       
      OpenMP的子句有以下一些
      private, 指定每个线程都有它自己的变量私有副本。
      firstprivate指定每个线程都有它自己的变量私有副本,并且变量要被继承主线程中的初值。
      lastprivate主要是用来指定将线程中的私有变量的值在并行处理结束后复制回主线程中的对应变量。
      reduce用来指定一个或多个变量是私有的,并且在并行处理结束后这些变量要执行指定的运算。
      nowait忽略指定中暗含的等待
      num_threads指定线程的个数
      schedule指定如何调度for循环迭代
      shared指定一个或多个变量为多个线程间的共享变量
      ordered用来指定for循环的执行要按顺序执行
      copyprivate用于single指令中的指定变量为多个线程的共享变量
      copyin用来指定一个threadprivate的变量的值要用主线程的值进行初始化。
      default用来指定并行处理区域内的变量的使用方式,缺省是shared
      3、parallel 指令的用法
      parallel 是用来构造一个并行块的,也可以使用其他指令如for、sections等和它配合使用。
      在C/C++中,parallel的使用方法如下:
      #pragma omp parallel [for | sections] [子句[子句]…]
      {
             //代码
      }
      parallel语句后面要跟一个大括号对将要并行执行的代码括起来。
      void main(int argc, char *argv[]) {
      #pragma omp parallel
      {
                    printf(“Hello, World!\n”);
      }
      }
      执行以上代码将会打印出以下结果
      Hello, World!
      Hello, World!
      Hello, World!
      Hello, World!
      可以看得出parallel语句中的代码被执行了四次,说明总共创建了4个线程去执行parallel语句中的代码。
      也可以指定使用多少个线程来执行,需要使用num_threads子句:
      void main(int argc, char *argv[]) {
      #pragma omp parallel num_threads(8)
      {
                    printf(“Hello, World!, ThreadId=%d\n”, omp_get_thread_num() );
      }
      }
      执行以上代码,将会打印出以下结果:
      Hello, World!, ThreadId = 2
      Hello, World!, ThreadId = 6
      Hello, World!, ThreadId = 4
      Hello, World!, ThreadId = 0
      Hello, World!, ThreadId = 5
      Hello, World!, ThreadId = 7
      Hello, World!, ThreadId = 1
      Hello, World!, ThreadId = 3
      从ThreadId的不同可以看出创建了8个线程来执行以上代码。所以parallel指令是用来为一段代码创建多个线程来执行它的。parallel块中的每行代码都被多个线程重复执行。
      和传统的创建线程函数比起来,相当于为一个线程入口函数重复调用创建线程函数来创建线程并等待线程执行完。
      4、for指令的使用方法
      for指令则是用来将一个for循环分配到多个线程中执行。for指令一般可以和parallel指令合起来形成parallel for指令使用,也可以单独用在parallel语句的并行块中。
      #pragma omp [parallel] for [子句]
            for循环语句
       
      先看看单独使用for语句时是什么效果:
      int j = 0;
      #pragma omp for
           for ( j = 0; j < 4; j++ ){
               printf(j = %d, ThreadId = %d\n, j, omp_get_thread_num());
           }
      执行以上代码后打印出以下结果
      j = 0, ThreadId = 0
      j = 1, ThreadId = 0
      j = 2, ThreadId = 0
      j = 3, ThreadId = 0
      从结果可以看出四次循环都在一个线程里执行,可见for指令要和parallel指令结合起来使用才有效果:
      如以下代码就是parallel 和for一起结合成parallel for的形式使用的:
      int j = 0;
      #pragma omp parallel for
           for ( j = 0; j < 4; j++ ){
               printf(j = %d, ThreadId = %d\n, j, omp_get_thread_num());
           }
      执行后会打印出以下结果:
      j = 0, ThreadId = 0
      j = 2, ThreadId = 2
      j = 1, ThreadId = 1
      j = 3, ThreadId = 3
      可见循环被分配到四个不同的线程中执行。
       
      上面这段代码也可以改写成以下形式:
      int j = 0;
      #pragma omp parallel
      {
      #pragma omp for
           for ( j = 0; j < 4; j++ ){
               printf(j = %d, ThreadId = %d\n, j, omp_get_thread_num());
           }
      }
      执行以上代码会打印出以下结果:
      j = 1, ThreadId = 1
      j = 3, ThreadId = 3
      j = 2, ThreadId = 2
      j = 0, ThreadId = 0
       
       
      在一个parallel 块中也可以有多个for语句,如:
      int j;
      #pragma omp parallel
      {
      #pragma omp for
           for ( j = 0; j < 100; j++ ){
              
           }
      #pragma omp for
           for (  j = 0; j < 100; j++ ){
              
           }
      }
       
      for 循环语句中,书写是需要按照一定规范来写才可以的,即for循环小括号内的语句要按照一定的规范进行书写,for语句小括号里共有三条语句
      for( i=start; i < end; i++)
       
      i=start; 是for循环里的第一条语句,必须写成 “变量=初值” 的方式。如 i=0
      i < end;是for循环里的第二条语句,这个语句里可以写成以下4种形式之一:
      变量 < 边界值
      变量 <= 边界值
      变量 > 边界值
      变量 >= 边界值
      如 i>10 i< 10   i>=10 i>10 等等
      最后一条语句i++可以有以下9种写法之一
      i++
      ++i
      i--
      --i
      i += inc
      i -= inc
      i = i + inc
      i = inc + i
      i = i –inc
       
      例如i += 2; i -= 2;i = i + 2;i = i - 2;都是符合规范的写法。
      5 sections和section指令的用法
      section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段,每段都并行执行。用法如下:
      #pragma omp [parallel] sections [子句]
      {
         #pragma omp section
         {
                  代码块
         } 
      }
       
      先看一下以下的例子代码:
      void main(int argc, char *argv)
      {
      #pragma omp parallel sections {
      #pragma omp section
          printf(“section 1 ThreadId = %d\n”, omp_get_thread_num());
      #pragma omp section
          printf(“section 2 ThreadId = %d\n”, omp_get_thread_num());
      #pragma omp section
          printf(“section 3 ThreadId = %d\n”, omp_get_thread_num());
      #pragma omp section
          printf(“section 4 ThreadId = %d\n”, omp_get_thread_num());
      }
      执行后将打印出以下结果:
      section 1 ThreadId = 0
      section 2 ThreadId = 2
      section 4 ThreadId = 3
      section 3 ThreadId = 1
       
      从结果中可以发现第4段代码执行比第3段代码早,说明各个section里的代码都是并行执行的,并且各个section被分配到不同的线程执行。
       
      使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差不大,否则某个section执行时间比其他section过长就达不到并行执行的效果了。
       
      上面的代码也可以改写成以下形式:
      void main(int argc, char *argv)
      {
      #pragma omp parallel {
      #pragma omp sections
      {
      #pragma omp section
             printf(“section 1 ThreadId = %d\n”, omp_get_thread_num());
      #pragma omp section
             printf(“section 2 ThreadId = %d\n”, omp_get_thread_num());
      }
      #pragma omp sections
      {
       
      #pragma omp section
             printf(“section 3 ThreadId = %d\n”, omp_get_thread_num());
      #pragma omp section
             printf(“section 4 ThreadId = %d\n”, omp_get_thread_num());
      }
      }
      执行后将打印出以下结果:
      section 1 ThreadId = 0
      section 2 ThreadId = 3
      section 3 ThreadId = 3
      section 4 ThreadId = 1
       
      这种方式和前面那种方式的区别是,两个sections语句是串行执行的,即第二个sections语句里的代码要等第一个sections语句里的代码执行完后才能执行。
       
      用for语句来分摊是由系统自动进行,只要每次循环间没有时间上的差距,那么分摊是很均匀的,使用section来划分线程是一种手工划分线程的方式,最终并行性的好坏得依赖于程序员。
       
      本篇文章中讲的几个OpenMP指令parallel, for, sections, section实际上都是用来如何创建线程的,这种创建线程的方式比起传统调用创建线程函数创建线程要更方便,并且更高效。
      当然,创建线程后,线程里的变量是共享的还是其他方式,主线程中定义的变量到了并行块内后还是和传统创建线程那种方式一样的吗?创建的线程是如何调度的?等等诸如此类的问题到下一篇文章中进行讲解。
       
       

      发表于 @ 2006年09月04日 15:31:00|评论(loading...)|编辑

      新一篇: 只要筑一座坝就可以治理中国的沙漠? | 旧一篇: OpenMP并行程序设计(一)

      评论

      #ethantung 发表于2007-04-19 16:10:08  IP: 219.140.144.*
      void main(int argc, char *argv[]) {
      #pragma omp parallel
      {
      printf(“Hello, World!\n”);
      }
      }
      执行以上代码将会打印出以下结果
      Hello, World!
      Hello, World!
      Hello, World!
      Hello, World!

      似乎这里没有做同步,输出也许是乱序的吧,比如HellHell oo WorWlodrl 之类。
      #drzhouweiming 发表于2007-04-19 22:37:36  IP: 220.249.249.*
      printf是系统函数,其同步是内置的,如果在VC里连接到多线程库时自然就是同步的。
      #baodi_z 发表于2007-06-26 17:08:20  IP: 59.61.91.*
      不错,收藏先
      同时问下楼主,我防止你的那个简单的printf例子,
      编译通过,但是在执行时出错,说是找不到vcompd.dll
      但是vcompd.dll文件是存在vs安装目录的某个子文件夹下的
      不知道这个是怎么回事
      在VS中按F5就会这样
      #drzhouweiming 发表于2007-06-26 21:18:48  IP: 58.31.71.*
      To baodi_z: 请检查一下“\[windows安装目录]\system32”目录下有没有vcompd.dll这个文件
      #baodi_z 发表于2007-06-27 11:32:45  IP: 59.61.91.*
      还有一点:在单核的CPU上是否可以运行openMP
      因为我把vcompd.dll复制到debug目录下,然后运行程序的exe文件还是出错,大概的错误信息如下:应用程序正常初始化(0xc0000142)失败.请单击确定终止应用程序。


      系统中存在几个vcompd.dll,但是路径不同,如下

      C:\WINDOWS\WinSxS\x86_Microsoft.VC80.DebugOpenMP_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_66b81908

      C:\WINDOWS\WinSxS\x86_Microsoft.VC80.OpenMP_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0ee63867
      #baodi_z 发表于2007-06-27 13:04:06  IP: 59.61.91.*
      除了弹出错误信息如下:应用程序正常初始化(0xc0000142)失败.请单击确定终止应用程序。


      在程序的控制台中还有如下错误信息,不知道楼主是否遇到过这种问题。
      Error: r6034
      An application has made an attempt to load vcompd.dll without using a manifest.
      this is an unsupported way to load Visual C++ DLLs. You need to modify your application
      to build with a manifest.
      For more information , see the "Visual C++ Libraries as shared Side-by-Side Assembles"
      topic in the product documentation.
      #drzhouweiming 发表于2007-06-27 17:08:50  IP: 58.31.71.*
      我的机器是双核机器,system32目录下有vcompd.dll这个文件
      估计可能是VS2005没有将东西装全,可能安装程序在单核CPU机器和多核CPU机器上的安装不同,我身边没有单核机器,没法试。
      #ghyangyh 发表于2007-12-17 22:36:11  IP: 221.223.4.*
      我根据本文的方法,用openmp实现了我需要并行化的一部分代码,速度提升了,但是我用生成的release版本在其他没有装vc2005环境的机器上却出现了“由于应用程序配置出错,应用程序未能启动。。。”的问题,能否问一下大家有没有遇到过这样的问题,该如何解决?另外,我是把有关openmp相关的dll文件、lib文件和.h文件及Microsoft.VC80.OpenMP.manifest文件都拷过去的,而且在工程的属性里的code generation里的run-time library里选了Multi-threaded dll这样还是出现了上面的错误,到底该如何解决希望能能帮帮我,非常感谢!
      #ghyangyh 发表于2007-12-17 22:38:12  IP: 221.223.4.*
      补充上面,我写的程序是一个consloe程序,没有用到。net特性和其他库
      #drzhouweiming 发表于2007-12-20 10:08:35  IP: 222.66.53.*
      To ghyangyh,

      先检查一下manifest里的目录和拷贝的后的目录是否一致,如果有不一致的先改成一样。

      如果还不行的话请把VC2005的Redist包在未装VC2005的机器上安装上去再试一下(注意检查一下安装目录)。

      (由于我这几天搬家,暂时没有时间来处理这些问题,过几天等有空时我再仔细测试一下你说的问题)


      #jadedrip 发表于2008-04-24 15:04:22  IP: 122.224.164.*
      To ghyangyh
      你犯了和我一样的错误。
      程序里没有包含 <omp.h> ,包含了就好了
      #egmkang 发表于2008-05-14 14:18:11  IP: 116.225.183.*
      你好.我写了一个很简单的程序,debug,release都可以成功运行.现在发布的时候出现了一点问题.
      设置运行时库为/MT,其他的均为设置.
      然后copy VC\redist\X86\Microsoft.VC80.OPENMP目录下面的所有文件到.exe所在目录.
      但是到了别人的机器上面还是不能运行.
      希望那个你能帮我解决.谢谢.
      用depends工具检测,好像说还有问题,让我看系统日志.
      #zhouwen 发表于2008-05-19 21:01:03  IP: 58.210.32.*
      已在project的property页面上的C/C++ -> Language 选项卡中将OpenMP Support这项设置为Yes,也添加了#include <omp.h> ,编译通过,但是在执行时出错,说是找不到vcompd.dll。拷贝dll到debug目录下也不行。
      #drzhouweiming 发表于2008-05-21 15:08:52  IP: 58.33.97.*
      To zhouwen:
      在VC里做如下配置试试,
      在项目属性§链接器§清单文件对话框页面,附加的清单依赖项需要增加类似如下所示的内容:
      "type='win32' name='Microsoft.VC80.OpenMP' version='8.0.50727.42' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b'"
      其中的version是VC的版本号,比如windows安装在C:\Windows目录下,那么安装完后可以在目录C:\Windows\winsxs\x86_microsoft.vc80.openmp_1fc8b3b9a1e18e3b_8.0.50727.42_none_
      45e008191e507087 中发现vcomp.dll文件,目录名称中vc80.openmp后接着的就是publicKeyToken字符串,再接着就是version号。在附加清单依赖项里输入的内容必须和目录名称中的内容一致。
      #zhouwen 发表于2008-05-22 21:10:00  IP: 58.210.62.*
      谢谢,但是添加后会在清单文件中出现如下所示的内容:
      <assemblyIdentity type='win32' name='Microsoft.VC80.DebugOpenMP' version='8.0.50727.42' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /DEBUG /PDB:e:\C++\OpenMPTest\debug\OpenMPTest.pdb />
      编译提示清单文件格式出错。
      #zhouwen 发表于2008-05-24 09:25:05  IP: 58.210.34.*
      谢谢指点,打了vs2005 sp1补丁就没问题了。
      非常感谢!
      #kuangxiangjie 发表于2008-07-16 22:10:21  IP: 222.171.7.*
      这种方式和前面那种方式的区别是,两个sections语句是串行执行的,即第二个sections语句里的代码要等第一个sections语句里的代码执行完后才能执行。

      您好,我对这句话不是很明白....parallel不是并行的么,怎么在它里面的两个ses会是串行的呢
      #drzhouweiming 发表于2008-07-18 12:05:49  IP: 222.66.65.*
      因为sections语句后面有隐含的barrier
      如果要将其变成并行的话,需要使用nowait子句
      发表评论  


      当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
      Csdn Blog version 3.1a
      Copyright © 周伟明