openMP多线程编程

简介:

OpenMP(Open Muti-Processing) 

OpenMP缺点:

1:作为高层抽象,OpenMp并不适合需要复杂的线程间同步和互斥的场合;

2:另一个缺点是不能在非共享内存系统(如计算机集群)上使用。在这样的系统上,MPI使用较多。

关于openMP实现 临界区 与互斥锁 可参考 reference3

windows系统下使用

==========================WINDOWS系统中使用==========================

基本使用:

在visual C++2010中使用OpenMP

1:将 Project 的Properties中C/C++里Language的OpenMP Support开启(参数为 /openmp);

2:在编写使用OpenMP 的程序时,则需要先include OpenMP的头文件:omp.h;

3:在要并行化的for循环前面加上  #pragma omp parallel for

如下简单例子:

[cpp]  view plain  copy
 
  1. //未使用OpenMP  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4.   
  5. void Test(int n) {  
  6. for(int i = 0; i < 10000; ++i)   
  7. {  
  8.       //do nothing, just waste time  
  9. }  
  10.     printf("%d, ", n);  
  11. }  
  12.   
  13. int main(int argc,char* argv[])   
  14. {  
  15.     for(int i = 0; i < 16; ++i)  
  16.     Test(i);  
  17.     system("pause");  
  18. }  

结果为:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,11,12,13,14,15,

[cpp]  view plain  copy
 
  1. //使用OpenMP  
  2. <pre name="code" class="cpp">#include <stdio.h>  
  3. #include <stdlib.h>  
  4. #include <omp.h>  
  5.   
  6. void Test(int n) {  
  7. for(int i = 0; i < 10000; ++i) {  
  8. //do nothing, just waste time  
  9. }  
  10.     printf("%d, ", n);  
  11. }  
  12.   
  13. int main(int argc,char* argv[])   
  14. {  
  15. #pragma omp parallel for  
  16.     for(int i = 0; i < 16; ++i)  
  17.     Test(i);  
  18.     system("pause");  
  19. }  
 

(我的笔记本为2核 4线程)

显示结果为:

0,12,4,8,1,13,5,9,2,14,6,10,3,15,7,11,

OpenMP将循环0-15拆分成0-3,4-7,8-11,12-15四个部分来执行。

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

关于获取CPU核数与线程ID

[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();//获得处理器个数(其实获取的是线程的数量,我的笔记本为2核4线程,测试时获取的数字为4)</span>  
  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 = <span style="color:#3366FF;">omp_get_thread_num();//获得每个线程的ID</span>  
  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. }  

 Ubuntu系统中使用

=================ubuntu系统中=====================================

Hands on FAQ:

*怎么在Linux上运行OpenMP程序? 
> 只需要安装支持OpenMP的编译器即可,比如GCC 4.2以上版本(好像Fedora Core带的部分4.1版本也支持),或者ICC(我用的version 9.1是支持的,其他没试过)。

*怎么缺点编译器是不是支持OpenMP? 
> 看编译器安装路径下/include目录里有没有omp.h。

*怎么区分OpenMP程序? 
> 程序中有没有以下内容: 
> #include <omp.h> 
> #pragma omp ...

*怎么编译OpenMP程序? 
> gcc -fopenmp [sourcefile]   -o [destination file] 
> icc   -openmp   [sourcefile]   -o [destination file]

*怎么运行OpenMP程序? 
> 编译后得到的文件和普通可执行文件一样可以直接执行。

*怎么设置线程数? 
>:在程序中写入set_num_threads(n); 
Method2:export OMP_NUM_THREADS=n; 
> 两种方法各有用处,前者只对该程序有效,后者不用重新编译就可以修改线程数。

 

Example1:并行与串行时间差别

Sequetial Version:

[cpp]  view plain  copy
 
  1. #include<iostream>  
  2. #include<sys/time.h>  
  3. #include<unistd.h>  
  4.   
  5. using namespace std;  
  6.   
  7. void test(int n)  
  8. {     
  9.     int a=0;  
  10.     struct timeval tstart,tend;  
  11.     double timeUsed;  
  12.     gettimeofday(&tstart,NULL);  
  13.     for(int i=0;i<1000000000;i++)  
  14.     {  
  15.         a=i+1;  
  16.     }  
  17.     gettimeofday(&tend,NULL);  
  18.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  19.     cout<<n<<" Time="<<timeUsed/1000<<" ms"<<endl;  
  20. }  
  21. int main()  
  22. {  
  23.     struct timeval tstart,tend;  
  24.     double timeUsed;  
  25.     gettimeofday(&tstart,NULL);  
  26.     int j=0;  
  27.     for(j=0;j<4;j++)  
  28.     {  
  29.         test(j);  
  30.     }  
  31.     gettimeofday(&tend,NULL);  
  32.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  33.     cout<<" Total Time="<<timeUsed/1000<<" ms"<<endl;  
  34.     return 0;  
  35. }  

Parallel Version:

[cpp]  view plain  copy
 
  1. #include<iostream>  
  2. #include<sys/time.h>  
  3. #include<unistd.h>  
  4. #include<omp.h>  
  5.   
  6. using namespace std;  
  7.   
  8. void test(int n)  
  9. {     
  10.     int a=0;  
  11.     struct timeval tstart,tend;  
  12.     double timeUsed;  
  13.     gettimeofday(&tstart,NULL);  
  14.     for(int i=0;i<1000000000;i++)  
  15.     {  
  16.         a=i+1;  
  17.     }  
  18.     gettimeofday(&tend,NULL);  
  19.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  20.     cout<<n<<" Time="<<timeUsed/1000<<" ms"<<endl;  
  21. }  
  22. int main()  
  23. {  
  24.     struct timeval tstart,tend;  
  25.     double timeUsed;  
  26.     gettimeofday(&tstart,NULL);  
  27.     int j=0;  
  28. #pragma omp parallel for  
  29.     for(j=0;j<4;j++)  
  30.     {  
  31.         test(j);  
  32.     }  
  33.     gettimeofday(&tend,NULL);  
  34.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  35.     cout<<" Total Time="<<timeUsed/1000<<" ms"<<endl;  
  36.     return 0;  

Result:

Sequential version:

[cpp]  view plain  copy
 
  1. 0 Time=2064.69 ms  
  2. 1 Time=2061.11 ms  
  3. 2 Time=2076.32 ms  
  4. 3 Time=2077.93 ms  
  5.  Total Time=8280.14 ms  


Parallel version:

 

 

[cpp]  view plain  copy
 
  1. 2 Time=2148.22 ms  
  2. 3 Time=2151.72 ms  
  3. 0 Time=2151.85 ms  
  4. 1 Time=2151.77 ms  
  5.  Total Time=2158.81 ms  

------------------------------------------------------------------------------------------------------------------------------------------------------------

Example2:矩阵拟合法计算Pi  

Sequential Version:

[cpp]  view plain  copy
 
  1. #include<iostream>  
  2. #include<sys/time.h>  
  3. #include<unistd.h>  
  4. //#include <omp.h>  
  5.   
  6. using namespace std;  
  7.   
  8. int main ()  
  9. {  
  10.     struct timeval tstart,tend;  
  11.     double timeUsed;  
  12.     static long num_steps =1000000000;  
  13.     double step;  
  14.     int i;  
  15.     double x, pi, sum = 0.0;  
  16.     step = 1.0/(double) num_steps;  
  17.     gettimeofday(&tstart,NULL);  
  18. //#pragma omp parallel for reduction(+:sum) private(x) /*只加了这一句,其他不变*/  
  19.     for (i=0;i < num_steps; i++)  
  20.     {  
  21.         x = (i+0.5)*step;  
  22.         sum = sum + 4.0/(1.0+x*x);  
  23.     }  
  24.     pi = step * sum;  
  25.     gettimeofday(&tend,NULL);  
  26.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  27.     timeUsed=timeUsed/1000;  
  28.     cout<<"pi="<<pi<<"  ("<<num_steps<<" )   "<<timeUsed<<" ms"<<endl;  
  29.     return 0;  
  30. }  

Parallel Version:

[cpp]  view plain  copy
 
  1. #include<iostream>  
  2. #include<sys/time.h>  
  3. #include<unistd.h>  
  4. #include <omp.h>  
  5.   
  6. using namespace std;  
  7.   
  8. int main ()  
  9. {  
  10.     struct timeval tstart,tend;  
  11.     double timeUsed;  
  12.     static long num_steps = 1000000000;  
  13.     double step;  
  14.     int i;  
  15.     double x, pi, sum = 0.0;  
  16.     step = 1.0/(double) num_steps;  
  17.     gettimeofday(&tstart,NULL);  
  18. #pragma omp parallel for reduction(+:sum) private(x) /*只加了这一句,其他不变*/  
  19.     for (i=0;i < num_steps; i++)  
  20.     {  
  21.         x = (i+0.5)*step;  
  22.         sum = sum + 4.0/(1.0+x*x);  
  23.     }  
  24.     pi = step * sum;  
  25.     gettimeofday(&tend,NULL);  
  26.     timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;  
  27.     timeUsed=timeUsed/1000;  
  28.     cout<<"pi="<<pi<<"  ("<<num_steps<<" )   "<<timeUsed<<" ms"<<endl;  
  29.     return 0;  
  30. }  

运行结果为:

[cpp]  view plain  copy
 
  1. von@von-pc:~/test$ ./parrPI2  
  2. pi=3.14159  (1000000000 )   3729.68 ms  
  3. von@von-pc:~/test$ ./seqPI2  
  4. pi=3.14159  (1000000000 )   13433.1 ms  

我的电脑为2核,4线程 提升速度为13433/3739=3.6 。因为这个程序本身具有良好的并发性,循环间几乎没有数据依赖,除了sum,但是用reduction(+:sum)把对于sum的相关也消除了。

关于reduction , private具体请到references 7中查看。

需要特别注意的一点是: 

上述的计时方法使用的是gettimeofday() 而原博客给出的计时方法是time_t (使用time_t是没法达到作者所说的速度的,你会发现 并行的时间比串行还慢)。

主要原因:计时方法不一样,具体请看两者的区别(另一篇博客)

reference:

1:http://baike.baidu.com/view/1687659.htm

2:http://www.cnblogs.com/yangyangcv/archive/2012/03/23/2413335.html

-----------------------------------------------------------------------------------------------------------------

3:http://www.ibm.com/developerworks/cn/aix/library/au-aix-openmp-framework/index.html

4:http://openmp.org/wp/openmp-compilers/(官网)

5:http://blog.163.com/zl_dream1106/blog/static/84286020105210012295/   (linux 系统中OpenMP)

6:http://blog.163.com/zl_dream1106/blog/static/842860201052952352/?suggestedreading&wumiiOpenMP编程指南)

7:http://blog.163.com/zl_dream1106/blog/static/84286020105293213869/?suggestedreading&wumiiOpenMP 入门)

本文转自博客园知识天地的博客,原文链接:openMP多线程编程,如需转载请自行联系原博主。

相关文章
|
10月前
|
Java API 微服务
为什么虚拟线程将改变Java并发编程?
为什么虚拟线程将改变Java并发编程?
443 83
|
7月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
293 6
|
12月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
423 0
|
8月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
522 16
|
7月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
706 0
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
287 26
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
316 17
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
891 2

热门文章

最新文章