0

私はfor-loopOpenMPでかなり大規模な並列化を試みています。約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うまくいかないのでしょうか?

  • ここまで読んだら、ありがとうとゴッドスピード。

4

1 に答える 1

1

したがって、誰もがこれに答えることができる方法はありませんが、私のシステムの動作が非常に奇妙だったことを考えると、これが誰かの助けになることを願っています.

私が最終的に呼び出していた (C) 関数の 1 つ ( my_function-> intermediate_function-> lower_function-> BAD_FUNCTION) は、多くの変数を として宣言しましたstatic。これは、それらが同じメモリ アドレスを保持していたため、本質的に共有変数として機能していたことを意味します。static が OpenMP をオーバーライドするのは興味深いことです。

私はこれをすべて発見しました。

  • Valgrid を使用してエラーが発生した場所を特定し、関連する特定の変数を調べます。

  • 全体for-loopをクリティカル セクションとして定義し、上部と下部でより多くのコードを公開します。

  • 上司と話しています。特に問題を言語化することを余儀なくされているため、より多くの目のセットが常に役立ちます(結局、犯人関数を開いて宣言を指すことになりました)

于 2012-05-24T18:05:13.837 に答える