OpenMPプログラミング - 変数のスレッドコピー

いちごパック > OpenMPの解説 > OpenMPプログラミング - 変数のスレッドコピー

全スレッド共通の変数とスレッド単位の変数

OpenMPでは、omp parallelの外にある変数は全スレッドで共通の変数になりますが、 omp parallelの対象範囲では変数もスレッドごとに用意されます。 OpenMPではこの運用を変えるための機能も提供しており、 それらを利用したソースコードも存在しますので、ここではその一部を説明します。
なお、いちごパックでは、できる限り何も指定しない形で変数を運用したほうが、 プログラミングミスが起こりにくいと考えています。 ここで説明するfirstprivateやprivateを、ご自身のソースコードで利用することはおすすめしません。

初期化つき変数 firstprivate

全スレッド共通の変数があるとき、#pragma omp parallelの指示にfirstprivate(変数名)を加えると、 同じ名前を持つスレッド単位の変数を、全スレッド共通の変数の値で初期化して用意します。
例として、次のソースコードを考えます。
#include <iostream>
#include <omp.h>

int main()
{
    int ichigoval = 3;

    #pragma omp parallel
    {
        ichigoval += omp_get_thread_num();
        #pragma omp critical(crit_cout)
        {
            std::cout << "ichigoval = " << ichigoval << std::endl;
        }
        #pragma omp barrier
    }

    return 0;
}
このソースコードは、各スレッドで、3にスレッド番号を加えた値が出力されることを期待しています。 しかしながら、ichigovalは全スレッド共通の変数であり、 排他制御もしていませんので、その実行結果は期待したものにはなりません。
ichigoval = 3
ichigoval = 4
ichigoval = 6
ichigoval = 9
次のようにfirstprivateを使うことで、期待通りの結果が得られます。
#include <iostream>
#include <omp.h>

int main()
{
    int ichigoval = 3;

    #pragma omp parallel firstprivate(ichigoval)
    {
        ichigoval += omp_get_thread_num();
        #pragma omp critical(crit_cout)
        {
            std::cout << "ichigoval = " << ichigoval << std::endl;
        }
        #pragma omp barrier
    }

    return 0;
}
その実行結果は次の通りです。
ichigoval = 3
ichigoval = 6
ichigoval = 5
ichigoval = 4
なお、スレッドごとに変数を用意してichigovalをコピーすれば、 firstprivateを使わなくても同じ結果が得られます。 firstprivateを使うと変数がスレッド単位であるかわかりにくくなりますので、 いちごパックとしては次のソースコードのように、スレッドごとに変数を用意することを推奨します。
#include <iostream>
#include <omp.h>

int main()
{
    int ichigoval = 3;

    #pragma omp parallel
    {
        int l_val = ichigoval;
        l_val += omp_get_thread_num();
        #pragma omp critical(crit_cout)
        {
            std::cout << "ichigoval = " << l_val << std::endl;
        }
        #pragma omp barrier
    }

    return 0;
}
実行結果は先の例と同じになります。
ichigoval = 3
ichigoval = 5
ichigoval = 6
ichigoval = 4

初期化なし変数 private

全スレッド共通の変数があるとき、#pragma omp parallelの指示にprivate(変数名)を加えると、 同じ名前を持つスレッド単位の変数を、何も初期化せずに用意します。
次のソースコードでその動作を説明します。
#include <iostream>
#include <omp.h>

int main()
{
    int ichigoval = 3;

    #pragma omp parallel private(ichigoval)
    {
        #pragma omp critical(crit_cout)
        {
            std::cout << "ichigoval pre = " << ichigoval << std::endl;
        }
        #pragma omp barrier
        ichigoval = omp_get_thread_num();
        #pragma omp critical(crit_cout)
        {
            std::cout << "ichigoval post = " << ichigoval << std::endl;
        }
        #pragma omp barrier
    }

    return 0;
}
このソースコードでは、各スレッドの最初のichigovalと、数値をセットした後のichigovalを表示しています。 実行結果は次のようになります。
ichigoval = 3
ichigoval = 5
ichigoval = 6
ichigoval = 4
いちごパックとしては、この動作は不具合の原因になりやすいと考えています。 初期化する必要がないのであれば、omp parallelの対象範囲でichigovalを準備したほうが良いでしょう。

関連ページ

  • OpenMPの解説 目次
  • 前の項目: 並列実行の競合対策