Parallel Construct、Barrier Construct

parallel construct是由parallel directive與想要平行處理的敘述所組成的程式區塊,只有在此區塊內的程式敘述會被平行執行(使用多執行緒),區塊外的程式則是序列執行(使用單一執行緒)。以fork-join model來看,parallel directive是fork的起始點,系統在此處加入其它的執行緒來協助主執行緒一起處理工作,而在parallel construct的結尾則進行join,只留下主執行緒繼續以序列方式執行剩下的工作,整個過程如下圖所示:

Parallel Construct

從圖中可以看出parallel directive只負責新增執行緒,若沒有進一步的指示,則這群執行緒會重複執行平行區塊內的程式敘述,如此一來construct內的工作實際上並沒有被分工處理,而是在同一時間內被完成了很多次,所以內部通常會另外使用work-sharing directives,指定如何分工完成任務。

parallel construct的啟始點由含有parallel directive的特殊敘述行開始,在C/C++中必須用小寫的英文字來寫。directive之後可以依需要加上其它clauses來做更多的設定,比如我們可加上private clause,宣告某個變數在各執行緒內所私有的。construct的開始點在左大誇號"{“,結束點在右大括號 “}",使用時須先含入omp.h這個OpenMP函式庫,簡單用以下程式碼表示Parallel Construct:

#pragma omp parallel [clause[[,] clause]...]
{
    statement
    ...
}

在用visual studio編譯時想使用OpenMP,需進行下列步驟,按屬性 -> C/C++ -> 語言 -> 開啟MP支援

Parallel Construct


以下示範如何使用parallel construct,讓參與運算的執行緒都印出Hello和Bye bye,omp_get_thread_num()函式回傳目前的執行緒編號,我們另外加入一個private clause,表示construct內的thread_id變數為各執行緒所私有的,如此各執行緒在接下來的程式敘述時,才能取用各自的thread_id值,以下為詳細的程式碼:

#include <cstdio>
#include <cstdlib>
#include <omp.h>

int main(){
    int thread_id;
    printf("I am the main thread.\n\n");

    #pragma omp parallel private(thread_id)
    {
        thread_id = omp_get_thread_num();

        printf("Thread %d: Hello.\n", thread_id);
        printf("Thread %d: Bye bye.\n", thread_id);
    }

    printf("\nI am the main thread.\n\n");
    system("PAUSE");
    return 0;
}

進入平行區域後可以看到有編號0到3共四個執行緒在執行,執行緒並未按照其編號順序來執行程式,基本上先完成工作的執行緒會先輸出訊息,因此每次執行此程式的輸出結果都不見得會一樣,不過parallel construct內的敘述,一定都會被每個執行緒執行一次。


Parallel Construct


OpenMP的程式有讓所有執行緒暫停的點,當執行緒執行到這邊就會進入暫停執行的狀態,如果這個暫停點是用來同步執行緒的進度,則只有當所有參與平行運算的執行緒都到達這個點後,才能結束暫停狀態讓執行緒繼續往下執行,例如parallel construct的結束點就是執行緒同步點,所有的執行緒都會停在此處,等到齊之後再進行join步驟以離開平行運算區域,另外可以使用barrier construct來設立同步點,只有所有執行緒都到達這個點後,才繼續往下執行。

以下程式碼加上一行#pragma omp barrier,會讓所有執行緒都先印出Hello後,再印出Bye bye:

#include <cstdio>
#include <cstdlib>
#include <omp.h>

int main(){
    int thread_id;
    printf("I am the main thread.\n\n");

    #pragma omp parallel private(thread_id)
    {
        thread_id = omp_get_thread_num();

        printf("Thread %d: Hello.\n", thread_id);
        #pragma omp barrier
        printf("Thread %d: Bye bye.\n", thread_id);
    }

    printf("\nI am the main thread.\n\n");
    system("PAUSE");
    return 0;
}

Barrier Construct


回到首頁

回到OpenMP教學


參考資料:

aaz 的記憶倉庫