チュートリアル : 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] で乱数が生成され、xy の座標のセットが決定されます。この座標セットに基づいて、座標点が 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)」を参照してください。

サンプル プロジェクトを作成するには

  1. Visual Studio 2010 を実行します。

  2. ParallelPI という名前の新しい C++ Win32 コンソール アプリケーションを作成します。プリコンパイル済みヘッダーのないプロジェクトを使用します。

    1. [ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。

    2. [新しいプロジェクト] ダイアログ ボックスで、[インストールされたテンプレート] をクリックして、[Visual C++] をクリックします (Visual Studio のセットアップ方法によっては、[Visual C++] は [他の言語] ノードの下に表示されていることがあります)。

    3. テンプレートの一覧で、[Win32 コンソール アプリケーション] をクリックします。

    4. プロジェクト名として、ParallelPI.

    5. [OK] をクリックします。すると、Win32 コンソール アプリケーション ウィザードが開きます。

    6. [次へ] をクリックします。

    7. [アプリケーションの設定] の [追加のオプション] で、[プリコンパイル済みヘッダー] チェック ボックスをオフにします。

    8. [完了] をクリックし、ウィザードを閉じてプロジェクトを作成します。

  3. プロジェクトの追加のプロパティを指定します。

    1. [ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。

    2. [構成プロパティ] を展開して、[VC++ ディレクトリ] をクリックします。

      [インクルード ディレクトリ] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、MS MPI C ヘッダー ファイルの場所を指定して、その後ろにセミコロン (;) を入力します。たとえば、次のように指定します。

      C:\Program Files\Microsoft HPC Pack 2008 SDK\Include;
      
    3. [ライブラリ ディレクトリ] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、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;
      
    4. [リンカー] の下の [入力] をクリックします。

      [追加の依存ファイル] で、テキスト ボックスに表示されている一覧の先頭にカーソルを合わせて、次のように入力します。

      msmpi.lib;

    5. OpenMP によるコード サンプルを使用している場合は、次の手順を実行します。

      [構成プロパティ] で [C/C++] を展開して、[言語] をクリックします。

      [OpenMP のサポート] で、[はい (/openmp)] をクリックして、コンパイラでの OpenMP のサポートを有効にします。

    6. [OK] をクリックしてプロパティ ページを閉じます。

  4. メイン ソース ファイルで、すべてのコードを選択して削除します。

  5. 次のいずれかのコード サンプルを空のソース ファイルに貼り付けます。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();
    
    }
    
  6. [ファイル] メニューの [すべてを保存] をクリックします。

  7. [ビルド] メニューの [ソリューションのリビルド] をクリックします。

MPI クラスター デバッガーの構成と起動

アプリケーションをビルドしたら、デバッガーを構成して起動する準備は完了です。ここでは、次の 3 つのデバッグ オプションについて説明します。

  • ローカル コンピューター上の 1 つの MPI プロセスのデバッグ

  • ローカル コンピューター上の複数の MPI プロセスのデバッグ

  • クラスター上の 1 つまたは複数の MPI プロセスのデバッグ

Ee441265.note(ja-jp,VS.100).gifメモ :
MPI クラスター デバッガーでは、デバッグを行うことなく開始することはできません。Ctrl キーを押しながら F5 キーを押すと (または [デバッグ] メニューの [デバッグなしで開始] をクリックすると)、デバッグも開始されます。

ローカル コンピューター上の 1 つの MPI プロセスのデバッグ

1 つの MPI プロセスのみを使用してローカル コンピューター上でデバッグするには、他のアプリケーションをデバッグする場合と同じ手順に従います。プログラム内の希望の位置にブレークポイントを設定し、F5 キーを押してデバッガーを起動します。

MPI プログラムは IP を使用してポート経由で通信します。MPI プログラムを初めて起動すると、ファイアウォールによるセキュリティ警告が表示されて、ポートが開かれようとしていることが通知されます。この警告メッセージを参照し、システムに加える変更について確認してください。ローカル コンピューター上のデバッグを続行するには、ファイアウォールのブロックを解除する必要があります。

ローカル コンピューター上の複数の MPI プロセスのデバッグ

次の手順では、ParallelPI のローカル デバッグ セッションを開始する方法を説明します。

ローカル コンピューター上で実行されている 4 つの MPI プロセスを使用して MPI クラスター デバッガーを起動するには

  1. [ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。

  2. [構成プロパティ] を展開して、[デバッグ] をクリックします。

  3. [起動するデバッガー] ボックスの一覧の [MPI クラスター デバッガー] をクリックします。

  4. [実行環境] ボックスの一覧の [HPC ノードの編集] をクリックします。すると、[ノード セレクター] ダイアログ ボックスが開きます。

  5. [ヘッド ノード] ボックスの一覧の [localhost] をクリックします。

  6. [プロセス数] ボックスの一覧の [4] をクリックします。

  7. [OK] をクリックして変更内容を保存し、[ノード セレクター] ダイアログ ボックスを閉じます。

  8. ParallelPI は、実行を繰り返す回数を指定する 1 つの引数を受け取ります。既定値は、50,000,000 回に設定されています。ローカル デバッグ セッションの場合は、次のようにこの繰り返し回数を 5,000 に減らしてください。

    [アプリケーションの引数] で、「5000」と入力します。

  9. [OK] をクリックして変更内容を保存し、[プロパティ ページ] を閉じます。

  10. パラレル for ループの本体内にブレークポイントを設定します。

  11. F5 キーを押してデバッガーを起動します。

  12. 5 つのコンソール ウィンドウが表示されます。[cmd.exe] ウィンドウが 1 つと、[ParallelPI.exe] ウィンドウが 4 つ (起動したプロセスごとに 1 つ) です。ランク 0 のプロセスに対応するコンソール ウィンドウには、繰り返しの回数と算出された円周率の近似値が表示されます。

  13. [デバッグ] メニューの [ウィンドウ] をポイントし、[プロセス] をクリックします。

  14. [プロセス] ウィンドウで任意のプロセスをダブルクリックして、デバッグ対象のアクティブ プロセスを設定します。

Ee441265.note(ja-jp,VS.100).gifメモ :
複数のプロセスをデバッグしている場合、既定では、デバッグ対象のすべてのプロセスにブレークポイントが適用されます。意図していない場所でプロセスがブレークされないようにするには、[1 つのプロセスがブレークするとき、他のプロセスもブレークする] チェック ボックスをオフにします([ツール] メニューで、[オプション]、[デバッグ] の順にクリックします)。ブレーク動作の変更方法の詳細については、「方法 : 実行を中断する」を参照してください。

クラスター上の 1 つまたは複数の MPI プロセスのデバッグ

クラスター上で MPI デバッガーを起動すると、デバッガーからクラスターにアプリケーションがジョブとして送信されます。現在のプロジェクト (x86 または x64、デバッグまたはリリース) に適した Visual C ランタイムが、計算ノードの作業ディレクトリに配置されている必要があります。適切なランタイムが計算ノード上にまだない場合は、[追加の配置ファイル] プロパティを指定して、デバッガーの配置にそれらを含める必要があります。次の手順では、OpenMP デバッグ ランタイム DLL を配置する方法を説明しています。既定では、C ランタイム (CRT) ライブラリは、MPI クラスター デバッガーを起動したときに配置されます。適切なランタイムがない場合は、アプリケーションを実行しようとすると、サイドバイサイド エラーが発生します。OpenMP ランタイムが含まれていない場合、ブレークポイントはヒットしません。

クラスター上の MPI デバッガーを起動するには

  1. [ソリューション エクスプローラー] で、[ParallelPI] を右クリックして [プロパティ] をクリックします。すると、[プロパティ ページ] ダイアログ ボックスが開きます。

  2. [構成プロパティ] を展開して、[デバッグ] をクリックします。

  3. [起動するデバッガー] ボックスの一覧の [MPI クラスター デバッガー] をクリックします。

  4. [実行環境] ボックスの一覧の [HPC ノードの編集] をクリックします。すると、[ノード セレクター] ダイアログ ボックスが開きます。

  5. [ヘッド ノード] ボックスの一覧の、使用するクラスターのヘッド ノードの名前をクリックします。

    ヘッド ノードの一覧の内容は、Active Directory のドメイン コントローラーから取得されます。参加しているドメイン内のクラスターのみがこの一覧に表示されます。目的のヘッド ノードが表示されていない場合は、そのヘッド ノードの名前または IPv4 アドレスをプロパティ フィールドに入力します。

  6. [プロセス数] ボックスの一覧の [4] をクリックします。

  7. [1 つのプロセスをスケジュールする頻度] で、プロセスの割り当て方法を選択します。[コア]、[ソケット]、または [ノード] ごとに 1 つのプロセスを割り当てることができます。

  8. [OK] をクリックして変更内容を保存し、[ノード セレクター] ダイアログ ボックスを閉じます。

  9. [配置ディレクトリ] で、ヘッド ノード上の共有ディレクトリを指定します。配置ディレクトリが存在せず、指定されたルート ディレクトリ内の書き込みアクセス許可がユーザーに付与されていると、配置ディレクトリは自動的に作成されます。

    HPC Pack 2008 をヘッド ノードにインストールすると、CcpSpoolDir ディレクトリ共有リソースが作成されます。たとえば、次のように入力します (<myHeadNode> は、使用しているクラスターの名前です)。

    \\<myHeadNode>\CcpSpoolDir\

  10. [作業ディレクトリ] で、各計算ノード上のローカル作業ディレクトリを指定します。たとえば、次のように入力します (<myUserName> は、自分のユーザー名です)。

    C:\Users\<myUserName>\ParallelPI

  11. OpenMP によるサンプル コードを使用している場合は、OpenMP のデバッグ ランタイム DLL ファイル (Microsoft.VC100.DebugOpenMP\vcomp100d.dll) を追加します。

    1. [追加の配置ファイル] の一覧の [<ファイルの編集...>] をクリックします。すると、[ファイルとフォルダーのセレクター] ダイアログ ボックスが開きます。

    2. [ファイルの追加] をクリックし、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

    3. [OK] をクリックしてファイルを追加し、[ファイルとフォルダーのセレクター] ダイアログ ボックスを閉じます。

  12. [OK] をクリックして変更内容を保存し、[プロパティ ページ] を閉じます。

  13. パラレル for ループの本体内にブレークポイントを設定します。

  14. F5 キーを押してデバッガーを起動します。

  15. ジョブをクラスターに送信しようとしているため、クラスターに接続するためのパスワードの入力が求められます。パスワードを入力し、Enter キーを押します。

  16. デバッガーが起動したら、プロセス ウィンドウでプロセスの配置を確認します。各プロセスについて、[トランスポート修飾子] 列で、そのプロセスが実行されている計算ノードを確認します。

付録 : 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 アプリケーションのデバッグ

その他の技術情報

デバッガーのロードマップ
mpiexec Command Reference