visual studio C++ 使用OpenMP 進行並行計算


https://blog.csdn.net/dengm155/article/details/78836447?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

 

那么用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問題,但顯而易見,它的執行速度會變低,因為可能存在線程等待的情況。
第二部分轉載於:http://www.cnblogs.com/wzyj/p/4501348.html

OpenMp之sections用法

section語句是用在sections語句里用來將sections語句里的代碼划分成幾個不同的段
#pragma omp [parallel] sections [子句] 
{ 
   #pragma omp section 
   { 
            代碼塊 
   }  
} 
     當存在可選參數#pragma omp parallel sections時,塊中的代碼section才會並行處理,而#pragma omp  sections是串行的程序。詳見下面的代碼: 

#include<stdio.h>

#include<stdlib.h>

#include<omp.h>

#include <unistd.h>

int main()

{

 

 

   printf("parent threadid:%d\n",omp_get_thread_num());

   #pragma omp  sections

   {

     #pragma omp section

     {

          printf("section 0,threadid=%d\n",omp_get_thread_num());

          sleep(1);

     }

     #pragma omp section

     {

          printf("section 1,threadid=%d\n",omp_get_thread_num());

          //sleep(1);

     }

     #pragma omp section

     {

          printf("section 2,threadid=%d\n",omp_get_thread_num());

          sleep(1);

     }

   }

   #pragma omp parallel sections

   {

      #pragma omp section

     {

          printf("section 3,threadid=%d\n",omp_get_thread_num());

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 4,threadid=%d\n",omp_get_thread_num());

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 5,threadid=%d\n",omp_get_thread_num());

          sleep(1);

     }

   }

 

 return 0;

}
輸出結果為: 

parent threadid:0
section 0,threadid=0
section 1,threadid=0
section 2,threadid=0
section 3,threadid=0
section 4,threadid=2
section 5,threadid=1

針對上面的代碼,首先應該明確下面幾點:
   1. sections之間是串行的。主線程把section0~2執行完之后才執行的第二個sections。
   2.沒有加parallel的sections里面的section是串行的,為此我專門sleep了一秒,結果顯示0~2都是主線程做的。
   3.第二個sections里面是並行的,進程編號分別為0,21。
問題來了,第二部分的0是不是主線程呢?還是第二部分新開的一個線程?為此需要真正輸出每個線程在內核中的線程編號:

#include<stdio.h>

#include<stdlib.h>

#include<omp.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/syscall.h>

 

int main()

{

 

   printf("pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));

   #pragma omp sections

   {

     #pragma omp section

     {

          printf("section 0,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

     #pragma omp section

     {

          printf("section 1,tid=%ld\n",syscall(SYS_gettid));

          //sleep(1);

     }

     #pragma omp section

     {

          printf("section 2,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

   }

   #pragma omp parallel sections

   {

      #pragma omp section

     {

          printf("section 3,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 4,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 5,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

   }

 

 return 0;

}
輸出結果:
 
pid:7619,tid=7619
section 0,tid=7619
section 1,tid=7619
section 2,tid=7619
section 5,tid=7621
section 4,tid=7619
section 3,tid=7620
從結果中可以看出以下幾點:
OpenMP上說當程序執行到第二個sections是並行的,主線程是休眠的,一直等所有的子線程都執行完畢之后才喚醒,可是在第二個sections中有個線程id和主線程id一致?其實是不一致的,首先從兩者的類型上來看,線程編號是long int的,而進程是int的,數字一致並不能說兩者相同。另外一方面,在linuxthreads時代,線程稱為輕量級進程(LWP),也就是每個線程就是個進程,每個線程(進程)ID不同;而從2.4.10后,采用NPTL(Native Posix Thread Library)的線程庫, 各個線程同樣是通過fork實現的,並且具備同一個父進程。
主進程id為7619,同時它又有個線程id也是7619,又一次證明在linux中線程進程差別不大。
猜測主線程並不是休眠,而是將原先的上下文保存,然后自身也作為並行的一份子進行並行程序的執行,當並行程序完成之后,再回復原先的上下文信息。
下面是一個比較復雜的例子

#include<stdio.h>

#include<stdlib.h>

#include<omp.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/syscall.h>

 

int main()

{

#pragma omp parallel

{

   printf("pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));

   #pragma omp sections

   {

     #pragma omp section

     {

          printf("section 0,tid=%ld\n",syscall(SYS_gettid));

          //sleep(1);

     }

     #pragma omp section

     {

          printf("section 1,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

     #pragma omp section

     {

          printf("section 2,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

   }

   #pragma omp sections

   {

      #pragma omp section

     {

          printf("section 3,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 4,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

      #pragma omp section

     {

          printf("section 5,tid=%ld\n",syscall(SYS_gettid));

          sleep(1);

     }

   }

}

 

 return 0;

}

輸出結果:
 
pid:7660,tid=7660
section 0,tid=7660
section 1,tid=7660
pid:7660,tid=7662
section 2,tid=7662
pid:7660,tid=7663
pid:7660,tid=7661
section 3,tid=7660
section 5,tid=7661
section 4,tid=7662
 
#pragma omp parallel里面的代碼是並行處理的,但是並不意味着代碼要執行N次(其中N為核數),sections之間是串行的,而並行的實際部分是sections內部的代碼。當線程7660在處理0,1時,因為section1休眠1s,所以section2在此期間會被新的線程進行處理。第一個sections真正處理完成之后,第二個sections才開始並行處理。
另外值得注意的是,printf並不是並行的函數,它是將結果輸出到控制台中,可是控制台資源並不是共享的。當被某個線程占用之后,其余的線程只能等待,拿輸出的結果為例。對於#pragma omp parallel里面的代碼是並行的,可是線程之間還是有先后的次序的,次序和線程的創建時間有關,對於線程7660來說,本身就已經存在了,所以首先獲得printf函數,而直到它執行section0里面的printf時,其他的線程還沒有創建完畢,接着是setion1里面的printf,即使是這個時候有其他的線程創建完成了,也只能等待,在section1中,sleep了1秒鍾,printf函數被新的線程使用,下面也如此。

 

那么用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問題,但顯而易見,它的執行速度會變低,因為可能存在線程等待的情況。

第二部分轉載於:http://www.cnblogs.com/wzyj/p/4501348.html

 

OpenMp之sections用法

 

section語句是用在sections語句里用來將sections語句里的代碼划分成幾個不同的段

#pragma omp [parallel] sections [子句]
{
   #pragma omp section
   {
            代碼塊
   } 
}
     當存在可選參數#pragma omp parallel sections時,塊中的代碼section才會並行處理,而#pragma omp  sections是串行的程序。詳見下面的代碼:
  1.  
    #include<stdio.h>
  2.  
    #include<stdlib.h>
  3.  
    #include<omp.h>
  4.  
    #include <unistd.h>
  5.  
    int main()
  6.  
    {
  7.  
     
  8.  
     
  9.  
    printf( "parent threadid:%d\n",omp_get_thread_num());
  10.  
    #pragma omp sections
  11.  
    {
  12.  
    #pragma omp section
  13.  
    {
  14.  
    printf( "section 0,threadid=%d\n",omp_get_thread_num());
  15.  
    sleep( 1);
  16.  
    }
  17.  
    #pragma omp section
  18.  
    {
  19.  
    printf( "section 1,threadid=%d\n",omp_get_thread_num());
  20.  
    //sleep(1);
  21.  
    }
  22.  
    #pragma omp section
  23.  
    {
  24.  
    printf( "section 2,threadid=%d\n",omp_get_thread_num());
  25.  
    sleep( 1);
  26.  
    }
  27.  
    }
  28.  
    #pragma omp parallel sections
  29.  
    {
  30.  
    #pragma omp section
  31.  
    {
  32.  
    printf( "section 3,threadid=%d\n",omp_get_thread_num());
  33.  
    sleep( 1);
  34.  
    }
  35.  
    #pragma omp section
  36.  
    {
  37.  
    printf( "section 4,threadid=%d\n",omp_get_thread_num());
  38.  
    sleep( 1);
  39.  
    }
  40.  
    #pragma omp section
  41.  
    {
  42.  
    printf( "section 5,threadid=%d\n",omp_get_thread_num());
  43.  
    sleep( 1);
  44.  
    }
  45.  
    }
  46.  
     
  47.  
    return 0;
  48.  
    }

輸出結果為:

 

parent threadid:0
section 0,threadid=0
section 1,threadid=0
section 2,threadid=0
section 3,threadid=0
section 4,threadid=2
section 5,threadid=1

 

針對上面的代碼,首先應該明確下面幾點:

   1. sections之間是串行的。主線程把section0~2執行完之后才執行的第二個sections。

   2.沒有加parallel的sections里面的section是串行的,為此我專門sleep了一秒,結果顯示0~2都是主線程做的。

   3.第二個sections里面是並行的,進程編號分別為0,2,1。

問題來了,第二部分的0是不是主線程呢?還是第二部分新開的一個線程?為此需要真正輸出每個線程在內核中的線程編號:

  1.  
    #include<stdio.h>
  2.  
    #include<stdlib.h>
  3.  
    #include<omp.h>
  4.  
    #include <unistd.h>
  5.  
    #include <sys/types.h>
  6.  
    #include <sys/syscall.h>
  7.  
     
  8.  
    int main()
  9.  
    {
  10.  
     
  11.  
    printf( "pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));
  12.  
    #pragma omp sections
  13.  
    {
  14.  
    #pragma omp section
  15.  
    {
  16.  
    printf( "section 0,tid=%ld\n",syscall(SYS_gettid));
  17.  
    sleep( 1);
  18.  
    }
  19.  
    #pragma omp section
  20.  
    {
  21.  
    printf( "section 1,tid=%ld\n",syscall(SYS_gettid));
  22.  
    //sleep(1);
  23.  
    }
  24.  
    #pragma omp section
  25.  
    {
  26.  
    printf( "section 2,tid=%ld\n",syscall(SYS_gettid));
  27.  
    sleep( 1);
  28.  
    }
  29.  
    }
  30.  
    #pragma omp parallel sections
  31.  
    {
  32.  
    #pragma omp section
  33.  
    {
  34.  
    printf( "section 3,tid=%ld\n",syscall(SYS_gettid));
  35.  
    sleep( 1);
  36.  
    }
  37.  
    #pragma omp section
  38.  
    {
  39.  
    printf( "section 4,tid=%ld\n",syscall(SYS_gettid));
  40.  
    sleep( 1);
  41.  
    }
  42.  
    #pragma omp section
  43.  
    {
  44.  
    printf( "section 5,tid=%ld\n",syscall(SYS_gettid));
  45.  
    sleep( 1);
  46.  
    }
  47.  
    }
  48.  
     
  49.  
    return 0;
  50.  
    }

輸出結果:

復制代碼
pid:7619,tid=7619
section 0,tid=7619
section 1,tid=7619
section 2,tid=7619
section 5,tid=7621
section 4,tid=7619
section 3,tid=7620

從結果中可以看出以下幾點:

  1. OpenMP上說當程序執行到第二個sections是並行的,主線程是休眠的,一直等所有的子線程都執行完畢之后才喚醒,可是在第二個sections中有個線程id和主線程id一致?其實是不一致的,首先從兩者的類型上來看,線程編號是long int的,而進程是int的,數字一致並不能說兩者相同。另外一方面,在linuxthreads時代,線程稱為輕量級進程(LWP),也就是每個線程就是個進程,每個線程(進程)ID不同;而從2.4.10后,采用NPTL(Native Posix Thread Library)的線程庫, 各個線程同樣是通過fork實現的,並且具備同一個父進程。
  2. 主進程id為7619,同時它又有個線程id也是7619,又一次證明在linux中線程進程差別不大。
  3. 猜測主線程並不是休眠,而是將原先的上下文保存,然后自身也作為並行的一份子進行並行程序的執行,當並行程序完成之后,再回復原先的上下文信息。

下面是一個比較復雜的例子

  1.  
    #include<stdio.h>
  2.  
    #include<stdlib.h>
  3.  
    #include<omp.h>
  4.  
    #include <unistd.h>
  5.  
    #include <sys/types.h>
  6.  
    #include <sys/syscall.h>
  7.  
     
  8.  
    int main()
  9.  
    {
  10.  
    #pragma omp parallel
  11.  
    {
  12.  
    printf( "pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));
  13.  
    #pragma omp sections
  14.  
    {
  15.  
    #pragma omp section
  16.  
    {
  17.  
    printf( "section 0,tid=%ld\n",syscall(SYS_gettid));
  18.  
    //sleep(1);
  19.  
    }
  20.  
    #pragma omp section
  21.  
    {
  22.  
    printf( "section 1,tid=%ld\n",syscall(SYS_gettid));
  23.  
    sleep( 1);
  24.  
    }
  25.  
    #pragma omp section
  26.  
    {
  27.  
    printf( "section 2,tid=%ld\n",syscall(SYS_gettid));
  28.  
    sleep( 1);
  29.  
    }
  30.  
    }
  31.  
    #pragma omp sections
  32.  
    {
  33.  
    #pragma omp section
  34.  
    {
  35.  
    printf( "section 3,tid=%ld\n",syscall(SYS_gettid));
  36.  
    sleep( 1);
  37.  
    }
  38.  
    #pragma omp section
  39.  
    {
  40.  
    printf( "section 4,tid=%ld\n",syscall(SYS_gettid));
  41.  
    sleep( 1);
  42.  
    }
  43.  
    #pragma omp section
  44.  
    {
  45.  
    printf( "section 5,tid=%ld\n",syscall(SYS_gettid));
  46.  
    sleep( 1);
  47.  
    }
  48.  
    }
  49.  
    }
  50.  
     
  51.  
    return 0;
  52.  
    }

 

輸出結果:

復制代碼
pid:7660,tid=7660
section 0,tid=7660
section 1,tid=7660
pid:7660,tid=7662
section 2,tid=7662
pid:7660,tid=7663
pid:7660,tid=7661
section 3,tid=7660
section 5,tid=7661
section 4,tid=7662
復制代碼

#pragma omp parallel里面的代碼是並行處理的,但是並不意味着代碼要執行N次(其中N為核數),sections之間是串行的,而並行的實際部分是sections內部的代碼。當線程7660在處理0,1時,因為section1休眠1s,所以section2在此期間會被新的線程進行處理。第一個sections真正處理完成之后,第二個sections才開始並行處理。

另外值得注意的是,printf並不是並行的函數,它是將結果輸出到控制台中,可是控制台資源並不是共享的。當被某個線程占用之后,其余的線程只能等待,拿輸出的結果為例。對於#pragma omp parallel里面的代碼是並行的,可是線程之間還是有先后的次序的,次序和線程的創建時間有關,對於線程7660來說,本身就已經存在了,所以首先獲得printf函數,而直到它執行section0里面的printf時,其他的線程還沒有創建完畢,接着是setion1里面的printf,即使是這個時候有其他的線程創建完成了,也只能等待,在section1中,sleep了1秒鍾,printf函數被新的線程使用,下面也如此。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM