チュートリアル : C++ AMP アプリケーションのデバッグ

このトピックでは、(GPU) 単位を処理するグラフィックスを利用するには、C++ で加速された大きい並列 AMP (C++) を使用するアプリケーションをデバッグする方法について説明します。これは、整数の大きな配列を要約して並列プログラムを犠牲に使用します。このチュートリアルでは、次の作業について説明します。

  • GPU のデバッガーを起動します。

  • GPU を検査するのは、GPU のスレッド] ウィンドウに一覧表示されます。

  • [並列スタック] ウィンドウを使用して同時に複数 GPU の呼び出し履歴を確認することです。

  • 複数のスレッドを越えて一つの式の値を同時に検査する並列 [ウォッチ] ウィンドウを使用する。

  • GPU のスレッドにフラグを設定し、固定、解放、およびグループ化します。

  • コードの特定の場所にタイルのすべてのスレッドが実行されます。

必須コンポーネント

このチュートリアルを開始する前に:

  • C++ AMP の概要 を読み取りました。

  • 行番号がテキスト エディターに表示されることを確認します。詳細については、「方法 : エディターで行番号を表示する」を参照してください。

  • ソフトウェアのエミュレーターでデバッグをサポートするように Windows 8 か Windows Server 2012 が実行されていることを確認します。

[!メモ]

お使いのマシンで、Visual Studio ユーザー インターフェイスの一部の要素の名前や場所が、次の手順とは異なる場合があります。これらの要素は、使用している Visual Studio のエディションや独自の設定によって決まります。詳細については、「Visual Studio の設定」を参照してください。

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

  1. Visual Studio を起動します。

  2. メニュー バーで、[ファイル]新規、**[プロジェクト]**を選択します。

  3. テンプレート ペインの [インストール済み] の下に、**[Visual C++]**を選択します。

  4. [Win32 コンソール アプリケーション]を選択し、[名前] ボックスの AMPMapReduce を入力し、を [OK] のボタンをクリックします。

  5. [次へ] ボタンをクリックします。

  6. [プリコンパイル済みヘッダー] のチェック ボックスをオフにし、[完了] のボタンをクリックします。

  7. [ソリューション エクスプローラー]、プロジェクトから削除 targetver.h、stdafx.h では、stdafx.cpp。

  8. 開いている AMPMapReduce.cpp は次のコードの内容を置き換えます。

    // AMPMapReduce.cpp defines the entry point for the program.
    // The program performs a parallel-sum reduction that computes the sum of an array of integers. 
    
    #include <stdio.h>
    #include <tchar.h>
    #include <amp.h>
    
    const int BLOCK_DIM = 32;
    
    using namespace concurrency;
    
    void sum_kernel_tiled(tiled_index<BLOCK_DIM> t_idx, array<int, 1> &A, int stride_size) restrict(amp)
    {
        tile_static int localA[BLOCK_DIM];
    
        index<1> globalIdx = t_idx.global * stride_size;
        index<1> localIdx = t_idx.local;
    
        localA[localIdx[0]] =  A[globalIdx];
    
        t_idx.barrier.wait();
    
        // Aggregate all elements in one tile into the first element.
        for (int i = BLOCK_DIM / 2; i > 0; i /= 2) 
        {
            if (localIdx[0] < i) 
            {
    
                localA[localIdx[0]] += localA[localIdx[0] + i];
            }
    
            t_idx.barrier.wait();
        }
    
        if (localIdx[0] == 0)
        {
            A[globalIdx] = localA[0];
        }
    }
    
    int size_after_padding(int n)
    {
        // The extent might have to be slightly bigger than num_stride to 
        // be evenly divisible by BLOCK_DIM. You can do this by padding with zeros.
        // The calculation to do this is BLOCK_DIM * ceil(n / BLOCK_DIM)
        return ((n - 1) / BLOCK_DIM + 1) * BLOCK_DIM;
    }
    
    int reduction_sum_gpu_kernel(array<int, 1> input) 
    {
        int len = input.extent[0];
    
        //Tree-based reduction control that uses the CPU.
        for (int stride_size = 1; stride_size < len; stride_size *= BLOCK_DIM) 
        {
            // Number of useful values in the array, given the current
            // stride size.
            int num_strides = len / stride_size;  
    
            extent<1> e(size_after_padding(num_strides));
    
            // The sum kernel that uses the GPU.
            parallel_for_each(extent<1>(e).tile<BLOCK_DIM>(), [&input, stride_size] (tiled_index<BLOCK_DIM> idx) restrict(amp)
            {
                sum_kernel_tiled(idx, input, stride_size);
            });
        }
    
        array_view<int, 1> output = input.section(extent<1>(1));
        return output[0];
    }
    
    int cpu_sum(const std::vector<int> &arr) {
        int sum = 0;
        for (size_t i = 0; i < arr.size(); i++) {
            sum += arr[i];
        }
        return sum;
    }
    
    std::vector<int> rand_vector(unsigned int size) {
        srand(2011);
    
        std::vector<int> vec(size);
        for (size_t i = 0; i < size; i++) {
            vec[i] = rand();
        }
        return vec;
    }
    
    array<int, 1> vector_to_array(const std::vector<int> &vec) {
        array<int, 1> arr(vec.size());
        copy(vec.begin(), vec.end(), arr);
        return arr;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::vector<int> vec = rand_vector(10000);
        array<int, 1> arr = vector_to_array(vec);
    
        int expected = cpu_sum(vec);
        int actual = reduction_sum_gpu_kernel(arr);
    
        bool passed = (expected == actual);
        if (!passed) {
            printf("Actual (GPU): %d, Expected (CPU): %d", actual, expected);
        }
        printf("sum: %s\n", passed ? "Passed!" : "Failed!"); 
    
        getchar();
    
        return 0;
    }
    
  9. メニュー バーで、[ファイル]、**[すべて保存]**を選択します。

  10. **[ソリューション エクスプローラー]では、AMPMapReduceのショートカット メニューを開き、[プロパティ]**を選択します。

  11. [プロパティ ページ] のダイアログ ボックスで、**[構成プロパティ]の下に、C/C++[プリコンパイル済みヘッダー]**を選択します。

  12. [プリコンパイル済みヘッダー] のプロパティに、選択 **[プリコンパイル済みヘッダーを使用しない]**は、を [OK] のボタンをクリックします。

  13. メニュー バーで、[ビルド]、**[ソリューションのビルド]**を選択します。

CPU コードのデバッグ

この手順では、このアプリケーションの CPU コードが正しいことを確認するためののローカル Windows デバッガーを使用します。特に関心このアプリケーションの CPU コード セグメントは reduction_sum_gpu_kernel 関数の for のループです。これは、GPU で実行されるツリー ベースの並列低下を制御します。

CPU コードをデバッグするには

  1. **[ソリューション エクスプローラー]では、AMPMapReduceのショートカット メニューを開き、[プロパティ]**を選択します。

  2. [プロパティ ページ] のダイアログ ボックスで、[構成プロパティ]の下に、[デバッグ]を選択します。[ローカル Windows デバッガー][起動するデバッガ] の一覧で、が選択されていることを確認します。

  3. コード エディターに戻ります。

  4. 次の図に示すコード行にブレークポイントを設定します (約 67 行を 70 行数)。

    CPU のブレークポイント

    CPU ブレークポイント

  5. メニュー バーで、[デバッグ][デバッグ開始] の順に選択します。

  6. [ローカル] のペインで、70 行のブレークポイントに達するまで stride_size の値を確認します。

  7. メニュー バーで、[デバッグ]、**[デバッグの停止]**を選択します。

GPU コードのデバッグ

ここでは sum_kernel_tiled の関数に含まれるコードである GPU コードをデバッグする方法について説明します。GPU コードは、「ブロック」の整数の合計を並行して計算します。

GPU コードをデバッグするには

  1. **[ソリューション エクスプローラー]では、AMPMapReduceのショートカット メニューを開き、[プロパティ]**を選択します。

  2. [プロパティ ページ] のダイアログ ボックスで、**[構成プロパティ]の下に、[デバッグ]**を選択します。

  3. [起動するデバッガ] の一覧で、を選択 [ローカル Windows デバッガー]

  4. [デバッガーの種類] の一覧で、を選択 [GPU のみ]

  5. [OK] を選択します。

  6. 次の図に示すように、30 行にブレークポイントを設定します。

    GPU のブレークポイント

    GPU ブレークポイント

  7. メニュー バーで、[デバッグ][デバッグ開始] の順に選択します。行 67 および 70 の CPU コードのブレークポイントは、GPU のデバッグ中にこれらのコード行が CPU で実行されるため実行されません。

GPU のスレッド] ウィンドウを使用するには

  1. GPU のスレッド] ウィンドウ、メニュー バーで開くには、[デバッグ]Windows、**[GPU スレッド]**を選択します。

    GPU が表示される GPU のスレッドのスレッドでウィンドウの状態を確認できます。

  2. Visual Studio の下部に GPU のスレッドのペインをドッキングします。タイル、およびスレッドのテキスト ボックスを表示するに スレッドのスイッチを展開します。*** のボタンをクリックします。GPU を次の図に示すように、ウィンドウは、アクティブとブロック GPU のスレッドの総数、スレッド。

    GPU はスレッド] ウィンドウ

    4 つのアクティブ スレッドがある [GPU スレッド] ウィンドウ

    この計算に割り当てた 313 のタイルがあります。各タイルが 32 のスレッドが含まれています。ローカル GPU のデバッグがソフトウェアのエミュレーターで発生するため、4 種類のアクティブな GPU のスレッドがあります。4 種類のスレッドは命令を同時に実行し、次の手順を同時に進みます。

    GPU のスレッドのペインで、アクティブな 4 種類の GPU のスレッドと 21 (t_idx.barrier.wait();) に定義されている tile_barrier::wait のステートメント ブロックで、GPU のスレッドが 28 行になります。32、GPU のすべてのスレッドは最初のタイル、tile[0]に属しています。現在のスレッドを含む行への矢印のポインター。別のスレッドに切り替えるには、次のメソッドのうち 1 つがの使用:

    • GPU のウィンドウでスレッドを切り替えるにスレッドの行でショートカット メニューを開き、**[スレッドに切り替え]**を選択します。行は複数のスレッドを表す場合、スレッドの座標に従って一つのスレッドに切り替えます。

    • スレッドのタイル、およびスレッドの値を対応するテキスト ボックスに入力し、を [スレッドの切り替え] のボタンをクリックします。

    [呼び出し履歴] ウィンドウは、GPU 現在のスレッドの呼び出し履歴を表示します。

[並列スタック] ウィンドウを使用する

  1. [並列スタック] ウィンドウは、メニュー バーで開くには、[デバッグ]Windows、**[並列スタック]**を選択します。

    同時に複数 GPU のスレッドのスタック フレームを検査するために、[並列スタック] ウィンドウを使用できます。

  2. Visual Studio の下部に [並列スタック] ウィンドウをドッキングします。

  3. [スレッド] が左上隅の一覧で、が選択されていることを確認します。次の図では、並列は、GPU のスレッドでウィンドウに説明したことをウィンドウには、GPU のスレッドの呼び出し履歴でローカライズされたビューが表示されます。

    [並列スタック] ウィンドウ

    4 つのアクティブ スレッドがある [並列スタック] ウィンドウ

    次に 32 のスレッドからは parallel_for_each の関数呼び出しのラムダのステートメントとデクリメントが並列実行される sum_kernel_tiled の関数に _kernel_stub 行きました。32 から 28 のスレッドは tile_barrier::wait のステートメントに他の 4 種類のスレッドが sum_kernel_tiled の関数に 30 行でアクティブなまま一方、進行状況および行 22 ブロックです。

    [並列スタック] ウィンドウの豊富なデータヒント、GPU のスレッドにウィンドウで使用できる GPU のスレッドのプロパティを確認できます。これを行うには、sum_kernel_tiledのスタック フレーム上にマウス ポインターを置きます。次の図は、データヒントを示します。

    GPU のスレッド データヒント

    [並列スタック] ウィンドウのデータヒント

    [並列スタック ウィンドウの詳細については、[並列スタック] ウィンドウの使用を参照してください。

並列 [ウォッチ] ウィンドウを使用する

  1. 並列ウォッチ ウィンドウは、メニュー バーで開くには、[デバッグ]Windows[並列ウォッチ]、**[並列ウォッチ 1]**を選択します。

    複数のスレッド間での式の値を検査するために、並列 [ウォッチ] ウィンドウを使用できます。

  2. Visual Studio の下部に並列ウォッチ 1 のペインをドッキングします。並列ウォッチ ウィンドウのテーブルに 32 の行が含まれています。各は、GPU のスレッド] ウィンドウと [並列スタック] ウィンドウの両方で、GPU のスレッドに対応します。次に、値を、すべての 32、GPU のスレッド間で検査する式を入力できます。

  3. [ウォッチ式の追加] の列ヘッダーを選択し、localIdxを入力し、Enter キーを選択します。

  4. [ウォッチ式の追加] の列ヘッダーをもう一度選択し、globalIdxを入力し、Enter キーを選択します。

  5. [ウォッチ式の追加] の列ヘッダーをもう一度選択し、localA [localIdx [0]]を入力し、Enter キーを選択します。

    指定された式によって対応する列ヘッダーの選択によって並べ替えることができます。

    列を並べ替えるに localA [localIdx [0]] の列ヘッダーを選択します。次の図は **localA [localIdx [0]]**で並べ替える場合の結果を示します。

    結果の並べ替え

    結果が並べ替えられている [並列ウォッチ] ウィンドウ

    Excel に Excel のボタンを選択し、**[Excel で開く]**を選択することで、並列ウォッチ ウィンドウの内容をエクスポートできます。開発用コンピューターにインストールされている Excel がある場合はコンテンツを含む Excel ワークシートが開きます。

  6. 並列ウォッチ ウィンドウの右上隅に、ブール式を使用してコンテンツをフィルター処理に使用できるフィルター コントロールがあります。localA [localIdx [0]] > 20000 をフィルター コントロールのテキスト ボックスに入力し、Enter キーを選択します。

    ウィンドウでは、localA[localIdx[0]] の値が 20000 より大きいスレッドのみが含まれます。コンテンツは、以前に実行した並べ替えの動作である localA[localIdx[0]] の列に基づいて分類されます。

GPU のスレッドにフラグを設定できます。

[並列スタック] ウィンドウの GPU のスレッドの並列ウォッチ ウィンドウ、ウィンドウ、またはデータヒントにフラグを設定することによって、特定の GPU のスレッドをマークできます。GPU のスレッドのペインの行が複数のスレッドが含まれている場合、行は行に含まれるすべてのスレッドにフラグを設定するフラグを設定する。

GPU のスレッドにフラグを設定するには

  1. 並列ウォッチ 1] ウィンドウに [スレッド] の列ヘッダーを並べ替えにタイルのインデックスに基づいて選択し、インデックスに実行します。

  2. 次のバリアと 4 のスレッドを引き起こすメニュー バーで、[デバッグ]、**[続行]**をクリックし、(AMPMapReduce.cpp の行 32 に定義されます) 進行状況がアクティブになって。

  3. 現在アクティブな 4 種類のスレッドを含む行の左側のフラグのシンボルを選択します。

    次の図は、GPU のスレッドのウィンドウに 4 個のアクティブによってフラグが設定されたスレッドを示します。

    GPU のアクティブなスレッドはスレッド] ウィンドウ

    スレッドにフラグが設定されている [GPU スレッド] ウィンドウ

    [並列スタック] ウィンドウの並列ウォッチ ウィンドウとデータヒントは、フラグが設定されたスレッドを示します。

  4. フラグが設定された、選択示すように、4 種類のスレッドに焦点を同期する場合は、GPU で並列にウォッチ] ウィンドウと [並列スタック] ウィンドウ、フラグが設定されたスレッドのみ適用されます。

    次に、フラグが設定されたウィンドウのいずれかまたは [デバッグの場所] ツール バー ボタンだけを選択します。次の図は、によってフラグが設定されている [デバッグの場所] のツール バーのボタンだけが表示されます。

    フラグが設定されているボタンのみ表示]

    [フラグが設定されたもののみを表示] アイコンがある [デバッグの場所] ツール バー

    これで、GPU のスレッド、並列ウォッチ] ウィンドウと [並列スタック] ウィンドウでは、フラグが設定されたスレッドのみが表示されます。

GPU のスレッドを凍結と凍結解除

(中断する)、固定、再開 () GPU の並列スレッド] ウィンドウまたは [ウォッチ] ウィンドウから、GPU のスレッドを解放できます。同じ方法で CPU、スレッドを凍結解除できます; ついては、方法 : [スレッド] ウィンドウを使用するを参照してください。

GPU のスレッドを解放するには、固定

  1. すべてのスレッドを表示するに フラグが設定されたのみ表示] のボタンをクリックします。

  2. メニュー バーで、[デバッグ]、**[続行]**を選択します。

  3. アクティブな行のショートカット メニューを開き、**[凍結]**を選択します。

    GPU のスレッド] ウィンドウ内の次の図は、4 種類のすべてのスレッドが固定されることを示します。

    GPU の凍結されたスレッドは、スレッド] ウィンドウ

    凍結されたスレッドを示す [GPU スレッド] ウィンドウ

    同様に、並列ウォッチ ウィンドウでは 4 種類のすべてのスレッドが固定されることを示します。

  4. メニュー バーで、次の 4 つが、GPU のスレッドがバリアを含む行 22 プログレスし、30 行にブレークポイントに達する [デバッグ][続行] を選択します。GPU のスレッド] ウィンドウには、4 種類の前に凍結されたスレッドが凍結し、アクティブ状態であることを示します。

  5. メニュー バーで、[デバッグ]、**[続行]**を選択します。

  6. 並列 [ウォッチ] ウィンドウから、それぞれ GPU または複数のスレッドを解放できます。

GPU のスレッドをグループ化するには

  1. [GPU スレッド] のウィンドウのスレッドの 1 種類のショートカット メニューで、[グループ化]、**[アドレス]**を選択します。

    GPU のスレッド] ウィンドウのスレッドは、アドレスにグループ化されます。アドレスは、スレッドの各グループがある分解の命令に対応します。24 のスレッドは 22 行 tile_barrier::wait メソッド が実行された位置にあります。12 のスレッドは 32 行バリアの命令にあります。これらの 4 種類のスレッドにフラグが設定されます。8 種類のスレッドにブレークポイントは行 30 になります。これらのスレッドの 4 つは固定されます。次の図は、GPU のスレッド] ウィンドウでグループ化されたスレッドを示しています。

    GPU のグループ化されたスレッドは、スレッド] ウィンドウ

    スレッドがアドレスごとにグループ化されている [GPU スレッド] ウィンドウ

  2. また、並列ウォッチ ウィンドウのデータ グリッドのショートカット メニューの **[グループ化]**を開いて、次にスレッドをどのようにグループ化するか対応するメニュー項目を選択する [グループ化] 操作を実行し、選択できます。

コードの特定の場所にすべてのスレッドを実行できます。

**[現在の Tile をカーソル行の前まで実行]**を使用して、カーソルがある行に特定のタイルのすべてのスレッドを実行します。

すべてのスレッドがカーソルで示された位置に移動するには

  1. 凍結されたスレッドのショートカット メニューで、**[凍結解除]**を選択します。

  2. コード エディターで、30 行にカーソルを置きます。

  3. コード エディターのショートカット メニューで、**[現在の Tile をカーソル行の前まで実行]**を選択します。

    21 行のバリアにブロック 24 のスレッドは 32 Line3 進んでいます。これは [GPU スレッド] のペインに表示されます。

参照

処理手順

方法: GPU スレッド ウィンドウを使用する

方法: 並列ウォッチ ウィンドウを使用する

概念

C++ AMP の概要

その他の技術情報

GPU コードのデバッグ

同時実行ビジュアライザーで AMP C++ コードの分析