Visual C ++ 2010でのOpenMPの高速化

C ++でマルチスレッドコンピューティングを実装する一般的で安価な手段の1つがOpenMPです。



テクノロジーの利点は明らかです。シンプルさ。 小さく、コード変更を簡単にオフにする 最も人気のあるコンパイラの作者によるサポート: バトルチェックは成功し、ここで並列化されたコードはプロジェクトの最も人里離れたコーナーに侵入し、すべてのパフォーマンスレコードを破り、リリースの成功を確認するスマートな統計を生成します。



数年経つと、Visual Studio 2010に正常に移行し、...水たまりに座っていることに気づきます。 昨日、マルチコアプロセッサを搭載したマシンで大きなデータ配列の処理が数秒で行われた場合、今日では、独自の計算を使用して1つ以上のコアを使用するバックグラウンドアプリケーションが存在するため、アプリケーションが実質的にハングします。



なぜこれが起こり、どのように対処するのですか?





新しいOpenMP実装では、アクティブ領域を終了してアイドル状態にする前に、プログラムはI / Oが完了するまで待機し、アクティブなSpinWaitで待機します。



つまり OMP(コアごとに1)を使用してOスレッドを作成し、コアの1つが別のアプリケーションによって占有されていることを予期せずに発見した場合、1コアで2つ以上のスレッドが実行される可能性が高く、200ミリ秒の休止を挟んで切り替えます。



しかし、決済プログラムでは、追加の同期は必要ありません!

Intel開発者はこれを認識しており、kmp_set_blocktime()オプションを使用して、ユーザー自身が追加の制限を処理することをお勧めします。 残念ながら、Microsoftの同僚は、ユーザーを追加の設定と混同しないことを決定しました。



プログラムを正直なスレッドプールに書き換えると、怠inessは宗教を許しません-私の経験を活用することをお勧めします... OpenMPをMicrosoft Visual Studio 2005からオリジナルバージョンにダウングレードします。また、指示は最小限の変更でVisual Studio 2008に適しています。



最初に、Visual C ++ 2005スイートから個別のフォルダーvcomp.lib、vcompd.libにコピーします(インストール済みのディストリビューションを直接参照できますが、これはあまり便利ではありません)。 OpenMPプロジェクトを使用してプロパティに移動し、ディレクトリを「追加のライブラリディレクトリ」に追加します。 出来上がり-プロジェクトは、OpenMPの「正しい」高速実行バージョンとリンクしています。



しかし、それだけではありません。 次のように、<omp.h>のインクルードをヘッダーファイルに置き換えます。

#pragma once #include <omp.h> #ifndef __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX #define __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX "Microsoft.VC80" #endif #ifndef _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN #define _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "1fc8b3b9a1e18e3b" #endif #ifndef __OMP_CRT_ASSEMBLY_VERSION #define __OMP_CRT_ASSEMBLY_VERSION "8.0.50727.762" #endif #if defined(_DEBUG) #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".DebugOpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #else // _DEBUG #if defined(_M_IX86) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='x86' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_AMD64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='amd64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #elif defined(_M_IA64) #pragma comment(linker,"/manifestdependency:\"type='win32' " \ "name='" __OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX ".OpenMP' " \ "version='" __OMP_CRT_ASSEMBLY_VERSION "' " \ "processorArchitecture='ia64' " \ "publicKeyToken='" _OMP_VC_ASSEMBLY_PUBLICKEYTOKEN "'\"") #endif #endif // _DEBUG
      
      







これは、実行可能ファイルおよび.dllファイルにマニフェストを正しくロードするために必要です。 ロードされた.dllファイルでOpenMPが使用されている場合でも、マニフェストも実行可能ファイルに登録する必要があることを忘れないでください!



値__OMP_LIBRARIES_ASSEMBLY_NAME_PREFIX、_OMP_VC_ASSEMBLY_PUBLICKEYTOKEN、および__OMP_CRT_ASSEMBLY_VERSIONは、Visual C ++ 2005パッケージに含まれる<crtassem.h>から取得されます。



プロジェクトはまだ実行されていません-スタジオは__You_must_link_with_Microsoft_OpenMP_libraryシンボルが定義されていないという制限にinしています。



はい、コンパイラからの非常に微妙なヒントでした。



生成された.objファイルの内容を見てみましょう。 私の意見では、逆アセンブラモードのobjconvユーティリティがこれに最適です。



指定した名前でバイトサイズ変数を定義する必要があることがわかります。 残念ながら、CおよびC ++では、インポートされた文字を正確に再作成できないため、MASM32を使用する必要があります。



意味のない変数を追加します。

 PUBLIC __You_must_link_with_Microsoft_OpenMP_library data segment __You_must_link_with_Microsoft_OpenMP_library db 1 data ends end
      
      







コンパイル:

 ml /c antiomp.asm
      
      







結果のantiomp.obj出力は、OpenMPを使用するプロジェクトの追加入力に追加されます。



それだけです-動作するコードが必要です。 OpenMPバージョンを確認する方法は2つあります。

  1. アプリケーションを実行し、デバッガーを選択して、ロードされたモジュールのリストでOpenMPライブラリーを見つけます(Debug | Windows | Modules)
  2. 実行可能ファイルでvcomp100部分文字列を見つけてください。 すべてが指示に従って行われた場合、この行は




素敵な並列プログラミングをしてください!






All Articles