全スレッド共通の変数とスレッド単位の変数
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の解説 目次
前の項目: 並列実行の競合対策