OpenMP:ヒープ破損の原因は何ですか?
-
16-09-2019 - |
質問
編集: 同じプログラムを問題なく同時に 2 回実行できますが、OpenMP または他の方法でこれを複製するにはどうすればよいでしょうか?
これが問題の基本的な枠組みです。
//Defined elsewhere
class SomeClass
{
public:
void Function()
{
// Allocate some memory
float *Data;
Data = new float[1024];
// Declare a struct which will be used by functions defined in the DLL
SomeStruct Obj;
Obj = MemAllocFunctionInDLL(Obj);
// Call it
FunctionDefinedInDLL(Data,Obj);
// Clean up
MemDeallocFunctionInDLL(Obj);
delete [] Data;
}
}
void Bar()
{
#pragma omp parallel for
for(int j = 0;j<10;++j)
{
SomeClass X;
X.Function();
}
}
一部のメモリの割り当てを解除しようとすると、 MemDeallocFunctionInDLL()
, 、 _CrtIsValidHeapPointer()
アサーションは失敗します。
これは両方のスレッドが同じメモリに書き込んでいるためでしょうか?
それで、これを修正するために、私は作ろうと思いました SomeClass
プライベート(これは私にとってまったく縁のないものなので、助けていただければ幸いです)。
void Bar()
{
SomeClass X;
#pragma omp parallel for default(shared) private(X)
for(int j = 0;j<10;++j)
{
X.Function();
}
}
そして 今 しようとすると失敗します 割り当てる 初めの頃の記憶 Data
.
注記: 必要に応じて DLL を変更できます
注記: 何もなくても完璧に動作します #pragma omp parallel for
編集: 今 Bar
次のようになります:
void Bar()
{
int j
#pragma omp parallel for default(none) private(j)
for(j = 0;j<10;++j)
{
SomeClass X;
X.Function();
}
}
まだ運がありません。
解決
MemAllocFunctionInDLL、FunctionDefinedInDLL、MemDeallocFunctionInDLL を確認してください。 スレッドセーフ, 、 または リエントラント. 。言い換えれば、これらの関数は静的変数ですか、それとも共有変数ですか?このような場合、これらの変数が他のスレッドによって破損されていないことを確認する必要があります。
omp-for がなくても問題ないという事実は、スレッドセーフになるようにいくつかの関数を正しく記述していないことを意味している可能性があります。
Mem(Alloc|Dealloc)FunctionInDLL でどのようなメモリ割り当て/解放関数が使用されているかを確認したいと思います。
追加した:DLL 内の関数はスレッドセーフではないと確信しています。このプログラムは問題なく同時に実行できます。はい、プログラムがシステム全体の共有リソース (グローバル メモリやプロセス間の共有メモリなど) を使用することはほとんどありませんが、それ以外は問題ありません。この場合、スレッド内に共有変数がないため、プログラムは正常に動作します。
ただし、これらの関数を呼び出すと、 マルチスレッド (つまり、単一プロセスで) プログラムがクラッシュします。これは、スレッド間に共有変数がいくつかあり、それが破損している可能性があることを意味します。
これは OpenMP の問題ではなく、単なるマルチスレッドのバグです。この問題を解決するのは簡単かもしれません。DLL 関数が多くのスレッドによって同時に呼び出されても安全かどうかを確認してください。
静的変数をプライベート化する方法
このようなグローバル変数があるとします。
static int g_data;
static int* g_vector = new int[100];
民営化は創造にほかならない プライベート スレッドごとにコピーします。
int g_data[num_threads];
int* g_vector[num_threads];
for (int i = 0; i < num_threads; ++i)
g_vector[i] = new int[100];
そして、そのような変数の参照は次のようになります。
// Thread: tid
g_data[tid] = ...
.. = g_vector[tid][..]
はい、とても簡単です。ただし、この種のコードには、 フォールスシェアリング 問題。ただし、フォールス シェアリングはパフォーマンスの問題であり、正確さの問題ではありません。
まず、静的変数とグローバル変数をプライベート化してみます。次に、それが正しいかどうかを確認します。次に、どの程度のスピードアップが得られるかを見てみましょう。高速化がスケーラブルであれば (クアッドコアで 3.7 倍高速など)、問題はありません。ただし、低速の高速化 (クアッド コアで 2 倍の高速化など) の場合は、おそらくフォールス シェアリングの問題が考えられます。偽共有の問題を解決するには、データ構造にパディングを入れるだけです。
他のヒント
の代わりに
delete Data
書かなければなりません
delete [] Data;
new [] を実行する場合は常に、delete [] を使用してください。
あなたの問題は openmp に固有のものではないようです。#pragmaParallel を含めずにアプリケーションを実行しようとしましたか?
default(shared) は、すべての変数がスレッド間で共有されることを意味しますが、これは希望するものではありません。それをデフォルト(なし)に変更します。
Private(X) はスレッドごとに X のコピーを作成しますが、いずれも初期化されないため、必ずしも構築が実行されるわけではありません。
最初のアプローチで、Dealloc 呼び出しにブレークポイントを設定し、メモリ ポインタとその内容を確認する方がよいと思います。ご覧いただけます。 ガードバイト 単一の呼び出しの終了時、またはスレッドの後にメモリが上書きされたかどうかを確認します。
ちなみに、これは omp ループなしで一度実行すれば機能すると思いますか?