openMP 函數總結(并行程序設計導論)

本篇文章只是記錄api的用法和回顧,方便記憶

openMP

openMP提供“基于指令”的共享內存API。這就意味著在c和c++中,有一些特殊的預處理指令pragma。在系統中加入預處理指令一般時用來允許不是基本C語言的規范的行為。
不支持pragma的編譯器會忽略pragma指令提示的那些語句,這樣就允許使用pragma的程序在不支持它的平臺上運行

  • OpenMP的pragma總是以 ##pragma omp 開始
簡單例子
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void Hello(void);
int main(int argc,char* argv[])
{       
         /*
             long strtol( 
                        const char* number_p *in*, 第一個參數是字符串
                        const char** end_p  *out*,終止的非法字符串 
                        int     base  *in* 進制(2-36)
                        )
            
            例:
            char buffer[20]="10379cend$3";
            char *stop;
            printf("%d\n",strtol(buffer, &stop, 2));
            printf("%s\n", stop);
            輸出結果:
            2
            379cend$3
        */
        int thread_count = strtol(argv[1],NULL,10);
#pragma omp parallel num_threads(thread_count)
        Hello();
        
        return 0;
}

void Hello(void)
{
    int my_rank = omp_get_thread_num();
    int thread_count = omp_get_num_threads();

    printf("hello from thread %d of %d \n",my_rank,thread_count);
}


#編譯
gcc -g -Wall -fopenmp -o main main.c

#-g :產生供gdb調試用的可執行文件
# http://www.lxweimin.com/p/30ffc01380a0
#-Wall:編譯后顯示所有警告
#-fopenmp 使用mpi支持
#-o:輸出到指定文件


#pragma omp pallel
  • 使用parallel是用來表明之后的結構化代碼塊(一個結構化代碼塊時一條C語句或者只有一個入口一個出口的一組復合C語句)應該被多個線程并行執行。
  • 完成代碼塊前會有一個隱式路障,先完成的線程必須等待線程組其他線程完成代碼塊。
- num_threads 子句
  1. 允許程序員指定執行后代碼塊的線程數
  2. 程序可以啟動的線程數可能會受系統定義的限制。OpenMP標準并不保證實際能夠啟動thread_count個線程
#pragma omp parallel num_threads(thread_count)
  • 線程被同一個進程派生,這些線程共享大部分資源。有它自己的計數器。當一個線程完成了執行,它就又合并到啟動它的線程中。
  • 每個線程都有它自己的棧,所以執行一個代碼塊將在代碼塊內創建自己的私有局部變量
-func omp_get_thread_num | omp_get_num_threads
#獲得當前線程的編號
int omp_get_thread_num(void)
#獲得線程數量
int omp_get_num_threads(void)
錯誤檢查

可以通過預處理宏_OPENMP是否定義。

#ifdef _OPENMP
#include<omp.h>
#endif

#ifdef _OPENMP
    int my_rank=omp_get_thread_num();
    int thread_count=omp_get_num_threads();
#else
    int my_rank=0;
    int thread_count=1;
#endif
#pragma omp critical
  • 只有一個線程能夠執行對應代碼塊,并且第一個線程完成操作前,沒有其他的線程能夠開始執行這段代碼。
  • 當不添加name時,OpenMP默認做法將所有臨界區代碼塊作為符合臨界區一部分,添加name后兩個不同名字的cirtical指令保護的代碼可以同時執行
語法
#pragma omp critical [(name)]
用法
#pragma omp critical
global_result += my_result;
變量的作用域
  • 在parallel塊之前被聲明的變量的缺省作用域時共享的。
  • parallel指令前已經被聲明的變量,擁有線程組中所有線程間的共享作用域,而在塊中聲明的變量(例如,函數中的變量)中有私有作用域
- reduction 規約子句
語法
reduction(<operator>:<variable list>)
# operator : +,*,-,&,|,^,&&,||
用法
  • 當一個變量包含在一個reduction子句中時,變量本身是共享的。然而,線程組中的每個線程都創建自己的私有變量。在parallel塊里,每當一個線程執行涉及這個變量(共享變量)的語句時,它使用的其實時私有變量。當parallel塊執行結束后,私有變量中的值被整合到一個共享變量中。
  • 如果一個規約變量時floatdouble變量型數據,那么當使用不同數量的線程時,結果可能有些許不同。這是由于浮點數運算不滿足結合律
  • OpenMp會為此創建一個臨界區,并且在這個臨界區中,將存儲在私有變量中的值進行相加(或其他operator)。
global_result=0.0;
#pragma omp parallel num_threads(thread_count) reduction(+:global_result)
global_result += Local_trap(double a,double b,int n);

####等同

global_result=0.0;
#pragma omp parallel num_threads(thread_count) 
{
    double my_result =0.0;/*私有變量*/
    my_result += Local_trap(double a,double b,int n);
#pragma omp critial
    global_result += Local_trap(double a,double b,int n);
}

#pragma omp parallel for
  • parallel for 指令生成一組線程來執行后面的結構化代碼塊(必須是for循環)。
  • 系統通過在線程間劃分循環迭代來并行化for循環。與parallel指令非常不同,因為在parallel指令之前的塊,一般來說其工作必須由線程本身在線程之間劃分。
  • 在一個已經被parallel for指令并行化的for循環中,線程間的缺省劃分方式由系統決定(大約 m(迭代次數)/thread_count)。
  • 在一個被parallel for指令并行化的循環中,循環變量的缺省作用域是私有的,每個線程會有它自己的循環變量副本
合法方式
h=(b-a)/n;
approx =(f(a)+f(b))/2.0;
# pragma omp parallel for num_threads(thread_count) reduction(+:=approx)
approx += f(a+i*h);
approx = h* approx;
線程重用
  • 與parallel指令不同的是,for指令并不創建任何線程。它使用已經在parallel塊中創建的線程。在循環的末尾有一個隱式的路障
#pragma omp parallel num_threads(thread_count) default(none) \ 
  shared(a,n) private(i,tmp,phase)
for(phase = 0;phase<n;phase++)
{
    if(phase%2 == 0)
      #pargma omp for
      for(i=1;i<n;i++)
        ...
    else
      #pargma omp for
      for(i=1;i<n;i++)
        ...
}  
數據依賴性
  • OpenMP編譯器不檢查parallel for指令并行化的循環所包含的迭代間的依賴關系,而是由程序員來識別這些依賴。
  • 一個或更多個迭代結果依賴于其他迭代的循環,一般不能被OpenMP正確地并行化。
數據依賴
#y依賴于x
for(i=0;i<n;i++)
{
    x[i]=a+i*h;
    y[i]=exp(x[i]);
}
循環依賴

一個值在循環中計算,其結果在之后迭代中使用。

#并行化后某一個邊界值將是另一個并行化線程中的使用。
fibo[0]=fibo[1]=1;
for(i=2;i<n;i++)
    fibo[i]=fibo[i-1]+fibo[i-2];
- private 子句
  • 在private子句列舉的變量,在每個線程上都有一個私有副本被創建
  • 一個私有作用域的變量的值在parallel塊或者parallel for塊的開始處是未指定的。它的值在parallel塊或者parallel for塊完成之后也是未指定的。
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>

void Hello(void);
int main(int argc,char* argv[])
{

    int x=5;

    #pragma omp parallel private(x)
    {
        int my_rank =omp_get_thread_num();
        printf("Thread %d > before initialization,x=%d \n",my_rank,x);
        x=2*my_rank+2;
        printf("Thread %d > after initialization,x=%d \n",my_rank,x);
    }

    printf("after parallel,x=%d \n",x);
        return 0;
}
- default(none) 子句
  • 讓程序員明確塊中每個變量的作用域。
double sum = 0.0;
/*
sum是一個規約變量(同時擁有私有和共享作用域的屬性)。
*/
#pragma omp parallel for num_threads(thread_count) \
  default(none) redcution(+:sum) private(k,factor) \
  shared(n)
  for(k=0;k<n;k++)
    if(k%2 ==0)
        factor = 1.0;
    else
        factor = -1.0;
    sum += factor/(2*K+1);
- schedule子句

對線程進行調度。

語法
schedule(<type> [,<chunsize>]

type可以時一下的任意一個。

  • static。迭代能夠在循環執行前分配給線程。
(static,1)
Thread0:0,3,6,9
Thread1:1,4,7,10
Thread2:2,5,8,11

(static,2)
Thread0:0,1,6,7
Thread1:2,3,8,9
Thread2:4,5,10,11

缺省調度(static,total_iterations/thread_count)
  • dynamic或guided。迭代在循環執行時被分配給線程,因此在一個線程完成了它的當前迭代集合后,他能從運行時系統中請求更多。
dynamic調度中,迭代也被分成chunksize個連續迭代的塊。
每個線程執行一塊,并且當一個線程完成一塊時,
他將從運行時系統請求另一塊,直到所有的迭代完成。
chunksize可以被忽略。當它被忽略時,chunksize為1。
在guided調度中,每個線程也執行一塊,并且當一個線程完成一塊,將請求另一塊。
然而,在guided調度中,當塊完成后,新塊的大小變小。
例如:
n=10 000并且thread_count=2時,迭代將如表那樣分配。塊的大小近似等于的迭代數除以線程數。第一塊的大小為9999/2 ~=5000,因為9999個未被分配的迭代。第二塊的大小為4999/2~=2500。以此類推。

| 線程 |    塊    | 快的大小 | 剩下的迭代代數 |
| 0   | 1~5000   | 5000 | 4999 |
| 1   | 5001-7500| 2500 | 2499 |
| 1   | 7501-8750| 1250 | 1249 |
...
  • auto。編譯器和運行時系統決定調度方式。

  • runtime。調度在運行時決定。
    chunksize是一個正整數。在OpenMP中,迭代塊在順序循環中連續執行的一塊迭代語句,塊中的迭代次數時chunsize。只有static,dynamic和guided調度有chunksize。

設置環境變量
$export OMP_SCHEDULE="static,1"
#pragma omp barrier
  • 顯式的路障,當所有的線程都到達了這個路障時,這些線程就可以接著往下執行。
#pragma omp atomic
  • 只能保護由一條C語言賦值語句所形成的臨界區,是一個更高效的指令

語句必須是以下形式:

#op:+,*,-,/,&,^,|,<<,or >> .
#expreesion不能引用x。
x<op>=<expreesion>;
x++;
++x;
x--;
y--;

用法
#其他線程對x的更新必須等到該線程對x的更新結束之后。
#但對y不受保護,因此程序的結果是不可預測的。
#pragma omp atomic
  x+=y++
簡單鎖
  • 第一個函數初始化鎖,所以鎖此時處于解鎖狀態。
  • 第二個函數嘗試獲得鎖,如果成功,調用該函數的線程可以繼續執行,如果失敗調用該函數的線程被阻塞,直到鎖被其他線程釋放。
  • 第三個函數釋放鎖,以便其他線程獲得該鎖。
  • 第四個函數銷貨鎖。
void omp_init_lock(omp_lock_t*  lock_p  /*out*/);
void omp_set_lock(omp_lock_t*  lock_p  /*in/out*/);
void omp_unset_lock(omp_lock_t*  lock_p  /*in/out*/);
void omp_destroy_lock(omp_lock_t*  lock_p  /*in/out*/);
用法
static omp_lock_t lock;   
void test11()  
{  
    omp_init_lock(&lock); // 初始化互斥鎖    
  
#pragma omp parallel for    
    for (int i = 0; i < 5; ++i)     
    {    
        omp_set_lock(&lock); //獲得互斥器     
        std::cout << omp_get_thread_num() << "+" << std::endl;    
        std::cout << omp_get_thread_num() << "-" << std::endl;    
        omp_unset_lock(&lock); //釋放互斥器    
    }    
    omp_destroy_lock(&lock); //銷毀互斥器    
}  
#pragma omp single

這樣做能確保接下來的結構化代碼塊由線程組中的一個線程執行,而組內其他線程等待直到該線程執行結束(在代碼塊的最后設置一個隱式路障)

#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int main()
{

#pragma omp parallel
{
int my_rank = omp_get_thread_num();
if(my_rank == 1)
{
   int x=1;
   while(x<1e9)
    {
        x+=1;
    }
}
#pragma omp single
    printf("%d \n",my_rank);
printf("----> %d \n",my_rank);

}
    return 0;
}

#pragma omp master

這樣能確保線程0執行接下來的結構化代碼塊。然后master指令在最后不會設置隱式路障

-func omp_get_wtime

獲取運行時間。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容