次の方法で共有


チュートリアル : 行列乗算

このステップ バイ ステップ チュートリアルでは、C++ のストレージ ・ システムを使用して、行列乗算の実行を高速化する方法を示します。2 つのアルゴリズムが表示される、タイルとタイルのないです。

必須コンポーネント

始める前に。

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

  • タイルの使用 を読み取りました。

  • 確認してくださいWindows 7、 Windows 8、 Windows Server 2008 R2、またはWindows Server 2012がコンピューターにインストールされています。

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

  1. Visual Studio で、メニュー バーの選択ファイルNewプロジェクト

  2. インストール [テンプレート] ペインで、選択 Visual C

  3. 選択空プロジェクト、入力 MatrixMultiply で、 ボックス、し、選択、 OK ボタン。

  4. 選択、 ボタン。

  5. ソリューション エクスプ ローラー、ショートカット メニューを開く ソース ファイル、し [ 追加新しい項目

  6. [新しい項目の追加 ダイアログ ボックスで、 C++ ファイル (.cpp)、入力 MatrixMultiply.cpp で、 ボックス、し、選択、 追加ボタン。

タイリングを乗算

このセクションでは、次のように定義されている A と B の 2 つの行列の乗算を検討してください。

3 x 2 行列2 x 3 行列

3-2 行列ですし、B 2 × 3 の行列になります。掛けて算出の A を B の積は、次の 3 × 3 の行列です。製品 A の行は、列要素ごとの B の乗算することによって計算されます。

3 x 3 行列

C++ のストレージ ・ システムを使用せずに

  1. MatrixMultiply.cpp を開き、既存のコードを置き換えるには、次のコードを使用します。

    #include <iostream>
    
    void MultiplyWithOutAMP() {
    
        int aMatrix[3][2] = {{1, 4}, {2, 5}, {3, 6}};
        int bMatrix[2][3] = {{7, 8, 9}, {10, 11, 12}};
        int product[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
    
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {
                // Multiply the row of A by the column of B to get the row, column of product.
                for (int inner = 0; inner < 2; inner++) {
                    product[row][col] += aMatrix[row][inner] * bMatrix[inner][col];
                }
                std::cout << product[row][col] << "  ";
            }
            std::cout << "\n";
        }
    }
    
    void main() {
        MultiplyWithOutAMP();
        getchar();
    }
    

    アルゴリズムは、行列乗算の定義の簡単な実装です。パラレルまたは連結アルゴリズムの計算時間を短縮するのには使用しません。

  2. 選択のメニュー バーには、 ファイルを保存すべて

  3. デバッグを開始して、出力が正しいことを確認するには、F5 ショートカット キーを選択します。

  4. アプリケーションを終了するのには Enter をクリックします。

C++ のストレージ ・ システムを使用してを乗算するには

  1. MatrixMultiply.cpp では、前に、次のコードを追加する、 mainメソッド。

    void MultiplyWithAMP() {
        int aMatrix[] = { 1, 4, 2, 5, 3, 6 };
        int bMatrix[] = { 7, 8, 9, 10, 11, 12 };
        int productMatrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
        array_view<int, 2> a(3, 2, aMatrix);
        array_view<int, 2> b(2, 3, bMatrix);
        array_view<int, 2> product(3, 3, productMatrix);
    
        parallel_for_each(
            product.extent, 
             [=](index<2> idx) restrict(amp) {
                int row = idx[0];
                int col = idx[1];
                for (int inner = 0; inner < 2; inner++) {
                    product[idx] += a(row, inner) * b(inner, col);
                }
            }
        );
    
        product.synchronize();
    
        for (int row = 0; row < 3; row++) {
            for (int col = 0; col < 3; col++) {
                //std::cout << productMatrix[row*3 + col] << "  ";
                std::cout << product(row, col) << "  ";
            }
            std::cout << "\n";
        }
    }
    

    ストレージ ・ システムのコードは、A 以外のコードに似ています。呼び出しをparallel_for_eachの各要素に対して 1 つのスレッドを開始product.extentと置き換えます、 forの行と列のループ。行と列にあるセルの値にはidx。要素にアクセスできます、 array_viewいずれかを使用して、オブジェクト、 []演算子とインデックス変数、または、 ()演算子および行と列の変数。2 つの方法の例を示します。array_view::synchronizeメソッドは、値のコピーがproduct変数に戻る、 productMatrix変数。

  2. 次の追加includeとusing MatrixMultiply.cpp の先頭のステートメント。

    #include <amp.h>
    using namespace concurrency;
    
  3. 変更、 mainメソッド、 MultiplyWithAMPメソッド。

    void main() {
        MultiplyWithOutAMP();
        MultiplyWithAMP();
        getchar();
    }
    
  4. デバッグを開始するのには、ctrl キーを押しながら F5 キーボード ショートカットを選択し、出力が正しいことを確認します。

  5. アプリケーションを終了するのには、space キーを選択します。

タイリングを乗算

並べて表示される手法で、パーティション データとして知られている同じサイズのサブセットにtiles。タイリングを使用する場合は、次の 3 つを変更します。

  • 作成することができますtile_static変数。データにアクセスするtile_static領域が何回もグローバル領域内のデータへのアクセスよりも高速。インスタンスは、 tile_static変数が各タイルを作成し、タイルのすべてのスレッドは、変数へのアクセスがあります。タイルの主な利点は、パフォーマンス向上のためにはtile_staticへのアクセス。

  • 呼び出すことができます、 tile_barrier::wait すべてのスレッドで、指定した行のコードで 1 つのタイルを停止する方法。スレッドでのみ実行される順序を保証することはできませんすべてのタイルを 1 つのスレッドを停止するようにtile_barrier::waitの実行を再開する前にします。

  • スレッド全体を基準とするインデックスへのアクセスがあるarray_viewオブジェクトやタイルを基準とするインデックス。ローカル インデックスを使用して、コードの読み取り、およびデバッグするには、わかりやすくなります。

行列乗算の分割を利用するには、アルゴリズムする必要があります、行列をタイルに分割し、タイルにデータをコピー tile_staticの高速アクセス用の変数。この例では、行列が等しいサイズの submatrices に分割されます。Submatrices を乗算することによって、製品があります。2 つの行列とその製品の次の使用例を示します。

4 x 4 行列4 x 4 行列4 x 4 行列

行列は次のように定義されている 4 つの 2 x 2 マトリックスにパーティション化されます。

2 x 2 サブ行列に分割された 4 x 4 行列2 x 2 サブ行列に分割された 4 x 4 行列

製品 A のと B できます今すぐ記述し、次のように計算します。

2 x 2 サブ行列に分割された 4 x 4 行列

の行列a ~ hの製品はすべて、2 x 2 マトリックスをされ、それらの合計が 2 x 2 の行列でも。それも A * B、4 × 4 の行列が期待どおりになります。アルゴリズムをすばやく確認するには、要素の最初の行に、製品の最初の列の値を計算します。次の例では、その方が、要素の値を最初の行の最初の列でae + bg。最初の行、最初の列を計算する必要がだけaeとbgの各用語。That value for ae is 1*1 + 2*5 = 11.bg の値が 3*1 + 4*5 = 23 です。最終的な値は11 + 23 = 34が正しくありません。

このアルゴリズムでは、コードを実装するには。

  • 使用して、 tiled_extentのではなくオブジェクトをextentオブジェクト、 parallel_for_eachを呼び出します。

  • 使用して、 tiled_indexのではなくオブジェクトをindexオブジェクト、 parallel_for_eachを呼び出します。

  • 作成tile_static 、submatrices を保持する変数。

  • 使用して、 tile_barrier::wait 、submatrices の製品の計算のすべてのスレッドを停止する方法。

ストレージ ・ システムを使用してタイルを乗算するには

  1. MatrixMultiply.cpp では、前に、次のコードを追加する、 mainメソッド。

    void MultiplyWithTiling()
    {
        // The tile size is 2.
        static const int TS = 2;
    
        // The raw data.
        int aMatrix[] =       { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
        int bMatrix[] =       { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
        int productMatrix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
        // Create the array_view objects.
        array_view<int, 2> a(4, 4, aMatrix);
        array_view<int, 2> b(4, 4, bMatrix);
        array_view<int, 2> product(4, 4, productMatrix);
    
        // Call parallel_for_each by using  2x2 tiles.
        parallel_for_each(product.extent.tile< TS, TS >(),
            [=] (tiled_index< TS, TS> t_idx) restrict(amp) 
            {
                // Get the location of the thread relative to the tile (row, col) and the entire array_view (rowGlobal, colGlobal).
                int row = t_idx.local[0]; 
                int col = t_idx.local[1];
                int rowGlobal = t_idx.global[0];
                int colGlobal = t_idx.global[1];
                int sum = 0;
    
                // Given a 4x4 matrix and a 2x2 tile size, this loop executes twice for each thread.
                // For the first tile and the first loop, it copies a into locA and e into locB.
                // For the first tile and the second loop, it copies b into locA and g into locB.
                for (int i = 0; i < 4; i += TS) {
                    tile_static int locA[TS][TS];
                    tile_static int locB[TS][TS];
                    locA[row][col] = a(rowGlobal, col + i);
                    locB[row][col] = b(row + i, colGlobal);
                    // The threads in the tile all wait here until locA and locB are filled.
                    t_idx.barrier.wait();
    
    
                    // Return the product for the thread. The sum is retained across
                    // both iterations of the loop, in effect adding the two products
                    // together, for example, a*e.
                    for (int k = 0; k < TS; k++) {
                        sum += locA[row][k] * locB[k][col];
                    }
    
                    // All threads must wait until the sums are calculated. If any threads
                    // moved ahead, the values in locA and locB would change.      
                    t_idx.barrier.wait();
                    // Now go on to the next iteration of the loop.          
                }
    
                // After both iterations of the loop, copy the sum to the product variable by using the global location.
                product[t_idx.global] = sum;
        });
    
            // Copy the contents of product back to the productMatrix variable.
            product.synchronize();
    
            for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 4; col++) {
                // The results are available from both the product and productMatrix variables.
                //std::cout << productMatrix[row*3 + col] << "  ";
                std::cout << product(row, col) << "  ";
            }
            std::cout << "\n";
        }
    
    }
    

    この例は、タイリングを例と大きく異なっています。コードは、概念の手順を使用します。

    1. [0, 0] のタイルの要素をコピー aにlocA。[0, 0] のタイルの要素をコピー bにlocB。注意してくださいproduct並べて表示しません、 aとb。グローバル インデックスを使用してアクセスするために、 a, b、およびproduct。呼び出しをtile_barrier::waitが不可欠です。すべてのスレッドで、タイルの両方まで停止locAとlocBが入ります。

    2. 多重locAとlocBし、結果を置くproduct。

    3. [0, 1] のタイルの要素をコピー aにlocA。[1,0] のタイルの要素をコピー bにlocB。

    4. 多重locAとlocBとされての結果を追加product。

    5. [0, 0] のタイルの乗算は完了です。

    6. 他の 4 つのタイルを繰り返します。具体的には、タイルのインデックスがないと、スレッドが任意の順序で実行できます。各スレッドを実行すると、 tile_static変数が適切に、タイルを作成ですと呼び出しをtile_barrier::wait 、プログラム フローを制御します。

    7. アルゴリズムを調べると、各 submatrix が読み込まれることに注意してください、 tile_staticメモリを 2 回します。そのデータの転送に時間がかかります。しかしであるとtile_staticメモリ、データへのアクセスがはるかに高速。繰り返しアクセスするには、submatrices の値を製品を計算するため、全体的なパフォーマンスが向上されます。各アルゴリズムには、最適なアルゴリズムを見つけ、タイルのサイズに実験が必要です。

      A ないと非タイル例での A の各要素と B は、製品を計算するには、グローバル ・ メモリから 4 回アクセスします。タイルの例では、各要素は、グローバル ・ メモリの 2 倍、4 倍からにアクセスは、 tile_staticメモリ。大幅なパフォーマンスの向上です。ただし、1024 × 1024、A と B をされた場合は、行列と、タイルのサイズ 16、大幅なパフォーマンス向上がします。各要素にコピーされる場合は、 tile_staticメモリのみ 16 時間し、アクセスtile_staticメモリ 1024年回。

  2. 呼び出すため、main メソッドを変更する、 MultiplyWithTiling メソッドのように。

    void main() {
        MultiplyWithOutAMP();
        MultiplyWithAMP();
        MultiplyWithTiling();
        getchar();
    }
    
  3. デバッグを開始するのには、ctrl キーを押しながら F5 キーボード ショートカットを選択し、出力が正しいことを確認します。

  4. アプリケーションを終了するのには、スペース バーを選択します。

参照

処理手順

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

その他の技術情報

C++ AMP (C++ Accelerated Massive Parallelism)