精华内容
下载资源
问答
  • OpenMP实现数组并行相加

    千次阅读 2015-01-14 09:08:33
    int main() { int sum = 0; int a[] = {1,2,3,4,5,6,7,8}; int coreNum = omp_get_num_procs();//获得处理器个数 printf("处理器个数:%d\n", coreNum); int *sumArray = new int[coreNum];...
    int main()
    {
    	int sum = 0;
    	int a[] = {1,2,3,4,5,6,7,8};
    	int coreNum = omp_get_num_procs();//获得处理器个数
    	printf("处理器个数:%d\n", coreNum);
    	int *sumArray = new int[coreNum]; //每一个处理器统计一部分总和
    	for (int i=0; i<coreNum; ++i)
    	{
    		sumArray[i] = 0;
    	}
    #pragma omp parallel for
    	for (int i=0; i<8; ++i)  // 8核CPU,每个核心做一个
    	{
    		int k = omp_get_thread_num();// 获得每个线程的ID
    		printf("线程ID:%d\n", k);
    	    sumArray[k] = sumArray[k] + a[i];
    	}
    	for (int i=0; i<coreNum; ++i)
    	{
    		sum = sum + sumArray[i];
    	}
    	std::cout << "sum: " << sum << std::endl;
    	system("pause");
    	return 0;
    }

    展开全文
  • openmp做多核并行处理遇到了问题, 例如有个vector <string > a, 想要对下面并行 #program opm parallel for num_threads(2) for (int i=0;i();i++) cout [i]; 并行下a[i]被共享读,设置处理器个数为1...
  • OpenMP

    2014-01-06 16:35:56
    原文地址:OpenMP作者:丅鋭 我的学习笔记一:OpenMP 收藏  1. OpenMP是一种API,用于编写可移植的多线程应用程序,无需程序员进行复杂的线程创建、同步、负载平衡和销毁工作。 使用OpenMP的好处:  1)CPU核数...
    原文地址:OpenMP 作者:丅鋭
    我的学习笔记一:OpenMP 收藏 
    1.
    OpenMP是一种API,用于编写可移植的多线程应用程序,无需程序员进行复杂的线程创建、同步、负载平衡和销毁工作。
    使用OpenMP的好处:
       1)CPU核数扩展性问题
       2)方便性问题
       3)可移植性问题
    OpenMP指令和库函数介绍:
       在C/C++中,OpenMP指令使用的格式为:#pragma omp 指令 [子句[子句]…]
       用法详见OpenMP编程指南。
    2.
    #pragma omp parallel for
           for(i=0;i<length;i++)
           {
               //没有循环迭代相关的语句,如把图像数组中的RGB值转为灰度值。
           }
    3.
    对可以以多线程执行的循环的约束:
    1)循环变量必须是有符号整型,如果是无符号整型,就无法使用
    2)比较操作必须是<,>,<=,>=
    3)循环步长必须是整数加或整数减操作,加减的操作必须是一个不变量
    4)如果是<,<=,循环变量的值每次迭代时必须增加,否则减小
    5)循环内部不允许有能够到达循环之外的跳转语句,也不允许有外部的跳转语句到达循环内部。exit语句例外,goto 和break的跳转范围必须在循环内部,异常处理也必须在循环内部处理
    4.
    数据相关(以下假设为语句S2与语句S1存在数据相关):
    相关的种类(相关不等于循环迭代相关):
    1)流相关:S1先写某一存储单元,而后S2又读该单元
    2)输出相关:两个语句写同一存储单元
    3)反相关:一个语句先读一单元,然后另一语句写该单元
    相关产生的方式:
    1)S1在循环的一次迭代中访问存储单元L,S2在随后的一次迭代中访问L(是循环迭代相关)
    2)S1和S2在同一循环迭代中访问同一存储单元L,但S1的执行在S2之前。(非循环迭代相关)
    5.
    数据竞争:
          数据竞争可能是由于输出相关引起的,编译器不会进行数据竞争的检测,Intel线程检测器可以检测数据竞争。
    用类似于互斥量的机制进行私有化和同步,可以消除数据竞争。
    #pragma omp parallel for private(x)
           for(i=0;i<80;i++)
           {
             x=sin(i);
             if(x>0.6)x=0.6;
             printf("sin(%d)=%fn",i,x); 
           }
    6.
    管理共享数据和私有数据:
    private:每个线程都拥有该变量的一个单独的副本,可以私有的访问
             1)private:说明列表中的每个变量对于每个线程都应该有一个私有副本。这个私有副本用变量的默认值进行初始化
             2)firstprivate:见13数据的Copy-in 和Copy-out
             3)lastprivate:见13数据的Copy-in 和Copy-out
             4)reduction:
             5)threadprivate:指定由每个线程私有的全局变量
    有三种方法声明存储单元为私有:
             1)使用private,firstprivate,lastprivate,reduction子句
             2)使用threadprivate
             3)在循环内声明变量,并且不使用static关键字
    shared:所有线程都能够访问该单元,并行区域内使用共享变量时,如果存在写操作,必须对共享变量加以保护
    default:并行区中所有变量都是共享的,除下列三种情况下:
              1)在parallel for循环中,循环索引时私有的。
              2)并行区中的局部变量是私有的
              3)所有在private,firstprivate,lastprivate,reduction子句中列出的变量是私有的
    7.
    循环调度与分块
         为了提供一种简单的方法以便能够在多个处理器之间调节工作负载,OpenMP给出了四种调度方案:
    static,dynamic,runtime,guided.
         默认情况下,OpenMP采用静态平均调度策略,但是可以通过调用schedule(kind[,chunksize])子句提供循环调度信息
    如:#pragma omp for schedule (kind[,chunk-size])   //chunk-size为块大小
    guided根据环境变量里的设置来进行对前三种的调度
    在windows环境中,可以在”系统属性|高级|环境变量”对话框中进行设置环境变量。
    8.
    有效地使用归约:
    sum=0;
    for(k=0;k<100;k++)
    {
        sum=sum+func(k);
    }
         为了完成这种形式的循环计算,其中的操作必须满足算术结合律和交换律,同时sum是共享的,这样循环内部都可以加给这个变量,同时又必须是私有的,以避免在相加时的数据竞争。
    reduction子句可以用来有效地合并一个循环中某些关于一个或多个变量的满足结合律的算术归约操作。reduction子句主要用来对一个或多个参数条目指定一个操作符,每个线程将创建参数条目的一个私有拷贝,在区域的结束处,将用私有拷贝的值通过指定的运行符运算,原始的参数条目被运算结果的值更新。
    sum=0;
    #pragma omp parallel for reduction(+:sum)
    for(k=0;k<100;k++)
    {
        sum=sum+func(k);
    }
    9.
    降低线程开销:当编译器生成的线程被执行时,循环的迭代将被分配给该线程,在并行区的最后,所有的线程都被挂起,等待共同进入下一个并行区、循环或结构化块。
                  如果并行区域、循环或结构化块是相邻的,那么挂起和恢复线程的开销就是没必要的。
    举例如下:
                    #pragma omp parallel //并行区内
                    {
                       #pragma omp for // 任务分配for循环
                              for(k=0;k<m;k++){
                                   fun1(k);
                               }
                       #pragma omp for
                              for(k=0;k<m;k++){
                                   fun2(k);
                               }
                    }
    10.任务分配区:
         现实中应用程序的所有性能敏感的部分不是都在一个并行区域内执行,所以OpenMP用任务分配区这种结构来处理非循环代码。
    任务分配区可以指导OpenMP编译器和运行时库将应用程序中标示出的结构化块分配到用于执行并行区域的一组线程上。
    举例如下:
                  #pragma omp parallel //并行区内
                    {
                       #pragma omp for // 任务分配for循环
                              for(k=0;k<m;k++){
                                   fun1(k);
                               }
                       #pragma omp sections private(y,z)
                         {
                               #pragme omp section//任务分配section
                                   {y=sectionA(x);}
                               #pragme omp section
                                   {z=sectionB(x);}
                                          
                    }
    11.
    使用Barrier和Nowait:
          栅障(Barrier)是OpenMP用于线程同步的一种方法。线程遇到栅障是必须等待,直到并行区中的所有线程都到达同一点。
    注意:在任务分配for循环和任务分配section结构中,我们已经隐含了栅障,在parallel,for,sections,single结构的最后,也会有一个隐式的栅障。
    隐式的栅障会使线程等到所有的线程继续完成当前的循环、结构化块或并行区,再继续执行后面的工作。可以使用nowait去掉这个隐式的栅障
    去掉隐式栅障,例如:
                    #pragma omp parallel //并行区内
                    {
                       #pragma omp for nowait // 任务分配for循环
                              for(k=0;k<m;k++){
                                   fun1(k);
                               }
                       #pragma omp sections private(y,z)
                         {
                               #pragme omp section//任务分配section
                                   {y=sectionA(x);}
                               #pragme omp section
                                   {z=sectionB(x);}
                                          
                    }
         因为第一个 任务分配for循环和第二个任务分配section代码块之间不存在数据相关。
    加上显示栅障,例如:
                                  #pragma omp parallel shared(x,y,z) num_threads(2)//使用的线程数为2
                                   {
                                       int tid=omp_get_thread_num();
                                       if(tid==0)
                                           y=fun1();//第一个线程得到y
                                       else 
                                            z=fun2();//第二个线程得到z
                                       #pragma omp barrier //显示加上栅障,保证y和z在使用前已有值
                                       #pragma omp for
                                               for(k=0;k<100;k++)
                                                       x[k]=y+z;
                                   }
    12.
    单线程和多线程交错执行:
          当开发人员为了减少开销而把并行区设置的很大时,有些代码很可能只执行一次,并且由一个线程执行,这样单线程和多线程需要交错执行
    举例如下:
                   #pragma omp parallel //并行区
                  {
                        int tid=omp_get_thread_num();//每个线程都调用这个函数,得到线程号
                         //这个循环被划分到多个线程上进行
                          #pragma omp for nowait
                          for(k=0;k<100;k++)
                                x[k]=fun1(tid);//这个循环的结束处不存在使所有线程进行同步的隐式栅障
                        #pragma omp master
                          y=fn_input_only(); //只有主线程会调用这个函数
                        #pragma omp barrier   //添加一个显示的栅障对所有的线程同步,从而确保x[0-99]和y处于就绪状态
                         //这个循环也被划分到多个线程上进行
                        #pragma omp for nowait
                          for(k=0;k<100;k++)
                             x[k]=y+fn2(x[k]); //这个线程没有栅障,所以不会相互等待
                         //一旦某个线程执行完上面的代码,不需要等待就可以马上执行下面的代码
                         #pragma omp single //注意:single后面意味着有隐式barrier
                         fn_single_print(y);
                          //所有的线程在执行下面的函数前会进行同步
                         #pragma omp master
                         fn_print_array(x);//只有主线程会调用这个函数
                 
    13.
    数据的Copy-in 和Copy-out:
          在并行化一个程序的时候,一般都必须考虑如何将私有变量的初值复制进来(Copy-in ),以初始化线程组中各个线程的私有副本。
    在并行区的最后,还要将最后一次迭代/结构化块中计算出的私有变量复制出来(Copy-out),复制到主线程中的原始变量中。
    firstprivate:使用变量在主线程的值对其在每个线程的对应私有变量进行初始化。一般来说,临时私有变量的初值是未定义的。
    lastprivate:可以将最后一次迭代/结构化块中计算出来的私有变量复制出来,复制到主线程对应的变量中,一个变量可以同时用firstprivate和lastprivate来声明。
    copyin:将主线程的threadprivate变量的值复制到执行并行区的每个线程的threadprivate变量中。
    copyprivate:使用一个私有变量将某一个值从一个成员线程广播到执行并行区的其他线程。该子句可以关联single结构(用于single指令中的指定变量为多个线程的共享变量),在所有的线程都离开该结构中的同步点之前,广播操作就已经完成。
    14.
    保护共享变量的更新操作:
         OpenMP支持critical和atomic编译指导,可以用于保护共享变量的更新,避免数据竞争。包含在某个临界段且由atomic编译指导所标记的代码块可能只由一个线程执行。
    例如:#pragma omp critical
       {
                  if(max<new_value) max=new_value;
             }
    15.
    OpenMP库函数(#include <omp.h>):
    int omp_get_num_threads(void); //获取当前使用的线程个数
    int omp_set_num_threads(int NumThreads);//设置要使用的线程个数
    int omp_get_thread_num(void);//返回当前线程号
    int omp_get_num_procs(void);//返回可用的处理核个数
    16.
       编译OpenMP要需要一个支持OpenMP的编译器和线程安全的运行时库。vs2005的配置属性C/C++语言里提供对OpenMP的支持。
       编译时假如出现"没有找到vcompd.dll,因此这个应用程序未能启动。重新安装应用程序可能会修复此问题",
    可能的原因是该项目有可能是从VC移植过来的,如果由VS创建,一般不会出现该问题,因为VS会解决在清单文件的调用dll问题。
    解决方法如下:
    StdAfx.h中加入 #pragma comment(linker, ""/manifestdependency:type='Win32' name='Microsoft.VC80.DebugOpenMP' version='8.0.50608.0' processorArchitecture='X86' publicKeyToken='1fc8b3b9a1e18e3b' language='*'"") 
    或者在Linker -> Manifest File -> Additional Manifest Dependencies -> 中加入:
    "type='Win32' name='Microsoft.VC80.DebugOpenMP' version='8.0.50608.0' processorArchitecture='X86' publicKeyToken='1fc8b3b9a1e18e3b' language='*'"
    展开全文
  • RT,本人菜鸟,在编制一个使用OPENMP的并行高斯约当消去法程序,其中第一步是选取主元(每一阶子阵中最大的元素),初等变换到对角线上。该功能目前应该是已经实现了。但现在遇到一个很奇怪的问题,无论是在并行区内...
  • openmp

    2017-06-01 16:19:37
    最近在看多核编程。简单来说,由于现在电脑CPU一般都有两个核,4核与8核的CPU也逐渐走入了寻常百姓家,传统的单线程编程方式难以发挥多核CPU的强大...这两天关注的多核编程的工具包括OpenMP和TBB。按照目前网上的讨论,

    最近在看多核编程。简单来说,由于现在电脑CPU一般都有两个核,4核与8核的CPU也逐渐走入了寻常百姓家,传统的单线程编程方式难以发挥多核CPU的强大功能,于是多核编程应运而生。按照我的理解,多核编程可以认为是对多线程编程做了一定程度的抽象,提供一些简单的API,使得用户不必花费太多精力来了解多线程的底层知识,从而提高编程效率。这两天关注的多核编程的工具包括OpenMP和TBB。按照目前网上的讨论,TBB风头要盖过OpenMP,比如OpenCV过去是使用openMP的,但从2.3版本开始抛弃OpenMP,转向TBB。但我试下来,TBB还是比较复杂的,相比之下,OpenMP则非常容易上手。因为精力和时间有限,没办法花费太多时间去学习TBB,就在这里分享下这两天学到的OpenMP的一点知识,和大家共同讨论。

    OpenMP支持的编程语言包括C语言、C++和Fortran,支持OpenMP的编译器包括Sun Studio,Intel Compiler,Microsoft Visual Studio,GCC。我使用的是Microsoft Visual Studio 2008,CPU为Intel i5 四核,首先讲一下在Microsoft Visual Studio 2008上OpenMP的配置。非常简单,总共分2步:

    (1) 新建一个工程。这个不再多讲。

    (2) 建立工程后,点击 菜单栏->Project->Properties,弹出菜单里,点击 Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜单里选择Yes。

    至此配置结束。下面我们通过一个小例子来说明openMP的易用性。这个例子是 有一个简单的test()函数,然后在main()里,用一个for循环把这个test()函数跑8遍。

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. #include <time.h>  
    3. void test()  
    4. {  
    5.     int a = 0;  
    6.     for (int i=0;i<100000000;i++)  
    7.         a++;  
    8. }  
    9. int main()  
    10. {  
    11.     clock_t t1 = clock();  
    12.     for (int i=0;i<8;i++)  
    13.         test();  
    14.     clock_t t2 = clock();  
    15.     std::cout<<"time: "<<t2-t1<<std::endl;  
    16. }  

    编译运行后,打印出来的耗时为:1.971秒。下面我们用一句话把上面代码变成多核运行。

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. #include <time.h>  
    3. void test()  
    4. {  
    5.     int a = 0;  
    6.     for (int i=0;i<100000000;i++)  
    7.         a++;  
    8. }  
    9. int main()  
    10. {  
    11.     clock_t t1 = clock();  
    12. #pragma omp parallel for  
    13.     for (int i=0;i<8;i++)  
    14.         test();  
    15.     clock_t t2 = clock();  
    16.     std::cout<<"time: "<<t2-t1<<std::endl;  
    17. }  

    编译运行后,打印出来的耗时为:0.546秒,几乎为上面时间的1/4。

    由此我们可以看到OpenMP的简单易用。在上面的代码里,我们一没有额外include头文件,二没有额外link库文件,只是在for循环前加了一句#pragma omp parallel for。而且这段代码在单核机器上,或者编译器没有将OpenMP设为Yes的机器上编译也不会报错,将自动忽略#pragma这行代码,然后按照传统单核串行的方式编译运行!我们唯一要多做的一步,是从C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.OPENMP和C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugOpenMP目录下分别拷贝vcomp90d.dll和vcomp90.dll文件到工程文件当前目录下。

    对上面代码按照我的理解做个简单的剖析。

    当编译器发现#pragma omp parallel for后,自动将下面的for循环分成N份,(N为电脑CPU核数),然后把每份指派给一个核去执行,而且多核之间为并行执行。下面的代码验证了这种分析。

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. int main()  
    3. {  
    4. #pragma omp parallel for  
    5.    for (int i=0;i<10;i++)  
    6.        std::cout<<i<<std::endl;  
    7.    return 0;  
    8. }  

    会发现控制台打印出了0 3 4 5 8 9 6 7 1 2。注意:因为每个核之间是并行执行,所以每次执行时打印出的顺序可能都是不一样的。

    下面我们来了谈谈竞态条件(race condition)的问题,这是所有多线程编程最棘手的问题。该问题可表述为,当多个线程并行执行时,有可能多个线程同时对某变量进行了读写操作,从而导致不可预知的结果。比如下面的例子,对于包含10个整型元素的数组a,我们用for循环求它各元素之和,并将结果保存在变量sum里。

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. int main()  
    3. {  
    4.     int sum = 0;  
    5.     int a[10] = {1,2,3,4,5,6,7,8,9,10};  
    6. #pragma omp parallel for  
    7.     for (int i=0;i<10;i++)  
    8.         sum = sum + a[i];  
    9.     std::cout<<"sum: "<<sum<<std::endl;  
    10.     return 0;  
    11. }  

    如果我们注释掉#pragma omp parallel for,让程序先按照传统串行的方式执行,很明显,sum = 55。但按照并行方式执行后,sum则会变成其他值,比如在某次运行过程中,sum = 49。其原因是,当某线程A执行sum = sum + a[i]的同时,另一线程B正好在更新sum,而此时A还在用旧的sum做累加,于是出现了错误。

    那么用OpenMP怎么实现并行数组求和呢?下面我们先给出一个基本的解决方案。该方案的思想是,首先生成一个数组sumArray,其长度为并行执行的线程的个数(默认情况下,该个数等于CPU的核数),在for循环里,让各个线程更新自己线程对应的sumArray里的元素,最后再将sumArray里的元素累加到sum里,代码如下

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. #include <omp.h>  
    3. int main(){  
    4.     int sum = 0;  
    5.     int a[10] = {1,2,3,4,5,6,7,8,9,10};  
    6.     int coreNum = omp_get_num_procs();//获得处理器个数  
    7.     int* sumArray = new int[coreNum];//对应处理器个数,先生成一个数组  
    8.     for (int i=0;i<coreNum;i++)//将数组各元素初始化为0  
    9.         sumArray[i] = 0;  
    10. #pragma omp parallel for  
    11.     for (int i=0;i<10;i++)  
    12.     {  
    13.         int k = omp_get_thread_num();//获得每个线程的ID  
    14.         sumArray[k] = sumArray[k]+a[i];  
    15.     }  
    16.     for (int i = 0;i<coreNum;i++)  
    17.         sum = sum + sumArray[i];  
    18.    std::cout<<"sum: "<<sum<<std::endl;  
    19.     return 0;  
    20. }  

    需要注意的是,在上面代码里,我们用omp_get_num_procs()函数来获取处理器个数,用omp_get_thread_num()函数来获得每个线程的ID,为了使用这两个函数,我们需要include <omp.h>。

    上面的代码虽然达到了目的,但它产生了较多的额外操作,比如要先生成数组sumArray,最后还要用一个for循环将它的各元素累加起来,有没有更简便的方式呢?答案是有,openMP为我们提供了另一个工具,归约(reduction),见下面代码:

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. int main(){  
    3.     int sum = 0;  
    4.     int a[10] = {1,2,3,4,5,6,7,8,9,10};  
    5. #pragma omp parallel for reduction(+:sum)  
    6.     for (int i=0;i<10;i++)  
    7.         sum = sum + a[i];  
    8.     std::cout<<"sum: "<<sum<<std::endl;  
    9.     return 0;  
    10. }  

    上面代码里,我们在#pragma omp parallel for 后面加上了 reduction(+:sum),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。

    reduction虽然很方便,但它只支持一些基本操作,比如+,-,*,&,|,&&,||等。有些情况下,我们既要避免race condition,但涉及到的操作又超出了reduction的能力范围,应该怎么办呢?这就要用到openMP的另一个工具,critical。来看下面的例子,该例中我们求数组a的最大值,将结果保存在max里。

    [cpp]  view plain  copy
    1. #include <iostream>  
    2. int main(){  
    3.     int max = 0;  
    4.     int a[10] = {11,2,33,49,113,20,321,250,689,16};  
    5. #pragma omp parallel for  
    6.     for (int i=0;i<10;i++)  
    7.     {  
    8.         int temp = a[i];  
    9. #pragma omp critical   
    10.         {  
    11.             if (temp > max)  
    12.                 max = temp;  
    13.         }  
    14.     }  
    15.     std::cout<<"max: "<<max<<std::endl;  
    16.     return 0;  
    17. }  

    上例中,for循环还是被自动分成N份来并行执行,但我们用#pragma omp critical将 if (temp > max) max = temp 括了起来,它的意思是:各个线程还是并行执行for里面的语句,但当你们执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。这样就避免了race condition问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。
    有了以上基本知识,对我来说做很多事情都足够了。下面我们来看一个具体的应用例,从硬盘读入两幅图像,对这两幅图像分别提取特征点,特征点匹配,最后将图像与匹配特征点画出来。理解该例子需要一些图像处理的基本知识,我不在此详细介绍。另外,编译该例需要opencv,我用的版本是2.3.1,关于opencv的安装与配置也不在此介绍。我们首先来看传统串行编程的方式。

    [cpp]  view plain  copy
    1. #include "opencv2/highgui/highgui.hpp"  
    2. #include "opencv2/features2d/features2d.hpp"  
    3. #include <iostream>  
    4. #include <omp.h>  
    5. int main( ){  
    6.     cv::SurfFeatureDetector detector( 400 );      
    7.     cv::SurfDescriptorExtractor extractor;  
    8.     cv::BruteForceMatcher<cv::L2<float> > matcher;  
    9.     std::vector< cv::DMatch > matches;  
    10.     cv::Mat im0,im1;  
    11.     std::vector<cv::KeyPoint> keypoints0,keypoints1;  
    12.     cv::Mat descriptors0, descriptors1;  
    13.     double t1 = omp_get_wtime( );  
    14.     //先处理第一幅图像  
    15.     im0 = cv::imread("rgb0.jpg", CV_LOAD_IMAGE_GRAYSCALE );  
    16.     detector.detect( im0, keypoints0);  
    17.     extractor.compute( im0,keypoints0,descriptors0);  
    18.     std::cout<<"find "<<keypoints0.size()<<"keypoints in im0"<<std::endl;  
    19.     //再处理第二幅图像  
    20.     im1 = cv::imread("rgb1.jpg", CV_LOAD_IMAGE_GRAYSCALE );  
    21.     detector.detect( im1, keypoints1);  
    22.     extractor.compute( im1,keypoints1,descriptors1);  
    23.     std::cout<<"find "<<keypoints1.size()<<"keypoints in im1"<<std::endl;  
    24.     double t2 = omp_get_wtime( );  
    25.     std::cout<<"time: "<<t2-t1<<std::endl;  
    26.     matcher.match( descriptors0, descriptors1, matches );  
    27.     cv::Mat img_matches;  
    28.     cv::drawMatches( im0, keypoints0, im1, keypoints1, matches, img_matches );   
    29.     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);  
    30.     cv::imshow( "Matches", img_matches );  
    31.     cv::waitKey(0);  
    32.     return 1;  
    33. }  

    很明显,读入图像,提取特征点与特征描述子这部分可以改为并行执行,修改如下:

    [cpp]  view plain  copy
    1. #include "opencv2/highgui/highgui.hpp"  
    2. #include "opencv2/features2d/features2d.hpp"  
    3. #include <iostream>  
    4. #include <vector>  
    5. #include <omp.h>  
    6. int main( ){  
    7.     int imNum = 2;  
    8.     std::vector<cv::Mat> imVec(imNum);  
    9.     std::vector<std::vector<cv::KeyPoint>>keypointVec(imNum);  
    10.     std::vector<cv::Mat> descriptorsVec(imNum);  
    11.     cv::SurfFeatureDetector detector( 400 );    cv::SurfDescriptorExtractor extractor;  
    12.     cv::BruteForceMatcher<cv::L2<float> > matcher;  
    13.     std::vector< cv::DMatch > matches;  
    14.     char filename[100];  
    15.     double t1 = omp_get_wtime( );  
    16. #pragma omp parallel for  
    17.     for (int i=0;i<imNum;i++){  
    18.         sprintf(filename,"rgb%d.jpg",i);  
    19.         imVec[i] = cv::imread( filename, CV_LOAD_IMAGE_GRAYSCALE );  
    20.         detector.detect( imVec[i], keypointVec[i] );  
    21.         extractor.compute( imVec[i],keypointVec[i],descriptorsVec[i]);  
    22.         std::cout<<"find "<<keypointVec[i].size()<<"keypoints in im"<<i<<std::endl;  
    23.     }  
    24.     double t2 = omp_get_wtime( );  
    25.     std::cout<<"time: "<<t2-t1<<std::endl;  
    26.     matcher.match( descriptorsVec[0], descriptorsVec[1], matches );  
    27.     cv::Mat img_matches;  
    28.     cv::drawMatches( imVec[0], keypointVec[0], imVec[1], keypointVec[1], matches, img_matches );   
    29.     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);  
    30.     cv::imshow( "Matches", img_matches );  
    31.     cv::waitKey(0);  
    32.     return 1;  
    33. }  

    两种执行方式做比较,时间为:2.343秒v.s. 1.2441秒

    在上面代码中,为了改成适合#pragma omp parallel for执行的方式,我们用了STL的vector来分别存放两幅图像、特征点与特征描述子,但在某些情况下,变量可能不适合放在vector里,此时应该怎么办呢?这就要用到openMP的另一个工具,section,代码如下:

    [cpp]  view plain  copy
    1. #include "opencv2/highgui/highgui.hpp"  
    2. #include "opencv2/features2d/features2d.hpp"  
    3. #include <iostream>  
    4. #include <omp.h>  
    5. int main( ){  
    6.     cv::SurfFeatureDetector detector( 400 );  
    7.     cv::SurfDescriptorExtractor extractor;  
    8.     cv::BruteForceMatcher<cv::L2<float> > matcher  
    9.     std::vector< cv::DMatch > matches;  
    10.     cv::Mat im0,im1;  
    11.     std::vector<cv::KeyPoint> keypoints0,keypoints1;  
    12.     cv::Mat descriptors0, descriptors1;  
    13.     double t1 = omp_get_wtime( );  
    14. #pragma omp parallel sections  
    15.     {  
    16. #pragma omp section  
    17.         {  
    18.             std::cout<<"processing im0"<<std::endl;  
    19.             im0 = cv::imread("rgb0.jpg", CV_LOAD_IMAGE_GRAYSCALE );  
    20.             detector.detect( im0, keypoints0);  
    21.             extractor.compute( im0,keypoints0,descriptors0);  
    22.             std::cout<<"find "<<keypoints0.size()<<"keypoints in im0"<<std::endl;  
    23.         }  
    24. #pragma omp section  
    25.         {  
    26.             std::cout<<"processing im1"<<std::endl;  
    27.             im1 = cv::imread("rgb1.jpg", CV_LOAD_IMAGE_GRAYSCALE );  
    28.             detector.detect( im1, keypoints1);  
    29.             extractor.compute( im1,keypoints1,descriptors1);  
    30.             std::cout<<"find "<<keypoints1.size()<<"keypoints in im1"<<std::endl;  
    31.         }  
    32.     }  
    33.     double t2 = omp_get_wtime( );  
    34.     std::cout<<"time: "<<t2-t1<<std::endl;  
    35.     matcher.match( descriptors0, descriptors1, matches );  
    36.     cv::Mat img_matches;  
    37.     cv::drawMatches( im0, keypoints0, im1, keypoints1, matches, img_matches );   
    38.     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);  
    39.     cv::imshow( "Matches", img_matches );  
    40.     cv::waitKey(0);  
    41.     return 1;  
    42. }  

    上面代码中,我们首先用#pragma omp parallel sections将要并行执行的内容括起来,在它里面,用了两个#pragma omp section,每个里面执行了图像读取、特征点与特征描述子提取。将其简化为伪代码形式即为:

    [cpp]  view plain  copy
    1. #pragma omp parallel sections  
    2. {  
    3.     #pragma omp section  
    4.     {  
    5.         function1();  
    6.     }  
    7.     #pragma omp section  
    8.     {  
    9.         function2();  
    10.     }  
    11. }  

    意思是:parallel sections里面的内容要并行执行,具体分工上,每个线程执行其中的一个section,如果section数大于线程数,那么就等某线程执行完它的section后,再继续执行剩下的section。在时间上,这种方式与人为用vector构造for循环的方式差不多,但无疑该种方式更方便,而且在单核机器上或没有开启openMP的编译器上,该种方式不需任何改动即可正确编译,并按照单核串行方式执行。

    以上分享了这两天关于openMP的一点学习体会,其中难免有错误,欢迎指正。另外的一点疑问是,看到各种openMP教程里经常用到private,shared等来修饰变量,这些修饰符的意义和作用我大致明白,但在我上面所有例子中,不加这些修饰符似乎并不影响运行结果,不知道这里面有哪些讲究。

    参考:http://www.cnblogs.com/lexus/archive/2012/08/21/2648424.html

    展开全文
  • openMP

    2013-06-08 23:32:24
    最近在看多核编程。简单来说,由于现在电脑CPU一般都有两个核,4核与8核的CPU也逐渐走入了寻常百姓家,传统的单线程编程方式难以发挥多核CPU的强大...这两天关注的多核编程的工具包括openMP和TBB。按照目前网上的...

    最近在看多核编程。简单来说,由于现在电脑CPU一般都有两个核,4核与8核的CPU也逐渐走入了寻常百姓家,传统的单线程编程方式难以发挥多核CPU的强大功能,于是多核编程应运而生。按照我的理解,多核编程可以认为是对多线程编程做了一定程度的抽象,提供一些简单的API,使得用户不必花费太多精力来了解多线程的底层知识,从而提高编程效率。这两天关注的多核编程的工具包括openMP和TBB。按照目前网上的讨论,TBB风头要盖过openMP,比如openCV过去是使用openMP的,但从2.3版本开始抛弃openMP,转向TBB。但我试下来,TBB还是比较复杂的,相比之下,openMP则非常容易上手。因为精力和时间有限,没办法花费太多时间去学习TBB,就在这里分享下这两天学到的openMP的一点知识,和大家共同讨论。

    openMP支持的编程语言包括C语言、C++和Fortran,支持OpenMP的编译器包括Sun Studio,Intel Compiler,Microsoft Visual Studio,GCC。我使用的是Microsoft Visual Studio 2008,CPU为Intel i5 四核,首先讲一下在Microsoft Visual Studio 2008上openMP的配置。非常简单,总共分2步:

    (1) 新建一个工程。这个不再多讲。

    (2) 建立工程后,点击 菜单栏->Project->Properties,弹出菜单里,点击 Configuration Properties->C/C++->Language->OpenMP Support,在下拉菜单里选择Yes。

    至此配置结束。下面我们通过一个小例子来说明openMP的易用性。这个例子是 有一个简单的test()函数,然后在main()里,用一个for循环把这个test()函数跑8遍。

    复制代码
     1 #include <iostream>
     2 #include <time.h>
     3 void test()
     4 {
     5     int a = 0;
     6     for (int i=0;i<100000000;i++)
     7         a++;
     8 }
     9 int main()
    10 {
    11     clock_t t1 = clock();
    12     for (int i=0;i<8;i++)
    13         test();
    14     clock_t t2 = clock();
    15     std::cout<<"time: "<<t2-t1<<std::endl;
    16 }
    复制代码

    编译运行后,打印出来的耗时为:1.971秒。下面我们用一句话把上面代码变成多核运行。

    复制代码
     1 #include <iostream>
     2 #include <time.h>
     3 void test()
     4 {
     5     int a = 0;
     6     for (int i=0;i<100000000;i++)
     7         a++;
     8 }
     9 int main()
    10 {
    11     clock_t t1 = clock();
    12     #pragma omp parallel for
    13     for (int i=0;i<8;i++)
    14         test();
    15     clock_t t2 = clock();
    16     std::cout<<"time: "<<t2-t1<<std::endl;
    17 }
    复制代码

    编译运行后,打印出来的耗时为:0.546秒,几乎为上面时间的1/4。

    由此我们可以看到openMP的简单易用。在上面的代码里,我们一没有额外include头文件,二没有额外link库文件,只是在for循环前加了一句#pragma omp parallel for。而且这段代码在单核机器上,或者编译器没有将openMP设为Yes的机器上编译也不会报错,将自动忽略#pragma这行代码,然后按照传统单核串行的方式编译运行!我们唯一要多做的一步,是从C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.OPENMP和C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugOpenMP目录下分别拷贝vcomp90d.dll和vcomp90.dll文件到工程文件当前目录下。

    对上面代码按照我的理解做个简单的剖析。

    当编译器发现#pragma omp parallel for后,自动将下面的for循环分成N份,(N为电脑CPU核数),然后把每份指派给一个核去执行,而且多核之间为并行执行。下面的代码验证了这种分析。

    复制代码
    1 #include <iostream>
    2 int main()
    3 {
    4 #pragma omp parallel for
    5     for (int i=0;i<10;i++)
    6         std::cout<<i<<std::endl;
    7     return 0;
    8 }
    复制代码

    会发现控制台打印出了0 3 4 5 8 9 6 7 1 2。注意:因为每个核之间是并行执行,所以每次执行时打印出的顺序可能都是不一样的。

    下面我们来了谈谈竞态条件(race condition)的问题,这是所有多线程编程最棘手的问题。该问题可表述为,当多个线程并行执行时,有可能多个线程同时对某变量进行了读写操作,从而导致不可预知的结果。比如下面的例子,对于包含10个整型元素的数组a,我们用for循环求它各元素之和,并将结果保存在变量sum里。

    复制代码
     1 #include <iostream>
     
    2 int main()
     
    3 {
     
    4     int sum = 0;
     
    5     int a[10] = {1,2,3,4,5,6,7,8,9,10};
     
    6 #pragma omp parallel for
     
    7     for (int i=0;i<10;i++)
     
    8         sum = sum + a[i];
     
    9     std::cout<<"sum: "<<sum<<std::endl;
    
    10     return 0;
    
    11 }
    复制代码

    如果我们注释掉#pragma omp parallel for,让程序先按照传统串行的方式执行,很明显,sum = 55。但按照并行方式执行后,sum则会变成其他值,比如在某次运行过程中,sum = 49。其原因是,当某线程A执行sum = sum + a[i]的同时,另一线程B正好在更新sum,而此时A还在用旧的sum做累加,于是出现了错误。

    那么用openMP怎么实现并行数组求和呢?下面我们先给出一个基本的解决方案。该方案的思想是,首先生成一个数组sumArray,其长度为并行执行的线程的个数(默认情况下,该个数等于CPU的核数),在for循环里,让各个线程更新自己线程对应的sumArray里的元素,最后再将sumArray里的元素累加到sum里,代码如下

    复制代码
     1 #include <iostream>
     2 #include <omp.h>
     3 int main(){
     4     int sum = 0;
     5     int a[10] = {1,2,3,4,5,6,7,8,9,10};
     6     int coreNum = omp_get_num_procs();//获得处理器个数
     7     int* sumArray = new int[coreNum];//对应处理器个数,先生成一个数组
     8     for (int i=0;i<coreNum;i++)//将数组各元素初始化为0
     9         sumArray[i] = 0;
    10 #pragma omp parallel for
    11     for (int i=0;i<10;i++)
    12     {
    13         int k = omp_get_thread_num();//获得每个线程的ID
    14         sumArray[k] = sumArray[k]+a[i];
    15     }
    16     for (int i = 0;i<coreNum;i++)
    17         sum = sum + sumArray[i];
    18     std::cout<<"sum: "<<sum<<std::endl;
    19     return 0;
    20 }
    复制代码

    需要注意的是,在上面代码里,我们用omp_get_num_procs()函数来获取处理器个数,用omp_get_thread_num()函数来获得每个线程的ID,为了使用这两个函数,我们需要include <omp.h>。

    上面的代码虽然达到了目的,但它产生了较多的额外操作,比如要先生成数组sumArray,最后还要用一个for循环将它的各元素累加起来,有没有更简便的方式呢?答案是有,openMP为我们提供了另一个工具,归约(reduction),见下面代码:

    复制代码
     1 #include <iostream>
     2 int main(){
     3     int sum = 0;
     4     int a[10] = {1,2,3,4,5,6,7,8,9,10};
     5 #pragma omp parallel for reduction(+:sum)
     6     for (int i=0;i<10;i++)
     7         sum = sum + a[i];
     8     std::cout<<"sum: "<<sum<<std::endl;
     9     return 0;
    10 }
    复制代码

    上面代码里,我们在#pragma omp parallel for 后面加上了 reduction(+:sum),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。

    reduction虽然很方便,但它只支持一些基本操作,比如+,-,*,&,|,&&,||等。有些情况下,我们既要避免race condition,但涉及到的操作又超出了reduction的能力范围,应该怎么办呢?这就要用到openMP的另一个工具,critical。来看下面的例子,该例中我们求数组a的最大值,将结果保存在max里。

    复制代码
     1 #include <iostream>
     2 int main(){
     3     int max = 0;
     4     int a[10] = {11,2,33,49,113,20,321,250,689,16};
     5 #pragma omp parallel for
     6     for (int i=0;i<10;i++)
     7     {
     8         int temp = a[i];
     9 #pragma omp critical
    10         {
    11             if (temp > max)
    12                 max = temp;
    13         }
    14     }
    15     std::cout<<"max: "<<max<<std::endl;
    16     return 0;
    17 }
    复制代码

    上例中,for循环还是被自动分成N份来并行执行,但我们用#pragma omp critical将 if (temp > max) max = temp 括了起来,它的意思是:各个线程还是并行执行for里面的语句,但当你们执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。这样就避免了race condition问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。
    有了以上基本知识,对我来说做很多事情都足够了。下面我们来看一个具体的应用例,从硬盘读入两幅图像,对这两幅图像分别提取特征点,特征点匹配,最后将图像与匹配特征点画出来。理解该例子需要一些图像处理的基本知识,我不在此详细介绍。另外,编译该例需要opencv,我用的版本是2.3.1,关于opencv的安装与配置也不在此介绍。我们首先来看传统串行编程的方式。

    复制代码
     1 #include "opencv2/highgui/highgui.hpp"
     2 #include "opencv2/features2d/features2d.hpp"
     3 #include <iostream>
     4 #include <omp.h>
     5 int main( ){
     6     cv::SurfFeatureDetector detector( 400 );    
     7     cv::SurfDescriptorExtractor extractor;
     8     cv::BruteForceMatcher<cv::L2<float> > matcher;
     9     std::vector< cv::DMatch > matches;
    10     cv::Mat im0,im1;
    11     std::vector<cv::KeyPoint> keypoints0,keypoints1;
    12     cv::Mat descriptors0, descriptors1;
    13     double t1 = omp_get_wtime( );
    14     //先处理第一幅图像
    15     im0 = cv::imread("rgb0.jpg", CV_LOAD_IMAGE_GRAYSCALE );
    16     detector.detect( im0, keypoints0);
    17     extractor.compute( im0,keypoints0,descriptors0);
    18     std::cout<<"find "<<keypoints0.size()<<"keypoints in im0"<<std::endl;
    19     //再处理第二幅图像
    20     im1 = cv::imread("rgb1.jpg", CV_LOAD_IMAGE_GRAYSCALE );
    21     detector.detect( im1, keypoints1);
    22     extractor.compute( im1,keypoints1,descriptors1);
    23     std::cout<<"find "<<keypoints1.size()<<"keypoints in im1"<<std::endl;
    24     double t2 = omp_get_wtime( );
    25     std::cout<<"time: "<<t2-t1<<std::endl;
    26     matcher.match( descriptors0, descriptors1, matches );
    27     cv::Mat img_matches;
    28     cv::drawMatches( im0, keypoints0, im1, keypoints1, matches, img_matches ); 
    29     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);
    30     cv::imshow( "Matches", img_matches );
    31     cv::waitKey(0);
    32     return 1;
    33 }
    复制代码

    很明显,读入图像,提取特征点与特征描述子这部分可以改为并行执行,修改如下:

    复制代码
     1 #include "opencv2/highgui/highgui.hpp"
     2 #include "opencv2/features2d/features2d.hpp"
     3 #include <iostream>
     4 #include <vector>
     5 #include <omp.h>
     6 int main( ){
     7     int imNum = 2;
     8     std::vector<cv::Mat> imVec(imNum);
     9     std::vector<std::vector<cv::KeyPoint>>keypointVec(imNum);
    10     std::vector<cv::Mat> descriptorsVec(imNum);
    11     cv::SurfFeatureDetector detector( 400 );    cv::SurfDescriptorExtractor extractor;
    12     cv::BruteForceMatcher<cv::L2<float> > matcher;
    13     std::vector< cv::DMatch > matches;
    14     char filename[100];
    15     double t1 = omp_get_wtime( );
    16 #pragma omp parallel for
    17     for (int i=0;i<imNum;i++){
    18         sprintf(filename,"rgb%d.jpg",i);
    19         imVec[i] = cv::imread( filename, CV_LOAD_IMAGE_GRAYSCALE );
    20         detector.detect( imVec[i], keypointVec[i] );
    21         extractor.compute( imVec[i],keypointVec[i],descriptorsVec[i]);
    22         std::cout<<"find "<<keypointVec[i].size()<<"keypoints in im"<<i<<std::endl;
    23     }
    24     double t2 = omp_get_wtime( );
    25     std::cout<<"time: "<<t2-t1<<std::endl;
    26     matcher.match( descriptorsVec[0], descriptorsVec[1], matches );
    27     cv::Mat img_matches;
    28     cv::drawMatches( imVec[0], keypointVec[0], imVec[1], keypointVec[1], matches, img_matches ); 
    29     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);
    30     cv::imshow( "Matches", img_matches );
    31     cv::waitKey(0);
    32     return 1;
    33 }
    复制代码

    两种执行方式做比较,时间为:2.343秒v.s. 1.2441秒

    在上面代码中,为了改成适合#pragma omp parallel for执行的方式,我们用了STL的vector来分别存放两幅图像、特征点与特征描述子,但在某些情况下,变量可能不适合放在vector里,此时应该怎么办呢?这就要用到openMP的另一个工具,section,代码如下:

    复制代码
     1 #include "opencv2/highgui/highgui.hpp"
     2 #include "opencv2/features2d/features2d.hpp"
     3 #include <iostream>
     4 #include <omp.h>
     5 int main( ){
     6     cv::SurfFeatureDetector detector( 400 );    cv::SurfDescriptorExtractor extractor;
     7     cv::BruteForceMatcher<cv::L2<float> > matcher;
     8     std::vector< cv::DMatch > matches;
     9     cv::Mat im0,im1;
    10     std::vector<cv::KeyPoint> keypoints0,keypoints1;
    11     cv::Mat descriptors0, descriptors1;
    12     double t1 = omp_get_wtime( );
    13 #pragma omp parallel sections
    14     {
    15 #pragma omp section
    16         {
    17             std::cout<<"processing im0"<<std::endl;
    18             im0 = cv::imread("rgb0.jpg", CV_LOAD_IMAGE_GRAYSCALE );
    19             detector.detect( im0, keypoints0);
    20             extractor.compute( im0,keypoints0,descriptors0);
    21             std::cout<<"find "<<keypoints0.size()<<"keypoints in im0"<<std::endl;
    22         }
    23 #pragma omp section
    24         {
    25             std::cout<<"processing im1"<<std::endl;
    26             im1 = cv::imread("rgb1.jpg", CV_LOAD_IMAGE_GRAYSCALE );
    27             detector.detect( im1, keypoints1);
    28             extractor.compute( im1,keypoints1,descriptors1);
    29             std::cout<<"find "<<keypoints1.size()<<"keypoints in im1"<<std::endl;
    30         }
    31     }
    32     double t2 = omp_get_wtime( );
    33     std::cout<<"time: "<<t2-t1<<std::endl;
    34     matcher.match( descriptors0, descriptors1, matches );
    35     cv::Mat img_matches;
    36     cv::drawMatches( im0, keypoints0, im1, keypoints1, matches, img_matches ); 
    37     cv::namedWindow("Matches",CV_WINDOW_AUTOSIZE);
    38     cv::imshow( "Matches", img_matches );
    39     cv::waitKey(0);
    40     return 1;
    41 }
    复制代码

    上面代码中,我们首先用#pragma omp parallel sections将要并行执行的内容括起来,在它里面,用了两个#pragma omp section,每个里面执行了图像读取、特征点与特征描述子提取。将其简化为伪代码形式即为:

    复制代码
     1 #pragma omp parallel sections
     2 {
     3     #pragma omp section
     4     {
     5         function1();
     6     }
     7   #pragma omp section
     8     {
     9         function2();
    10     }
    11 }
    复制代码

    意思是:parallel sections里面的内容要并行执行,具体分工上,每个线程执行其中的一个section,如果section数大于线程数,那么就等某线程执行完它的section后,再继续执行剩下的section。在时间上,这种方式与人为用vector构造for循环的方式差不多,但无疑该种方式更方便,而且在单核机器上或没有开启openMP的编译器上,该种方式不需任何改动即可正确编译,并按照单核串行方式执行。

    以上分享了这两天关于openMP的一点学习体会,其中难免有错误,欢迎指正。另外的一点疑问是,看到各种openMP教程里经常用到private,shared等来修饰变量,这些修饰符的意义和作用我大致明白,但在我上面所有例子中,不加这些修饰符似乎并不影响运行结果,不知道这里面有哪些讲究。

    在写上文的过程中,参考了包括以下两个网址在内的多个地方的资源,不再一 一列出,在此一并表示感谢。

    http://blog.csdn.net/drzhouweiming/article/details/4093624
    http://software.intel.com/zh-cn/articles/more-work-sharing-with-openmp

     
     
     
    展开全文
  • OpenMp并行编程 程序

    2011-06-02 09:26:07
    该工程包括三个文件:stdafa.h;OpenMp.cpp;stdafx.cpp。...实现的功能是:在一个元素个数为10000的随机数组中(元素值为0—9之间),在不同的线程数时,查找某一个数字出现的次数和查找所用到的时间。
  • OpenMP】常用的OpenMP子句

    千次阅读 2019-04-10 18:01:00
    子句是构造的修饰和补充。不同的构造支持不同的子句组合。 1. collapse子句 collapse(n)表示紧随其后的 n 层循环会被合并然后并行化。...单独使用OpenMP线程化内层循环都会存在负载不够的问题...
  • OpenMP 收藏

    2018-10-31 11:12:27
    OpenMP是一种API,用于编写可移植的多线程应用程序,无需程序员进行复杂的线程创建、同步、负载平衡和销毁工作。 使用OpenMP的好处:  1)CPU核数扩展性问题  2)方便性问题  3)可移植性问题 OpenMP指令和库函数...
  • OpenMP框架是使用C,C ++和Fortran进行并发编程的强大方法。 GNU编译器集合(GCC)4.2版支持OpenMP 2.5标准,而GCC 4.4支持最新的OpenMP 3标准。 其他编译器,包括Microsoft®Visual Studio,也支持OpenMP。 在本文...
  • OpenMP实现数据统计

    2020-09-20 16:02:58
    编写OpenMP程序,数组里存放大量浮点数,数据分布在05之间,为了对数据分布有一个更为直观的感受,对数据进行统计,01之间、1~2之间…分别有多少浮点数。 #include <stdio.h> #include <stdlib.h> #...
  • OpenMP框架是使用C,C ++和Fortran进行并发编程的强大方法。 GNU编译器集合(GCC)4.2版支持OpenMP 2.5标准,而GCC 4.4支持最新的OpenMP 3标准。 其他编译器,包括Microsoft®Visual Studio,也支持OpenMP。 在本文...
  • OpenMP 入门

    2020-01-13 21:11:31
    前两天(其实是几个月以前了)看到了代码中有#pragma omp parallel for一段,感觉好像是 OpenMP,以前看到并行化的东西都是直接躲开,既然躲不开了,不妨研究一下: OpenMP 是 Open MultiProcessing 的缩写。OpenMP ...
  • OpenMP学习笔记

    2020-09-14 09:59:40
    OpenMP学习笔记 标签(空格分隔): 学习笔记 文章目录OpenMP学习笔记Hello World程序调控Openmp的行为num_threads(编译指示)omp_set_num_threads(运行API)环境变量来调整(环境变量)一个实例 Hello World程序...
  • OpenMp编程

    千次阅读 2014-07-03 01:53:02
    OpenMP发展与优势 OpenMP多线程编程基础 编译指导语句运行时库函数使用Visual StudioNet 2005编写OpenMP程序 OpenMP多线程应用程序编程技术 循环并行化并行区域编程线程同步OpenMP多线程应用程序性能分析 ...
  • 以下是代码片段 bool **comMatrix; int stemCount; #pragma omp parallel num_threads(2) firstprivate(comMatrix,stemCount) ... initInd(comMatrix,stemCount);...查看comMatrix的地址发现,两个线程中这个变量...
  • OpenMP编译器指令

    2021-02-11 03:00:18
    OpenMP通过在串行程序中插入编译制导指令, 来实现并行化, 支持OpenMP的编译器可以识别, 处理这些指令并实现对应的功能. 所有的编译制导指令都是以#pragma omp开始, 后面跟具体的功能指令(directive)或者命令. 一般...
  • openmp矩阵乘法

    千次阅读 2019-06-27 15:41:04
    目录 1. 实验内容与方法 2. 实验过程 2.1 运行时间 2.2 加速比 ...初始化数组。初始化三个double矩阵matrix_a,matrix_b...数组中的值使用c++11中的random类随机生成0到1之间的double值。 程序计时。使用c++11中的...
  • OpenMP学习

    千次阅读 2014-12-10 16:50:23
    http://openmp.org/wp/ 传统的单线程编程方式难以发挥多核CPU的强大功能,于是多核编程...多核编程的工具有OpenMP和TBB,OpenMP支持的编程语言包括C、C++和Fortran,支持OpenMP的编译器包括Sun Studio、Intel Co...
  • 其中包括许多循环融合优化,以及在工作数组上使用标量变量。 总体而言,这提高了缓存效率。 此版本还包含对显式切片的一些支持。 这是通过两个输入卡座参数激活的: tiles_per_chunk指定每个MPI等级有多少个瓦片...
  • OpenMP程序设计基础 首先,我们说了OpenMP在Linux下的编译/运行方法 g++ -fopenmp -o hello.o hello.cpp ./hello.o 接着,我们聊到了OpenMP程序结构,与C语言相同,由main函数开始,return 0为结束(整体来看为...
  • Openmp的Demo,包含循环、线程、数组并行的功能实现
  • 对“多个数组排序”的任务不均衡案例进行OpenMP编程实现,并探索不同循环调度方案的优劣。从任务分块的大小、线程数的多少、静态动态多线程结合等方面进行尝试,探索规律。
  • OpenMP知识点汇总

    万次阅读 2013-12-11 21:23:27
    OpenMP知识点汇总
  • OpenMP并行编程

    2019-10-09 04:47:47
    OpenMP并行编程 什么是OpenMP?“OpenMP (Open Multi-Processing) is an application programming interface (API) that supports multi-platform sha...
  • OpenMP学习总结

    2018-03-30 10:08:23
    OpenMP是一种API,是共享存储体系结构的一个并行编程模型,适合SMP共享内存多处理系 统和多核处理器体系结构。1.SMP:共享内存并行机(Shared Memory Processors)2.DSM:分布共享存储并行机(Distributed Shared ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,587
精华内容 634
关键字:

openmp数组