私はfor-loop
OpenMPでかなり大規模な並列化を試みています。約20%の時間は正常に実行されますが、残りの時間は次のようなさまざまなsegfaultでクラッシュします。
*** glibc detected *** ./execute: double free or corruption (!prev): <address> ***
*** glibc detected *** ./execute: free(): invalid next size (fast): <address> ***
[2] <PID> segmentation fault ./execute
私の一般的なコード構造は次のとおりです。
<declare and initialize shared variables here>
#pragma omp parallel private(list of private variables which are initialized in for loop) shared(much shorter list of shared variables)
{
#pragma omp for
for (index = 0 ; index < end ; index++) {
// Lots of functionality (science!)
// Calls to other deep functions which manipulate private variables
// Finally generated some calculated_values
shared_array1[index] = calculated_value1;
shared_array2[index] = calculated_value2;
shared_array3[index] = calculated_value3;
} // end for
}
// final tidy up
}
何が起こっているかという点では、各ループの反復は、共有マトリックスからデータをプルするという事実を除いて、他のループの反復から完全に独立しています(ただし、ループの反復ごとに異なる列があります)。私が他の関数を呼び出す場合、それらはプライベート変数を変更するだけです(ただし、共有変数を読み取ることもあります)ので、特定のスレッドにローカルなものだけをいじっているので、スレッドセーフだと思いますか?共有変数への唯一の書き込みは最後に行われ、さまざまな計算値がいくつかの共有配列に書き込まれます。ここで、配列要素はforループインデックスによってインデックス付けされます。このコードはC++ですが、呼び出すコードはCとC++の両方のコードです。
私は問題の原因を特定しようとしてきましたが、今のところ運がありません。num_theads(1)を設定すると、の内容を1for-loop
つで囲む場合と同様に、正常に実行されます。
#pragma omp for
for(index = 0 ; index < end ; index++) {
#pragma omp critical(whole_loop)
{
// loop body
}
}
これはおそらく同じ効果をもたらします(つまり、一度に1つのスレッドだけがループを通過できます)。
一方、for-loop's
内容を2つのcritical
ディレクティブで囲む場合。
#pragma omp for
for(index = 0 ; index < end ; index++) {
#pragma omp critical(whole_loop)
{
// first half of loop body
}
#pragma omp critical(whole_loop2)
{
// second half of loop body
}
}
予測できないセグフォールトが発生します。同様に、すべての関数呼び出しをディレクティブで囲むと、critical
それでも機能しません。
問題が関数呼び出しに関連している可能性があると思う理由は、Valgrind(を使用valgrind --tool=drd --check-stack-var=yes --read-var-info=yes ./execute
)およびSIGSEGingでプロファイルを作成すると、次のような非常に多くのロードおよびストアエラーが発生するためです。
Conflicting load by thread 2 at <address> size <number>
at <address> : function which is ultimately called from within my for loop
valgrindのマニュアルによると、これは競合状態で予想されるものとまったく同じです。確かに、この種の奇妙に現れる/消える問題は、競合状態が与える非決定論的エラーの種類と一致しているように見えますが、明らかな競合状態を与えるすべての呼び出しがクリティカルセクションにある場合、どうすればよいかわかりません。
間違っている可能性がありますが、私はそうは思いません。
すべてのprivate()変数は
for-loops
(スレッドローカルであるため)内部で初期化されます。共有変数のメモリアドレスが同じで、プライベート変数のメモリアドレスが異なることを確認しました。
同期が役立つかどうかはわかりませんが、
barrier
ディレクティブの開始と終了に暗黙のディレクティブcritical
があり、すべての関数呼び出しが(一意の名前の)クリティカルセクションで囲まれているバージョンのコードを試しました。アウト。
最善の方法についての考えは大歓迎です。私は一日中これに頭をぶつけてきました。明らかに、私は「ああ、これが問題だ」というタイプの答えを探しているのではなく、デバッグ/分解の観点からどのように進めるのが最善かを探しています。
問題になる可能性がある、または役立つ可能性があるもの。
コードには、vector.pushback()関数を使用して要素を追加するstd::Vectorがいくつかあります。ベクトルのサイズ変更はスレッドセーフではないことを読んだことを覚えていますが、ベクトルはプライベート変数にすぎないため、スレッド間で共有されません。これで大丈夫だと思いましたか?
for-loop
ボディ全体をディレクティブで囲み、critical
コードブロックの終わりをゆっくりと縮小すると(つまり、の終わりにある成長し続ける領域for-loop
がクリティカルセクションの外側にある場合)、関数呼び出しの1つを公開するまで、正常に実行されます。ポイントセグフォールトが再開されます。このバイナリをValgrindで分析すると、私が公開したものだけでなく、他の多くの関数呼び出しの競合状態がわかります。関数呼び出しの1つは、GSL関数への呼び出しです。これは、Valgrindによると競合状態をトリガーしません。
呼び出される関数でプライベート変数と共有変数を明示的に定義する必要がありますか?もしそうなら、これはOpenMPに対していくらか制限があるように思われます-これは、呼び出すレガシーコードに対してOpenMP互換性が必要であることを意味しませんか?
大きな並列化は
for-loop
うまくいかないのでしょうか?ここまで読んだら、ありがとうとゴッドスピード。