openMP 函數(shù)總結(jié)(并行程序設(shè)計(jì)導(dǎo)論)

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

openMP

openMP提供“基于指令”的共享內(nèi)存API拌牲。這就意味著在c和c++中,有一些特殊的預(yù)處理指令pragma撮珠。在系統(tǒng)中加入預(yù)處理指令一般時(shí)用來(lái)允許不是基本C語(yǔ)言的規(guī)范的行為沮脖。
不支持pragma的編譯器會(huì)忽略pragma指令提示的那些語(yǔ)句,這樣就允許使用pragma的程序在不支持它的平臺(tái)上運(yùn)行芯急。

  • OpenMP的pragma總是以 ##pragma omp 開(kāi)始
簡(jiǎn)單例子
#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*, 第一個(gè)參數(shù)是字符串
                        const char** end_p  *out*,終止的非法字符串 
                        int     base  *in* 進(jìn)制(2-36)
                        )
            
            例:
            char buffer[20]="10379cend$3";
            char *stop;
            printf("%d\n",strtol(buffer, &stop, 2));
            printf("%s\n", stop);
            輸出結(jié)果:
            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 :產(chǎn)生供gdb調(diào)試用的可執(zhí)行文件
# http://www.reibang.com/p/30ffc01380a0
#-Wall:編譯后顯示所有警告
#-fopenmp 使用mpi支持
#-o:輸出到指定文件


#pragma omp pallel
  • 使用parallel是用來(lái)表明之后的結(jié)構(gòu)化代碼塊(一個(gè)結(jié)構(gòu)化代碼塊時(shí)一條C語(yǔ)句或者只有一個(gè)入口一個(gè)出口的一組復(fù)合C語(yǔ)句)應(yīng)該被多個(gè)線程并行執(zhí)行勺届。
  • 完成代碼塊前會(huì)有一個(gè)隱式路障,先完成的線程必須等待線程組其他線程完成代碼塊娶耍。
- num_threads 子句
  1. 允許程序員指定執(zhí)行后代碼塊的線程數(shù)
  2. 程序可以啟動(dòng)的線程數(shù)可能會(huì)受系統(tǒng)定義的限制免姿。OpenMP標(biāo)準(zhǔn)并不保證實(shí)際能夠啟動(dòng)thread_count個(gè)線程
#pragma omp parallel num_threads(thread_count)
  • 線程被同一個(gè)進(jìn)程派生榕酒,這些線程共享大部分資源胚膊。有它自己的計(jì)數(shù)器故俐。當(dāng)一個(gè)線程完成了執(zhí)行,它就又合并到啟動(dòng)它的線程中紊婉。
  • 每個(gè)線程都有它自己的棧药版,所以執(zhí)行一個(gè)代碼塊將在代碼塊內(nèi)創(chuàng)建自己的私有局部變量
-func omp_get_thread_num | omp_get_num_threads
#獲得當(dāng)前線程的編號(hào)
int omp_get_thread_num(void)
#獲得線程數(shù)量
int omp_get_num_threads(void)
錯(cuò)誤檢查

可以通過(guò)預(yù)處理宏_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
  • 只有一個(gè)線程能夠執(zhí)行對(duì)應(yīng)代碼塊,并且第一個(gè)線程完成操作前槽片,沒(méi)有其他的線程能夠開(kāi)始執(zhí)行這段代碼。
  • 當(dāng)不添加name時(shí)株汉,OpenMP默認(rèn)做法將所有臨界區(qū)代碼塊作為符合臨界區(qū)一部分筐乳,添加name后兩個(gè)不同名字的cirtical指令保護(hù)的代碼可以同時(shí)執(zhí)行
語(yǔ)法
#pragma omp critical [(name)]
用法
#pragma omp critical
global_result += my_result;
變量的作用域
  • 在parallel塊之前被聲明的變量的缺省作用域時(shí)共享的乔妈。
  • parallel指令前已經(jīng)被聲明的變量,擁有線程組中所有線程間的共享作用域氓皱,而在塊中聲明的變量(例如路召,函數(shù)中的變量)中有私有作用域
- reduction 規(guī)約子句
語(yǔ)法
reduction(<operator>:<variable list>)
# operator : +,*,-,&,|,^,&&,||
用法
  • 當(dāng)一個(gè)變量包含在一個(gè)reduction子句中時(shí)波材,變量本身是共享的股淡。然而,線程組中的每個(gè)線程都創(chuàng)建自己的私有變量廷区。在parallel塊里唯灵,每當(dāng)一個(gè)線程執(zhí)行涉及這個(gè)變量(共享變量)的語(yǔ)句時(shí),它使用的其實(shí)時(shí)私有變量隙轻。當(dāng)parallel塊執(zhí)行結(jié)束后埠帕,私有變量中的值被整合到一個(gè)共享變量中。
  • 如果一個(gè)規(guī)約變量時(shí)floatdouble變量型數(shù)據(jù)玖绿,那么當(dāng)使用不同數(shù)量的線程時(shí)敛瓷,結(jié)果可能有些許不同。這是由于浮點(diǎn)數(shù)運(yùn)算不滿足結(jié)合律
  • OpenMp會(huì)為此創(chuàng)建一個(gè)臨界區(qū)斑匪,并且在這個(gè)臨界區(qū)中呐籽,將存儲(chǔ)在私有變量中的值進(jìn)行相加(或其他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 指令生成一組線程來(lái)執(zhí)行后面的結(jié)構(gòu)化代碼塊(必須是for循環(huán))蚀瘸。
  • 系統(tǒng)通過(guò)在線程間劃分循環(huán)迭代來(lái)并行化for循環(huán)狡蝶。與parallel指令非常不同,因?yàn)樵趐arallel指令之前的塊贮勃,一般來(lái)說(shuō)其工作必須由線程本身在線程之間劃分贪惹。
  • 在一個(gè)已經(jīng)被parallel for指令并行化的for循環(huán)中,線程間的缺省劃分方式由系統(tǒng)決定(大約 m(迭代次數(shù))/thread_count)衙猪。
  • 在一個(gè)被parallel for指令并行化的循環(huán)中馍乙,循環(huán)變量的缺省作用域是私有的布近,每個(gè)線程會(huì)有它自己的循環(huán)變量副本
合法方式
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指令并不創(chuàng)建任何線程撑瞧。它使用已經(jīng)在parallel塊中創(chuàng)建的線程。在循環(huán)的末尾有一個(gè)隱式的路障显蝌。
#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++)
        ...
}  
數(shù)據(jù)依賴性
  • OpenMP編譯器不檢查parallel for指令并行化的循環(huán)所包含的迭代間的依賴關(guān)系预伺,而是由程序員來(lái)識(shí)別這些依賴。
  • 一個(gè)或更多個(gè)迭代結(jié)果依賴于其他迭代的循環(huán)曼尊,一般不能被OpenMP正確地并行化酬诀。
數(shù)據(jù)依賴
#y依賴于x
for(i=0;i<n;i++)
{
    x[i]=a+i*h;
    y[i]=exp(x[i]);
}
循環(huán)依賴

一個(gè)值在循環(huán)中計(jì)算,其結(jié)果在之后迭代中使用骆撇。

#并行化后某一個(gè)邊界值將是另一個(gè)并行化線程中的使用瞒御。
fibo[0]=fibo[1]=1;
for(i=2;i<n;i++)
    fibo[i]=fibo[i-1]+fibo[i-2];
- private 子句
  • 在private子句列舉的變量,在每個(gè)線程上都有一個(gè)私有副本被創(chuàng)建神郊。
  • 一個(gè)私有作用域的變量的值在parallel塊或者parallel for塊的開(kāi)始處是未指定的肴裙。它的值在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) 子句
  • 讓程序員明確塊中每個(gè)變量的作用域涌乳。
double sum = 0.0;
/*
sum是一個(gè)規(guī)約變量(同時(shí)擁有私有和共享作用域的屬性)蜻懦。
*/
#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子句

對(duì)線程進(jìn)行調(diào)度。

語(yǔ)法
schedule(<type> [,<chunsize>]

type可以時(shí)一下的任意一個(gè)夕晓。

  • static宛乃。迭代能夠在循環(huán)執(zhí)行前分配給線程。
(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

缺省調(diào)度(static,total_iterations/thread_count)
  • dynamic或guided蒸辆。迭代在循環(huán)執(zhí)行時(shí)被分配給線程征炼,因此在一個(gè)線程完成了它的當(dāng)前迭代集合后,他能從運(yùn)行時(shí)系統(tǒng)中請(qǐng)求更多吁朦。
dynamic調(diào)度中柒室,迭代也被分成chunksize個(gè)連續(xù)迭代的塊。
每個(gè)線程執(zhí)行一塊逗宜,并且當(dāng)一個(gè)線程完成一塊時(shí)雄右,
他將從運(yùn)行時(shí)系統(tǒng)請(qǐng)求另一塊,直到所有的迭代完成纺讲。
chunksize可以被忽略擂仍。當(dāng)它被忽略時(shí),chunksize為1熬甚。
在guided調(diào)度中逢渔,每個(gè)線程也執(zhí)行一塊,并且當(dāng)一個(gè)線程完成一塊乡括,將請(qǐng)求另一塊肃廓。
然而智厌,在guided調(diào)度中,當(dāng)塊完成后盲赊,新塊的大小變小铣鹏。
例如:
n=10 000并且thread_count=2時(shí),迭代將如表那樣分配哀蘑。塊的大小近似等于的迭代數(shù)除以線程數(shù)诚卸。第一塊的大小為9999/2 ~=5000,因?yàn)?999個(gè)未被分配的迭代绘迁。第二塊的大小為4999/2~=2500合溺。以此類(lèi)推。

| 線程 |    塊    | 快的大小 | 剩下的迭代代數(shù) |
| 0   | 1~5000   | 5000 | 4999 |
| 1   | 5001-7500| 2500 | 2499 |
| 1   | 7501-8750| 1250 | 1249 |
...
  • auto缀台。編譯器和運(yùn)行時(shí)系統(tǒng)決定調(diào)度方式棠赛。

  • runtime。調(diào)度在運(yùn)行時(shí)決定将硝。
    chunksize是一個(gè)正整數(shù)恭朗。在OpenMP中,迭代塊在順序循環(huán)中連續(xù)執(zhí)行的一塊迭代語(yǔ)句依疼,塊中的迭代次數(shù)時(shí)chunsize。只有static而芥,dynamic和guided調(diào)度有chunksize律罢。

設(shè)置環(huán)境變量
$export OMP_SCHEDULE="static,1"
#pragma omp barrier
  • 顯式的路障,當(dāng)所有的線程都到達(dá)了這個(gè)路障時(shí)棍丐,這些線程就可以接著往下執(zhí)行误辑。
#pragma omp atomic
  • 只能保護(hù)由一條C語(yǔ)言賦值語(yǔ)句所形成的臨界區(qū),是一個(gè)更高效的指令歌逢。

語(yǔ)句必須是以下形式:

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

用法
#其他線程對(duì)x的更新必須等到該線程對(duì)x的更新結(jié)束之后。
#但對(duì)y不受保護(hù)秘案,因此程序的結(jié)果是不可預(yù)測(cè)的砰苍。
#pragma omp atomic
  x+=y++
簡(jiǎn)單鎖
  • 第一個(gè)函數(shù)初始化鎖,所以鎖此時(shí)處于解鎖狀態(tài)阱高。
  • 第二個(gè)函數(shù)嘗試獲得鎖赚导,如果成功,調(diào)用該函數(shù)的線程可以繼續(xù)執(zhí)行赤惊,如果失敗調(diào)用該函數(shù)的線程被阻塞吼旧,直到鎖被其他線程釋放。
  • 第三個(gè)函數(shù)釋放鎖未舟,以便其他線程獲得該鎖圈暗。
  • 第四個(gè)函數(shù)銷(xiāo)貨鎖掂为。
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); //銷(xiāo)毀互斥器    
}  
#pragma omp single

這樣做能確保接下來(lái)的結(jié)構(gòu)化代碼塊由線程組中的一個(gè)線程執(zhí)行,而組內(nèi)其他線程等待直到該線程執(zhí)行結(jié)束(在代碼塊的最后設(shè)置一個(gè)隱式路障)

#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執(zhí)行接下來(lái)的結(jié)構(gòu)化代碼塊员串。然后master指令在最后不會(huì)設(shè)置隱式路障勇哗。

-func omp_get_wtime

獲取運(yùn)行時(shí)間。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末昵济,一起剝皮案震驚了整個(gè)濱河市智绸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌访忿,老刑警劉巖瞧栗,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異海铆,居然都是意外死亡迹恐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)卧斟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)殴边,“玉大人,你說(shuō)我怎么就攤上這事珍语〈赴叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵板乙,是天一觀的道長(zhǎng)是偷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)募逞,這世上最難降的妖魔是什么蛋铆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮放接,結(jié)果婚禮上刺啦,老公的妹妹穿的比我還像新娘。我一直安慰自己纠脾,他們只是感情好玛瘸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著乳乌,像睡著了一般捧韵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上汉操,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天再来,我揣著相機(jī)與錄音,去河邊找鬼。 笑死芒篷,一個(gè)胖子當(dāng)著我的面吹牛搜变,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播针炉,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼挠他,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了篡帕?” 一聲冷哼從身側(cè)響起殖侵,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镰烧,沒(méi)想到半個(gè)月后拢军,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怔鳖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年茉唉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片结执。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡度陆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出献幔,到底是詐尸還是另有隱情懂傀,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布蜡感,位于F島的核電站鸿竖,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏铸敏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一悟泵、第九天 我趴在偏房一處隱蔽的房頂上張望杈笔。 院中可真熱鬧,春花似錦糕非、人聲如沸蒙具。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)禁筏。三九已至,卻和暖如春衡招,著一層夾襖步出監(jiān)牢的瞬間篱昔,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留州刽,地道東北人空执。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像穗椅,于是被迫代替她去往敵國(guó)和親辨绊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容