Single Construct、Master Construct

在平行化時有些工作其實只要由一個執行緒來做即可,例如印出訊息或者是讀寫檔案資料等,像這類只需要指派一個執行緒來做的工作,我們可以將它們放到single construct或master construct之中,我們這邊介紹這兩個construct的用法,以及什麼情況下該採用那個construct。


Single Construct

single construct屬於work-sharing constructs的一員,因此它的結束點有一個執行緒同步點存在,在平行區域中,第一個遭遇到single construct的執行緒將被指派去執行construct內的工作,而其它的執行緒則停在construct的結束點,直到執行single construct的執行緒完成工作後,才一起去處理各別之後的任務。

下圖說明single construct的工作流程,圖中TASK 2位於single construct內,系統將指派第一個到達這邊的執行緒去處理TASK 2:

Single Construct


Master Construct

master construct與single construct有以下兩個地方不同:

1.master construct保證在構造內的程式只由master thread (TID = 0)來執行。

2.master construct的結束點並沒有執行緒同步點,所以當主執行緒正在處理master construct內的工作時,其它的執行緒會去執行其它的工作。

下圖說明master construct的動作流程,master construct內的TASK 2一定是由主執行緒(藍色箭條線)所執行,同一時間其它執行緒會略過TASK 2而去處理TASK 3,主執行緒完成TASK 2後接著處理TASK 3:

Master Construct


使用時機

single construct與master construct在使用上都必須嵌入在parallel construct之中,雖然single construct或master construct都可以在平行區域中將任務委由單一執行緒處理,但因它們行為上的差異造成選用這兩種construct時會有些限制與考量,一般選用的準則如下:

  1. 當我們不在乎工作該由那一個執行緒去處理,但希望未被指派到的執行緒,應該等待該工作完成後再去進行其它任務時,宜優先採用single construct。

  2. 當只由單一執行緒處理的工作,執行結果不會影響到平行區域內接下來要被執行的任務時宜選用master construct,因為master construct的結尾沒有執行緒同步點,所以當主執行緒在處理master construct的工作期間,其它的執行緒也正在進行各自的工作,如果這些工作必須引用到由master construct內部某些指令所產生的結果,此時很可能發生邏輯錯誤的狀況。


以下示範single construct的用法,某個執行緒對陣列重新賦值,在完成這項工作後,所有執行緒才一起進入loop construct印出陣列的元素值:

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

int main(){
    int tid;
    int a[12];
    for (int i=0; i<12; i++){
        a[i] = i;
    }

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

        #pragma omp single
        {
            for (int i=0; i<12; i++) {
                a[i] = 0;
            }
            printf("---TID %d: a[i] has been initialized.\n", tid);
        }  

        #pragma omp for 
        for(int i=0; i<12; i++){
            printf("TID %d: a[%d] = %d\n", tid, i, a[i]);
        }

    }
    system("PAUSE");
    return 0;
}

Single Construct


以下用master construct進行上述的工作,我們可看出由主執行緒(TID = 0)對陣列重新賦值,其它執行緒直接進行下個工作,將陣列的值印出,這行為可能造成與預期結果不符的現象:

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

int main(){
    int tid;
    int a[12];
    for (int i=0; i<12; i++){
        a[i] = i;
    }

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

        #pragma omp master
        {
            Sleep(1);
            for (int i=0; i<12; i++) {
                a[i] = 0;
            }
            printf("---TID %d: a[i] has been initialized.\n", tid);
        }  

        #pragma omp for 
        for(int i=0; i<12; i++){
            printf("TID %d: a[%d] = %d\n", tid, i, a[i]);
        }

    }
    system("PAUSE");
    return 0;
}

Master Construct


回到首頁

回到OpenMP教學


參考資料:

aaz 的記憶倉庫