1. 程式人生 > >openMP多線程編程

openMP多線程編程

時間差 ostream lan 系統 1.8 內存 total hand 缺點

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. void Test(int n) {
  5. for(int i = 0; i < 10000; ++i)
  6. {
  7. //do nothing, just waste time
  8. }
  9. printf("%d, ", n);
  10. }
  11. int main(int argc,char* argv[])
  12. {
  13. for(int i = 0; i < 16; ++i)
  14. Test(i);
  15. system("pause");
  16. }

結果為:

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. void Test(int n) {
  6. for(int i = 0; i < 10000; ++i) {
  7. //do nothing, just waste time
  8. }
  9. printf("%d, ", n);
  10. }
  11. int main(int argc,char* argv[])
  12. {
  13. #pragma omp parallel for
  14. for(int i = 0; i < 16; ++i)
  15. Test(i);
  16. system("pause");
  17. }

(我的筆記本為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. using namespace std;
  5. void test(int n)
  6. {
  7. int a=0;
  8. struct timeval tstart,tend;
  9. double timeUsed;
  10. gettimeofday(&tstart,NULL);
  11. for(int i=0;i<1000000000;i++)
  12. {
  13. a=i+1;
  14. }
  15. gettimeofday(&tend,NULL);
  16. timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;
  17. cout<<n<<" Time="<<timeUsed/1000<<" ms"<<endl;
  18. }
  19. int main()
  20. {
  21. struct timeval tstart,tend;
  22. double timeUsed;
  23. gettimeofday(&tstart,NULL);
  24. int j=0;
  25. for(j=0;j<4;j++)
  26. {
  27. test(j);
  28. }
  29. gettimeofday(&tend,NULL);
  30. timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;
  31. cout<<" Total Time="<<timeUsed/1000<<" ms"<<endl;
  32. return 0;
  33. }


Parallel Version:

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


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. using namespace std;
  6. int main ()
  7. {
  8. struct timeval tstart,tend;
  9. double timeUsed;
  10. static long num_steps =1000000000;
  11. double step;
  12. int i;
  13. double x, pi, sum = 0.0;
  14. step = 1.0/(double) num_steps;
  15. gettimeofday(&tstart,NULL);
  16. //#pragma omp parallel for reduction(+:sum) private(x) /*只加了這一句,其他不變*/
  17. for (i=0;i < num_steps; i++)
  18. {
  19. x = (i+0.5)*step;
  20. sum = sum + 4.0/(1.0+x*x);
  21. }
  22. pi = step * sum;
  23. gettimeofday(&tend,NULL);
  24. timeUsed=1000000*(tend.tv_sec-tstart.tv_sec)+tend.tv_usec-tstart.tv_usec;
  25. timeUsed=timeUsed/1000;
  26. cout<<"pi="<<pi<<" ("<<num_steps<<" ) "<<timeUsed<<" ms"<<endl;
  27. return 0;
  28. }

Parallel Version:

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

運行結果為:

[cpp] view plain copy
  1. [email protected]:~/test$ ./parrPI2
  2. pi=3.14159 (1000000000 ) 3729.68 ms
  3. [email protected]:~/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&wumii(OpenMP編程指南)

7:http://blog.163.com/zl_dream1106/blog/static/84286020105293213869/?suggestedreading&wumii(OpenMP 入門)

openMP多線程編程