Visual C++のアプリケーション開発で、並列化プログラミングを行うためにOpenMPを使っているとWindowsタスクマネージャのスレッド数がやたらと増え続けてしまうことがあります。この問題についてちょっと紹介します。
ちなみにOpenMPはMSDNによると
OpenMP C/C++ アプリケーション プログラミング インターフェイスを使用して、複数のプロセッサを効果的に使用するアプリケーションを記述できます。
という説明がされております。
原因
まず、OpenMP(VCOMP90.DLL)のスレッドの特性ですが、
- OpenMPにより一度生成されたスレッドはそのDLL内で再利用のために保持される
- OpenMPで再利用可能なスレッドは、それを生成したスレッドと同一のものである
ということがなんとなくわかりました(開発環境は、Visual Studio 2008)。ということで、スレッドを生成してその中でOpenMPを使用するという使い方をすると、再利用できないスレッドがすべて再利用待ちの状態で残ってしまうということで、スレッドがやたら増え続けるという結果につながっていることがわかりました。
また、以下のソースコードで実験をしてみました。1から10まで数える計算を10回スレッドでやらせるというものですが、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
void Thread() { int sum = 0; #pragma omp parallel for for( int n = 1; n <= 10; n++ ) { sum += n; } }; void main() { for( int n = 0; n < 10; n++ ) { DWORD id; HANDLE hThread = ::CreateThread ( NULL, 0, ( LPTHREAD_START_ROUTINE )Thread, NULL, 0, &id ); WaitForSingleObject( hThread, INFINITE ); CloseHandle( hThread ); } } |
main関数の最後の位置にブレークポイントを置いてスレッドを見てみるとメインスレッドの他に10個のスレッドが残っているのがわかります。
ちなみにpersistentという単語の意味は、
固執する, 不屈の, 粘り強い, 頑固な; 持続する.
のようです(Goo辞書より)。
対策
次のような感じでomp_set_dynamicを設定してあげればよいようだ。どうやらデフォルトでは、omp_set_dynamic( 0 )が設定されていて動的なスレッド数変更には対応していないようである。ちなみに_OPENMPはOpenMP使用時に定義されるようだ。
1 2 3 |
#ifdef _OPENMP omp_set_dynamic( 1 ); #endif |
また、使ってみたところこれだけではパフォーマンスが落ちる場合があり、そのような場合には次のように使用後にこのような形でクリアしてあげるとよいようだ(追跡調査中)。
1 2 3 4 5 |
#ifdef _OPENMP omp_set_dynamic( 1 ); // なんらかのコード omp_set_dynamic( 0 ); #endif |