チュートリアル : Visual Studio 2010 での MPI クラスター デバッガーの起動
このチュートリアルでは、ローカル コンピューター上および Microsoft Windows HPC Server 2008 クラスター上で MPI クラスター デバッガー セッションをセットアップして起動する方法を説明します。Message Passing Interface (MPI) および Open Multi-Processing (OpenMP) のアプリケーション プログラミング インターフェイス (API) を使用するアプリケーションを作成するために必要な手順とサンプル コードについても紹介します。
このチュートリアルの内容 :
MPI クラスター デバッガーを使用するための要件
Visual Studio 2010 での C++ MPI サンプル プロジェクトの作成
MPI クラスター デバッガーの構成と起動
付録 : Visual Studio によって配置されるアプリケーション バイナリ以外のファイル (および要求に応じて CRT)
MPI クラスター デバッガーを使用するための要件
Visual Studio 2010 Professional エディション以上 (リモート デバッガーを含む) が開発用コンピューターにインストールされていること。
クラスター上の管理者アクセス許可が付与されていること。
Visual Studio から、デバッグ セッションの実行場所となる計算ノードにアクセスできること。次の場合は、必要なノードにアクセスできます。
クラスター ヘッド ノードまたは専用ログイン ノード上でアプリケーションを開発している場合。
使用しているクラスターの計算ノードがエンタープライズ ネットワーク (トポロジ 2、4、または 5) に接続されており、開発用コンピューターが同じドメインに参加しているか、クラスター ドメインと信頼関係を持つドメインに参加している場合。
クライアント コンピューターから HPC クラスターにアプリケーションを送信する場合は、Microsoft HPC Pack 2008 がインストールされていること。
Microsoft Message Passing Interface を使用して MPI プログラムを作成する場合は、開発用コンピューターに Windows HPC Server 2008 SDK がインストールされていること。
Visual Studio 2010 での C++ MPI サンプル プロジェクトの作成
ここで紹介するサンプル コードは、モンテカルロ シミュレーションを使用して円周率の近似値を計算する並列アプリケーションのものです。
このサンプル コードは、各 MPI プロセスで 50,000,000 回にわたって繰り返し実行されます。このサンプル コードでは、それぞれの実行時に区間 [0,1] で乱数が生成され、x と y の座標のセットが決定されます。この座標セットに基づいて、座標点が x2 + y2 = 1 の線より下に位置するかどうかが判定されます。座標点がこの線より下に位置する場合は、変数 count の値が 1 だけ増分されます。各 MPI プロセスの count の値の合計は、変数 result に格納されます。この線より下に位置する座標点の合計数 (result) に 4 を掛けてから、繰り返しの合計回数で割ることにより、円周率の近似値が算出されます。
次の手順には、モンテカルロ シミュレーションの 2 つの実装が含まれています。
1 つ目のサンプルでは MPI と OpenMP を使用しています。OpenMP の詳細については、「OpenMP in Visual C++」を参照してください。
2 つ目のサンプルでは MPI と Parallel Patterns Library (PPL) を使用しています。PPL の詳細については、「Parallel Patterns Library (PLL)」を参照してください。
サンプル プロジェクトを作成するには
Visual Studio 2010 を実行します。
ParallelPI という名前の新しい C++ Win32 コンソール アプリケーションを作成します。プリコンパイル済みヘッダーのないプロジェクトを使用します。
[ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。
[新しいプロジェクト] ダイアログ ボックスで、[インストールされたテンプレート] をクリックして、[Visual C++] をクリックします (Visual Studio のセットアップ方法によっては、[Visual C++] は [他の言語] ノードの下に表示されていることがあります)。
テンプレートの一覧で、[Win32 コンソール アプリケーション] をクリックします。
プロジェクト名として、ParallelPI.
[OK] をクリックします。すると、Win32 コンソール アプリケーション ウィザードが開きます。
[次へ] をクリックします。
[アプリケーションの設定] の [追加のオプション] で、[プリコンパイル済みヘッダー] チェック ボックスをオフにします。
[完了] をクリックし、ウィザードを閉じてプロジェクトを作成します。
プロジェクトの追加のプロパティを指定します。
[ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。
[構成プロパティ] を展開して、[VC++ ディレクトリ] をクリックします。
[インクルード ディレクトリ] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、MS MPI C ヘッダー ファイルの場所を指定して、その後ろにセミコロン (;) を入力します。たとえば、次のように指定します。
C:\Program Files\Microsoft HPC Pack 2008 SDK\Include;
[ライブラリ ディレクトリ] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、Microsoft HPC Pack 2008 SDK ライブラリ ファイルの場所を指定して、その後ろにセミコロン (;) を入力します。
たとえば、32 ビット アプリケーションをビルドおよびデバッグする場合は、次のように指定します。
C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\i386;
64 ビット アプリケーションをビルドおよびデバッグする場合は、次のように指定します。
C:\Program Files\Microsoft HPC Pack 2008 SDK\Lib\amd64;
[リンカー] の下の [入力] をクリックします。
[追加の依存ファイル] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、次のように入力します。
msmpi.lib;
OpenMP によるコード サンプルを使用している場合は、次の手順を実行します。
[構成プロパティ] で [C/C++] を展開して、[言語] をクリックします。
[OpenMP のサポート] で、[はい (/openmp)] をクリックして、コンパイラでの OpenMP のサポートを有効にします。
[OK] をクリックしてプロパティ ページを閉じます。
メイン ソース ファイルで、すべてのコードを選択して削除します。
次のいずれかのコード サンプルを空のソース ファイルに貼り付けます。1 つ目のサンプルでは MPI と OpenMP を使用しており、2 つ目のサンプルでは MPI と Parallel Patterns Library (PPL) を使用しています。
次のコード サンプルでは MPI と OpenMP を使用しています。
ThrowDarts
関数は、OpenMP のパラレルfor
ループを使用して、可能であればマルチコア ハードウェアを利用します。// ParallelPI.cpp : Defines the entry point for the MPI application. // #include "mpi.h" #include "stdio.h" #include "stdlib.h" #include "limits.h" #include "omp.h" #include <random> int ThrowDarts(int iterations) { std::tr1::uniform_real<double> MyRandom; std::tr1::minstd_rand0 MyEngine; double RandMax = MyRandom.max(); int count = 0; omp_lock_t MyOmpLock; omp_init_lock(&MyOmpLock); //Compute approximation of pi on each node #pragma omp parallel for for(int i = 0; i < iterations; ++i) { double x, y; x = MyRandom(MyEngine)/RandMax; y = MyRandom(MyEngine)/RandMax; if(x*x + y*y < 1.0) { omp_set_lock(&MyOmpLock); count++; omp_unset_lock(&MyOmpLock); } } omp_destroy_lock(&MyOmpLock); return count; } int main(int argc, char* argv[]) { int rank; int size; int iterations; int count; int result; double time; MPI_Status s; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Comm_size(MPI_COMM_WORLD,&size); if(rank == 0) { //Rank 0 asks the number of iterations from the user. iterations = 50000000; if(argc > 1) { iterations = atoi(argv[1]); } printf("Executing %d iterations.\n", iterations); fflush(stdout); } //Broadcast the number of iterations to execute. if(rank == 0) { for(int i = 1; i < size; ++i) { MPI_Ssend(&iterations, 1, MPI_INT, i, 0, MPI_COMM_WORLD); } } else { MPI_Recv(&iterations, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &s); } //MPI_Bcast(&iterations, 1, MPI_INT, 0, MPI_COMM_WORLD); count = ThrowDarts(iterations); //Gather and sum results if(rank != 0) { MPI_Ssend(&count, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); } else { for(int i = 1; i < size; ++i) { int TempCount = 0; MPI_Recv(&TempCount, 1, MPI_INT, i, 0, MPI_COMM_WORLD, &s); count += TempCount; } } result = count; //MPI_Reduce(&count, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if(rank == 0) { printf("The value of PI is approximated to be: %16f", 4*((float)result/(float)(iterations*size))); } MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); return 0; }
次のコード サンプルでは、OpenMP の代わりに Parallel Patterns Library (PPL) を使用しており、ポイントツーポイント操作の代わりに MPI 集合的操作を使用しています。
// ParallelPI.cpp : Defines the entry point for the MPI application. // #include "mpi.h" #include "stdio.h" #include "stdlib.h" #include "limits.h" #include <ppl.h> #include <random> #include <time.h> using namespace Concurrency; int ThrowDarts(int iterations) { combinable<int> count; int result = 0; parallel_for(0, iterations, [&](int i){ std::tr1::uniform_real<double> MyRandom; double RandMax = MyRandom.max(); std::tr1::minstd_rand0 MyEngine; double x, y; MyEngine.seed((unsigned int)time(NULL)); x = MyRandom(MyEngine)/RandMax; y = MyRandom(MyEngine)/RandMax; if(x*x + y*y < 1.0) { count.local() += 1; } }); result = count.combine([](int left, int right) { return left + right; }); return result; } void main(int argc, char* argv[]) { int rank; int size; int iterations; int count; int result; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD,&rank); MPI_Comm_size(MPI_COMM_WORLD,&size); if(rank == 0) { //Rank 0 reads the number of iterations from the command line. //50M iterations is the default. iterations = 50000000; if(argc > 1) { iterations = atoi(argv[argc-1]); } printf("Executing %d iterations on %d nodes.\n", iterations, size); fflush(stdout); } //Broadcast the number of iterations to execute. MPI_Bcast(&iterations, 1, MPI_INT, 0, MPI_COMM_WORLD); count = ThrowDarts(iterations); //Gather and sum results MPI_Reduce(&count, &result, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD); if(rank == 0) { printf("The value of PI is approximated to be: %16f", 4*((double)result/(double)(iterations*size))); } MPI_Barrier(MPI_COMM_WORLD); MPI_Finalize(); }
[ファイル] メニューの [すべてを保存] をクリックします。
[ビルド] メニューの [ソリューションのリビルド] をクリックします。
MPI クラスター デバッガーの構成と起動
アプリケーションをビルドしたら、デバッガーを構成して起動する準備は完了です。ここでは、次の 3 つのデバッグ オプションについて説明します。
ローカル コンピューター上の 1 つの MPI プロセスのデバッグ
ローカル コンピューター上の複数の MPI プロセスのデバッグ
クラスター上の 1 つまたは複数の MPI プロセスのデバッグ
メモ : |
---|
MPI クラスター デバッガーでは、デバッグを行うことなく開始することはできません。Ctrl キーを押しながら F5 キーを押すと (または [デバッグ] メニューの [デバッグなしで開始] をクリックすると)、デバッグも開始されます。 |
ローカル コンピューター上の 1 つの MPI プロセスのデバッグ
1 つの MPI プロセスのみを使用してローカル コンピューター上でデバッグするには、他のアプリケーションをデバッグする場合と同じ手順に従います。プログラム内の希望の位置にブレークポイントを設定し、F5 キーを押してデバッガーを起動します。
MPI プログラムは IP を使用してポート経由で通信します。MPI プログラムを初めて起動すると、ファイアウォールによるセキュリティ警告が表示されて、ポートが開かれようとしていることが通知されます。この警告メッセージを参照し、システムに加える変更について確認してください。ローカル コンピューター上のデバッグを続行するには、ファイアウォールのブロックを解除する必要があります。
ローカル コンピューター上の複数の MPI プロセスのデバッグ
次の手順では、ParallelPI のローカル デバッグ セッションを開始する方法を説明します。
ローカル コンピューター上で実行されている 4 つの MPI プロセスを使用して MPI クラスター デバッガーを起動するには
[ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。
[構成プロパティ] を展開して、[デバッグ] をクリックします。
[起動するデバッガー] ボックスの一覧の [MPI クラスター デバッガー] をクリックします。
[実行環境] ボックスの一覧の [HPC ノードの編集] をクリックします。すると、[ノード セレクター] ダイアログ ボックスが開きます。
[ヘッド ノード] ボックスの一覧の [localhost] をクリックします。
[プロセス数] ボックスの一覧の [4] をクリックします。
[OK] をクリックして変更内容を保存し、[ノード セレクター] ダイアログ ボックスを閉じます。
ParallelPI は、実行を繰り返す回数を指定する 1 つの引数を受け取ります。既定値は、50,000,000 回に設定されています。ローカル デバッグ セッションの場合は、次のようにこの繰り返し回数を 5,000 に減らしてください。
[アプリケーションの引数] で、「5000」と入力します。
[OK] をクリックして変更内容を保存し、[プロパティ ページ] を閉じます。
パラレル
for
ループの本体内にブレークポイントを設定します。F5 キーを押してデバッガーを起動します。
5 つのコンソール ウィンドウが表示されます。[cmd.exe] ウィンドウが 1 つと、[ParallelPI.exe] ウィンドウが 4 つ (起動したプロセスごとに 1 つ) です。ランク 0 のプロセスに対応するコンソール ウィンドウには、繰り返しの回数と算出された円周率の近似値が表示されます。
[デバッグ] メニューの [ウィンドウ] をポイントし、[プロセス] をクリックします。
[プロセス] ウィンドウで任意のプロセスをダブルクリックして、デバッグ対象のアクティブ プロセスを設定します。
メモ : |
---|
複数のプロセスをデバッグしている場合、既定では、デバッグ対象のすべてのプロセスにブレークポイントが適用されます。意図していない場所でプロセスがブレークされないようにするには、[1 つのプロセスがブレークするとき、他のプロセスもブレークする] チェック ボックスをオフにします([ツール] メニューで、[オプション]、[デバッグ] の順にクリックします)。ブレーク動作の変更方法の詳細については、「方法 : 実行を中断する」を参照してください。 |
クラスター上の 1 つまたは複数の MPI プロセスのデバッグ
クラスター上で MPI デバッガーを起動すると、デバッガーからクラスターにアプリケーションがジョブとして送信されます。現在のプロジェクト (x86 または x64、デバッグまたはリリース) に適した Visual C ランタイムが、計算ノードの作業ディレクトリに配置されている必要があります。適切なランタイムが計算ノード上にまだない場合は、[追加の配置ファイル] プロパティを指定して、デバッガーの配置にそれらを含める必要があります。次の手順では、OpenMP デバッグ ランタイム DLL を配置する方法を説明しています。既定では、C ランタイム (CRT) ライブラリは、MPI クラスター デバッガーを起動したときに配置されます。適切なランタイムがない場合は、アプリケーションを実行しようとすると、サイドバイサイド エラーが発生します。OpenMP ランタイムが含まれていない場合、ブレークポイントはヒットしません。
クラスター上の MPI デバッガーを起動するには
[ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。
[構成プロパティ] を展開して、[デバッグ] をクリックします。
[起動するデバッガー] ボックスの一覧の [MPI クラスター デバッガー] をクリックします。
[実行環境] ボックスの一覧の [HPC ノードの編集] をクリックします。すると、[ノード セレクター] ダイアログ ボックスが開きます。
[ヘッド ノード] ボックスの一覧の、使用するクラスターのヘッド ノードの名前をクリックします。
ヘッド ノードの一覧の内容は、Active Directory のドメイン コントローラーから取得されます。参加しているドメイン内のクラスターのみがこの一覧に表示されます。目的のヘッド ノードが表示されていない場合は、そのヘッド ノードの名前または IPv4 アドレスをプロパティ フィールドに入力します。
[プロセス数] ボックスの一覧の [4] をクリックします。
[1 つのプロセスをスケジュールする頻度] で、プロセスの割り当て方法を選択します。[コア]、[ソケット]、または [ノード] ごとに 1 つのプロセスを割り当てることができます。
[OK] をクリックして変更内容を保存し、[ノード セレクター] ダイアログ ボックスを閉じます。
[配置ディレクトリ] で、ヘッド ノード上の共有ディレクトリを指定します。配置ディレクトリが存在せず、指定されたルート ディレクトリ内の書き込みアクセス許可がユーザーに付与されていると、配置ディレクトリは自動的に作成されます。
HPC Pack 2008 をヘッド ノードにインストールすると、CcpSpoolDir ディレクトリ共有リソースが作成されます。たとえば、次のように入力します (<myHeadNode> は、使用しているクラスターの名前です)。
\\<myHeadNode>\CcpSpoolDir\
[作業ディレクトリ] で、各計算ノード上のローカル作業ディレクトリを指定します。たとえば、次のように入力します (<myUserName> は、自分のユーザー名です)。
C:\Users\<myUserName>\ParallelPI
OpenMP によるサンプル コードを使用している場合は、OpenMP のデバッグ ランタイム DLL ファイル (Microsoft.VC100.DebugOpenMP\vcomp100d.dll) を追加します。
[追加の配置ファイル] の一覧の [<ファイルの編集...>] をクリックします。すると、[ファイルとフォルダーのセレクター] ダイアログ ボックスが開きます。
[ファイルの追加] をクリックし、Microsoft.VC100.DebugOpenMP\vcomp100d.dll に移動して、このファイルを選択してから [開く] をクリックします。
たとえば、x86 ベースのコンピューターでは、64 ビット版 Windows Server 2008 オペレーティング システム上の既定の場所は次のとおりです。
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC100.DebugOpenMP\ vcomp100d.dll
[OK] をクリックしてファイルを追加し、[ファイルとフォルダーのセレクター] ダイアログ ボックスを閉じます。
[OK] をクリックして変更内容を保存し、[プロパティ ページ] を閉じます。
パラレル
for
ループの本体内にブレークポイントを設定します。F5 キーを押してデバッガーを起動します。
ジョブをクラスターに送信しようとしているため、クラスターに接続するためのパスワードの入力が求められます。パスワードを入力し、Enter キーを押します。
デバッガーが起動したら、プロセス ウィンドウでプロセスの配置を確認します。各プロセスについて、[トランスポート修飾子] 列で、そのプロセスが実行されている計算ノードを確認します。
付録 : Visual Studio によって配置されるアプリケーション バイナリ以外のファイル (および要求に応じて CRT)
DebuggerProxy.dll
DebuggerProxy.dll.manifest
Delete_from_workdir.bat : 配置されたファイルを削除するためのスクリプト
Deploy_to_workdir.bat : ファイルを配置ディレクトリから作業ディレクトリにコピーするためのスクリプト
dbghelp.dll
mcee.dll
Mpishim.bat : リモート デバッガーを起動するためのスクリプト
Mpishim.exe : IDE と Msvsmon.exe の間の通信を調整するプログラム
Msvsmon.exe : リモート デバッガー
Msvsmon.exe.config
PfxTaskProvider.dll
symsrv.dll
symsrv.yes
vbdebug.dll
1041\msdbgui.dll
1041\vbdebugui.dll
関連項目
概念
MPI クラスター デバッガーの構成プロパティ
HPC クラスター上での MPI アプリケーションのデバッグ