
C言語でOpenMPを利用したとき、parallel構文内で、共有変数を宣言する方法はありますか?
OpenMPを利用して、スレッド並列にしたプログラムを書いています。
#pragma omp parallel
{
~~
~~
}
この、~~の部分で、大きく分けて二つの処理をしているので、関数に分けました。
#pragma omp parallel private( a, b, c, d, e, f, g, h, i, j )
{
func1( a, b, c, d, e, f, g, h );
func2( a, b, c, d, e, f, g, h, i, j );
}
このとき、2つ目の関数で共有変数を複数使う必要があります。
しかし、共有変数の数は多く、引数にするとかなりの数の引数になってしまいます。
そこで、できればfunc2()という関数の中で、スレッドで共有できるshared変数を宣言したいのですが、方法がわかりません。
どなたか、知っている方教えてください。
No.11
- 回答日時:
たびたびすみません。
途中で大きな共有領域を確保するには、No.5のmktw00wさんの回答が正解のようですね。
質問者さんの質問の意図が正しく理解できていませんでした。すみませんでした。
No.10
- 回答日時:
No.8のお礼欄をよく読むとこの部分のopenMP化はできているわけですね。
まだできていないと勘違いしていました。ピンぼけ回答になってすみません。
リファクタリングなのでしょうか。さらなる高速化が必要なときに再チャレンジでどうでしょうか。
profilerの結果を見ながらベクトル化やCUDA化も結構おもしろいですよ。データ依存性を解決するのに手間どっておりますが。
No.9
- 回答日時:
問題点承知しました。
今までの議論の結果、並列化領域外でポインタなり配列なりで領域を確保して、必要に応じて関数に渡してやるしか無いように思います。No5の方法についても、ポンタはthread分確保されますが、共有領域を指すポインタなので、変更すると変数領域を見失うので変更が事実上できないように思います。(ここでの使い方限定ですが)
外で宣言、確保するのと同じようなことになるような気がします。
No.1とNo2の回答に戻ってしまいますが、共有変数に書き込むときにthread間のデータ依存性が問題になることが多々有りますので注意してください。
並列化をしたい領域はもともとはループになっていなかったのでしょうか?
ループ部分をopenMPでの並列化により高速にしたいというとが多いと思いますが。
並列化領域を関数に書き換えたのは大正解だと思います。private宣言を減らせるし、うっかり忘れてsharedになっているのに気がつかないことはよくあります。私もやってしまいます。
No.8
- 回答日時:
No.7のお礼欄について
private宣言した変数のコピーのことは書いた後で怪しいなと思ったのですが、質問さんの指摘のとおりだと思います。訂正します。
#pragma omp parallel for private(i)の場合は、空の変数i領域を確保するだけではなくて、openMPが中身を入れてやらないといけませんので、private宣言でそうなるのではなく、forがやっているようですね。
いずれにしても、privateの場合は、openMPはthread分の新たな領域を確保するという特別なことをやっています。
ただし、中で宣言したローカル変数は、openMPが特別なことをしているのではなく、Cのローカル変数の機能であると考えるのが自然ではないこと思います。並列化領域内部から外部の関数を呼び出す場合は、thread safeであればopenMPのことを外部の関数が知らなくても期待通りに動きますので。
それよりも何が問題なのでしょうか。
No.2のお礼欄ではMPIとOpenMPは正常に動いているような書き方なのですが。
並列化するときの構造上のことを少し考えないといけないように思うのですが、どうでしょうか。
#pragma omp parallel private
としたときは、OpenMPがスレッド生成と同時にプライベート変数を宣言します。
parallelリージョン内で変数を宣言した場合は、各スレッドが各々でプライベート変数を宣言したことになります。
この二つはOpenMP上での動作が少し異なりますが、結果として同じプライベート変数となります。
ですから、問題はparallelリージョン内で共有変数を宣言したいのにできない、ということです。
元々は関数に分けず長大なプログラムをつらつらと書いてあって、
parallelリージョンの外で共有変数を宣言してから使っていたのです。
それを関数に分けたものですから、
外で宣言した共有変数を引数として持ち込むよりも内部で共有変数を宣言したくなったのです。
No.7
- 回答日時:
No.6のお礼欄について
{}の中のthreadが複数できますので、ローカル変数iがthread分別々に確保されるということですのでopenMPでいうpriveteではないはずです。ごく普通のローカル変数の確保で、たまたまthreadが複数できただけで、関数の中で宣言するのと同じ挙動のはずです。
forの直前に#pragma omp parallel for private(i)と宣言した場合(ここではprivate宣言は省略可能ですが)には、iの値をprivate変数iにコピーされthreadが作られます。しかしNo.6のお礼欄の場合は、iが新しく確保されるだけで値はコピーされない。
実質的にはprivateと似ていますが、この解釈の方が自然だと思いますが如何でしょうか?
#pragma omp parallel for private(i)
だと、iの値はコピーされませんよ。
firstprivateならコピーされますが。
単にスレッドごとで新たな変数iがスレッドごとに宣言されるだけです。
OpenMP Application Program Interface version 3.0
を読んでいただければ分かりますが、
リージョン内から呼び出されたルーチンで宣言された変数は、基本的にプライベートとなります。
同じparallelリージョンに結合しているtaskリージョンのセットにおいて、
それぞれのtaskリージョンが同じ名前で記憶域の異なるブロックにアクセスする変数、というのがプライベート変数ですので、この場合も正しくプライベート変数のはずです。
No.6
- 回答日時:
No.4でポインタを作る方法を回答されていますので、openMPのsharedについて補足しておきます。
sharedは、openMP自体は何も特別のことをしないので、自然と他のthreadから見えると言うことです。
>#pragma omp parallelの中で変数の宣言を行うと、
>問答無用でプライベート変数になってしまいます。。。
多分関数の中での変数の宣言だと思うのですが、openMPでいうprivateではなくCのローカル変数ですのでローカル変数としての振る舞いをしているだけです。
#pragma omp parallel
{
int i;
for( i=0; i<3; i++ ){
printf("%d : i = %d\n",omp_get_thread_num(), i);
}
}
例えば、こういう風にして変数iを宣言しますと、iはプライベートの変数になって、
0 : i = 0
0 : i = 1
0 : i = 2
1 : i = 0
1 : i = 1
1 : i = 2
みたいな出力になりますよね??
sharedだとスレッドごとに同じiを参照してしまってこうはなりませんし
No.3
- 回答日時:
No.2の訂正です。
>動的に確保した領域のポイタ値を共有変数に書き込めがよいのです。
>が、おもいっきりthread間でデータの依存関係がでるように思います。
ちょっと不正確な記述でした。
動的に配列を確保した場合には領域を解放すると、ポインタの値は残るが中身は書き換えられることがあるので当然値は保証でできない。解放される前に(threadを止めて)他のthreadから読み込まれないと値の保証はできない。並列化処理でよく言うデータの依存関係とは異質のものでした。
ともかく
1) 並列化処理ではなく、ループに書き換えて同じ機能を出せるか。
を考えてみてください。
えっと、そこは問題ないです
記述はしてませんが、MPIとOpenMPのハイブリッド並列でして、
逐次の場合とハイブリッド並列の場合とで結果が同じであることは確認済です
No.2
- 回答日時:
No.1のお礼欄について
>その共有変数というのは複数の配列でして、かつfunc2内でしか使わない変数なのです。
>なので、できればfunc2内でポインタを宣言し、
>動的にメモリ確保して、func2内で解放したいのです。
動的に確保した領域のポイタ値を共有変数に書き込めがよいのです。
が、おもいっきりthread間でデータの依存関係がでるように思います。どうなんでしょうか?
次のことを考えてみてください
1) 並列化処理ではなく、ループに書き換えて同じ機能を出せるか。
2) ループの回る順番を変えても同じ結果になるか。
どうも両方とも駄目なように思いますがいかがですか?
openMPではなく素直に子プロセスを作って実行して、親からコントロールするのが適しているように思いますが、情報があまりにも少なすぎますので。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Excel(エクセル) エクセル関数の変わった使い方 3 2022/05/13 17:12
- JavaScript カラーミーショップのsectionループ内で、[引数][戻り値]ありの関数的な処理を行いたいです。 1 2022/05/07 19:39
- Ruby 初心者プログラミング 3 2022/10/12 11:31
- C言語・C++・C# C言語について コマンドラインで >変数 12.00 (char型) と、小数点付きの値を共用体に渡 1 2022/04/22 16:56
- その他(メールソフト・メールサービス) 詐欺メールの量が酷い 3 2022/05/19 12:36
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- 不動産業・賃貸業 賃貸契約契約の解除 1 2022/11/07 18:02
- C言語・C++・C# const char** p;のとき、free(p)でC4090エラーとなるのはなぜですか 3 2023/03/31 16:28
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# 競技プログラミングに関する質問です。 3 2022/04/03 19:51
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
allocってなんですか?
-
構造体でchar name[]と*nameの...
-
VB6からVCで作成したDLLへのvoi...
-
newしないオブジェクトについて
-
C++のnewで確保したメモリーの...
-
入れ子になった構造体について
-
STLのvectorのデフォルトの予約...
-
CreateFileMapping について
-
LoadLibraryでAccess Violation...
-
reallocについて
-
c言語のポインタへの文字列入力...
-
C++で、メンバもヒープに確保さ...
-
C++とC#って何が違うのですか?
-
行列積の問題で
-
callocの処理速度
-
スタック破壊の上手な見つけ方...
-
128ビット変数の符合表現について
-
HEAP に関すること
-
アンマネージド関数でのメモリ...
-
行数の変動にも対応したファイ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
allocってなんですか?
-
C++のnewで確保したメモリーの...
-
newしないオブジェクトについて
-
c言語のポインタへの文字列入力...
-
プログラムが途中で強制終了し...
-
Win32APIでのメモリ管理について
-
グローバル変数のサイズ
-
ヒープメモリの解放について
-
stringの最大サイズ
-
Accessで、メモリを開放するタ...
-
入れ子になった構造体について
-
void*型のデータサイズ
-
DLLで同じメモリ領域を参照する...
-
callocの処理速度
-
mallocで確保するメモリの領域...
-
ビットをローテートするプログ...
-
DLLのマルチスレッドの動作につ...
-
C++で、メンバもヒープに確保さ...
-
LoadLibraryでAccess Violation...
-
配列の添え字の最大数とは?
おすすめ情報