- ベストアンサー
CでOpenMP、パラレル内での共有変数の宣言方法
- C言語でOpenMPを利用したとき、parallel構文内で、共有変数を宣言する方法はありますか?
- OpenMPを利用して、スレッド並列にしたプログラムを書いています。関数に分けているため、共有変数を引数に渡すのは手間です。func2()内でスレッドで共有できるshared変数を宣言したいのですが、方法がわかりません。
- どなたか、C言語でOpenMPを利用してパラレル内で共有変数を宣言する方法を知っている方、教えてください。
- みんなの回答 (11)
- 専門家の回答
質問者が選んだベストアンサー
int *p; #pragma omp single copyprivate(p) { p = (int *) malloc ( size ); } これで多分いいと思います。 値がコピーされるので、同じアドレスを指すポインタになるはずです。
その他の回答 (10)
- ki073
- ベストアンサー率77% (491/634)
たびたびすみません。 途中で大きな共有領域を確保するには、No.5のmktw00wさんの回答が正解のようですね。 質問者さんの質問の意図が正しく理解できていませんでした。すみませんでした。
- ki073
- ベストアンサー率77% (491/634)
No.8のお礼欄をよく読むとこの部分のopenMP化はできているわけですね。 まだできていないと勘違いしていました。ピンぼけ回答になってすみません。 リファクタリングなのでしょうか。さらなる高速化が必要なときに再チャレンジでどうでしょうか。 profilerの結果を見ながらベクトル化やCUDA化も結構おもしろいですよ。データ依存性を解決するのに手間どっておりますが。
- ki073
- ベストアンサー率77% (491/634)
問題点承知しました。 今までの議論の結果、並列化領域外でポインタなり配列なりで領域を確保して、必要に応じて関数に渡してやるしか無いように思います。No5の方法についても、ポンタはthread分確保されますが、共有領域を指すポインタなので、変更すると変数領域を見失うので変更が事実上できないように思います。(ここでの使い方限定ですが) 外で宣言、確保するのと同じようなことになるような気がします。 No.1とNo2の回答に戻ってしまいますが、共有変数に書き込むときにthread間のデータ依存性が問題になることが多々有りますので注意してください。 並列化をしたい領域はもともとはループになっていなかったのでしょうか? ループ部分をopenMPでの並列化により高速にしたいというとが多いと思いますが。 並列化領域を関数に書き換えたのは大正解だと思います。private宣言を減らせるし、うっかり忘れてsharedになっているのに気がつかないことはよくあります。私もやってしまいます。
- ki073
- ベストアンサー率77% (491/634)
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リージョンの外で共有変数を宣言してから使っていたのです。 それを関数に分けたものですから、 外で宣言した共有変数を引数として持ち込むよりも内部で共有変数を宣言したくなったのです。
- ki073
- ベストアンサー率77% (491/634)
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リージョンが同じ名前で記憶域の異なるブロックにアクセスする変数、というのがプライベート変数ですので、この場合も正しくプライベート変数のはずです。
- ki073
- ベストアンサー率77% (491/634)
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を参照してしまってこうはなりませんし
- ki073
- ベストアンサー率77% (491/634)
No.3のお礼欄について 質問者さんのプログラムが既にopenMPで正常に動いているのなら、shared宣言は省略可能ですので省略すれば良いと思います。 thread間のデータ共有や依存性の問題がでるのでしたら別に考える必要がありますが。
お礼
えっと、その共有変数をどうやって宣言すればいいのでしょう?? #pragma omp parallelの中で変数の宣言を行うと、問答無用でプライベート変数になってしまいます。。。
- ki073
- ベストアンサー率77% (491/634)
No.2の訂正です。 >動的に確保した領域のポイタ値を共有変数に書き込めがよいのです。 >が、おもいっきりthread間でデータの依存関係がでるように思います。 ちょっと不正確な記述でした。 動的に配列を確保した場合には領域を解放すると、ポインタの値は残るが中身は書き換えられることがあるので当然値は保証でできない。解放される前に(threadを止めて)他のthreadから読み込まれないと値の保証はできない。並列化処理でよく言うデータの依存関係とは異質のものでした。 ともかく 1) 並列化処理ではなく、ループに書き換えて同じ機能を出せるか。 を考えてみてください。
お礼
えっと、そこは問題ないです 記述はしてませんが、MPIとOpenMPのハイブリッド並列でして、 逐次の場合とハイブリッド並列の場合とで結果が同じであることは確認済です
- ki073
- ベストアンサー率77% (491/634)
No.1のお礼欄について >その共有変数というのは複数の配列でして、かつfunc2内でしか使わない変数なのです。 >なので、できればfunc2内でポインタを宣言し、 >動的にメモリ確保して、func2内で解放したいのです。 動的に確保した領域のポイタ値を共有変数に書き込めがよいのです。 が、おもいっきりthread間でデータの依存関係がでるように思います。どうなんでしょうか? 次のことを考えてみてください 1) 並列化処理ではなく、ループに書き換えて同じ機能を出せるか。 2) ループの回る順番を変えても同じ結果になるか。 どうも両方とも駄目なように思いますがいかがですか? openMPではなく素直に子プロセスを作って実行して、親からコントロールするのが適しているように思いますが、情報があまりにも少なすぎますので。
- ki073
- ベストアンサー率77% (491/634)
普通のCの書き方で問題ないはずです。配列や構造体に共有変数をまとめてfunc2に参照渡しにすればどうしょうか。 並列化区間のthreadの実行順序は不定ですので、共有変数に並列化区間で書き込む時は十分注意してください。
お礼
回答ありがとうございます。 その共有変数というのは複数の配列でして、かつfunc2内でしか使わない変数なのです。 なので、できればfunc2内でポインタを宣言し、 動的にメモリ確保して、func2内で解放したいのです。 もちろん、構造体にまとめればできるのは分かっていますが。。。すみません。
お礼
まだ試してませんが、ありがとうございます これで一度やってみます