【平行運算】OpenMP教學(四) 資料共享 Data-sharing

在多執行緒平行化的程式中,有一些變數是所有執行緒 (threads) 共享的,大家都看得到。有一些變數則是各個執行緒私人擁有的,A 執行緒看得到,但 B 執行緒看不到。詳細來說,在OpenMP裡面有兩種變數: Shared VariablePrivate Variable。以之前的 for 平行化來說 (請參考多執行緒平行計算 OpenMP教學 Part2 : 分工 Worksharing (附完整程式碼))

...
int main()
{
    const int N = 8;
    int A[N] = {1, 2, 3, 4, 5, 6, 7, 8};
#pragma omp parallel
    {

        const int thread_id = omp_get_thread_num();

// parallel computing for A[i] = A[i] + 1
#pragma omp for
        for (int i = 0; i < N; i++)
        {
            A[i] = A[i] + 1;
            printf("A[%d] is computed by thread number : %d\n", i, thread_id);
        }
    }
...
    return 0;
}
  • Shared Variable 包含:
    • 在 parallel construct 外面宣告的變數,比如說陣列 A, 陣列大小 N
  • Private Variable 包含:
    • 在 parallel region 中宣告的變數,比如說 thread_id
    • 迴圈中的iteration variable (就是i)

我們也可以使用 private(), shared() 等等不同的 clause(子句) 來指定該變數是哪種類型的變數。clause,不同的 clause 請參考Microsoft OpenMP程式庫文件

完整程式碼

OpenMP 語法介紹

  • # pragma omp parallel private()
  • # pragma omp parallel firstprivate()
  • # pragma omp parallel shared()

OpenMP 範例程式 : private 變數

  1. 原來在 parallel region 外面宣告的變數,對於所有threads 來說是shared variable。但我們可以在進入 parallel region 時,指定變數為 private
  2. 在將變數宣告為 private 時,是未初始化的,不會繼承外面 shared variable 的值。以以下例子來說,經過 +1 的運算後,值會是 1。原因是裡面的 var,和外面的 var 已經不是同一個 var 了
  3. 若想讓 private variable 可以繼承外面 shared variable 的值,則可以用 firstprivate
# === complile 編譯 ===
$ g++ -fopenmp example_datasharing_1.cpp -o example_datasharing_1.out
// ** 檔名 example_datasharing_1.cpp **
// 都會阿嬤 OpenMP 教學
// 都會阿嬤 https://weikaiwei.com

#include <stdio.h>
#include <omp.h>

int main()
{

    int var = 10;
    printf("var : private variable\n");
#pragma omp parallel private(var)
    {
        const int thread_id = omp_get_thread_num();
        var += 1;
        printf("var : %d | computed by thread : %d\n", var, thread_id);
    }

    return 0;
}
# === 執行 execute===
$ ./example_datasharing_1.out

# === 輸出 output===
var : private variable
var : 1 | computed by thread : 2
var : 1 | computed by thread : 1
var : 1 | computed by thread : 0
var : 1 | computed by thread : 3

OpenMP 範例程式 : firstprivate 變數

  1. 若想讓 private variable 可以繼承外面 shared variable 的值,則可以用 firstprivat
  2. 注意 firstprivate 與 private (上面的例子)的差異
# === complile 編譯 ===
$ g++ -fopenmp example_datasharing_2.cpp -o example_datasharing_2.out
// ** 檔名 example_datasharing_2.cpp **
// 都會阿嬤 OpenMP 教學
// 都會阿嬤 https://weikaiwei.com

#include <stdio.h>
#include <omp.h>

int main()
{

    int var = 10;

    printf("\nvar : firstprivate variable\n");
#pragma omp parallel firstprivate(var)
    {
        const int thread_id = omp_get_thread_num();
        var += 1;
        printf("var : %d | computed by thread : %d\n", var, thread_id);
    }

    return 0;
}
# === 執行 execute===
$ ./example_datasharing_2.out

# === 輸出 output===
var : firstprivate variable
var : 11 | computed by thread : 2
var : 11 | computed by thread : 1
var : 11 | computed by thread : 0
var : 11 | computed by thread : 3

OpenMP 範例程式 : shared 變數

  1. 以下例子中,有沒有加上shared其實是沒有差的,原因是在外面宣告的 var 本來就是 shared variable 了
# === complile 編譯 ===
$ g++ -fopenmp example_datasharing_3.cpp -o example_datasharing_3.out
// ** 檔名 example_datasharing_3.cpp **
// 都會阿嬤 OpenMP 教學
// 都會阿嬤 https://weikaiwei.com

#include <stdio.h>
#include <omp.h>

int main()
{

    int var = 10;

    printf("\nvar : shared variable\n");
#pragma omp parallel shared(var)
    {
        const int thread_id = omp_get_thread_num();
        var += 1;
        printf("var : %d | computed by thread : %d\n", var, thread_id);
    }
    return 0;
}
# === 執行 execute===
$ ./example_datasharing_3.out

# === 輸出 output===
var : shared variable
var : 11 | computed by thread : 2
var : 13 | computed by thread : 0
var : 12 | computed by thread : 1
var : 14 | computed by thread : 3

留言討論區