연습: 매트릭스 곱

이 단계별 연습에서는 행렬 곱의 실행 속도 높이려면 AMP C++를 사용 하는 방법을 보여 줍니다.두 알고리즘 표시를 하나 없이 바둑판식 및 바둑판식으로.

사전 요구 사항

시작 하기 전에:

  • C++ AMP 개요를 읽는 경우

  • 타일 사용를 읽는 경우

  • 있는지 확인 Windows 7, Windows 8, Windows Server 2008 R2, 또는 Windows Server 2012 컴퓨터에 설치 됩니다.

프로젝트를 만들려면

  1. Visual Studio 메뉴 모음에서 선택 파일, New, 프로젝트.

  2. 아래에서 설치 된 템플릿 창에서 선택 Visual C++.

  3. 선택 빈 프로젝트, 입력 MatrixMultiply 에 이름 을 선택한 다음 선택은 확인 단추.

  4. 선택은 다음 단추.

  5. 솔루션 탐색기, 바로 가기 메뉴를 열고 소스 파일, 다음 선택 추가, 새 항목.

  6. 새 항목 추가 선택 대화 상자 C++ 파일 (.cpp), 입력 MatrixMultiply.cpp 에 이름 을 선택한 다음 선택은 추가 단추.

바둑판식 배열 없이 곱하기

이 섹션에서는 A와 B는 다음과 같이 정의 된 두 매트릭스의 곱셈을 고려 하십시오.

3x2 매트릭스2x3 매트릭스

A 3-2 행렬 이며 B 2-3 매트릭스입니다.곱하여 A가 B의 다음 3 x 3 매트릭스입니다.제품의 요소 별로 B 열에서 A의 행을 곱하여 계산 됩니다.

3x3 매트릭스

C + + AMP를 사용 하지 않고 곱할 합니다.

  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 + + AMP를 사용 하 여 곱셈

  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";
        }
    }
    

    AMP 코드 AMP 비 코드와 유사 합니다.호출을 parallel_for_each 각 요소에 대 한 스레드를 시작 product.extent, 및 대체는 for for 루프를 행과 열입니다.행과 열에 있는 셀의 값을 사용할 수 있습니다 idx.요소에 액세스할 수 있습니다는 array_view 중 하나를 사용 하 여 개체의 [] 연산자와 인덱스 변수를 또는 () 연산자 및 행과 열의 변수.이 예제에서는 두 메서드를 보여 줍니다.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. 응용 프로그램을 종료 하려면 스페이스바를 선택 합니다.

곱셈 바둑판식 배열

타일링 됩니다 기법을 하면 파티션을 데이터 크기가 같은 하위 집합으로 알려진 tiles.바둑판식 배열을 사용 하면 세 가지를 변경 합니다.

  • 만들 수 있습니다 tile_static 변수입니다.데이터에 액세스할 수 tile_static 공간 몇 배나 빠르다는 것 보다 액세스 전역 공간에서 데이터를 사용할 수 있습니다.인스턴스는 tile_static 만들어진 각 타일에 대 한 변수 및 변수 타일의 모든 스레드가 액세스할 수 있습니다.바둑판식 배열에 대 한 가장 큰 이점은 성능 향상으로 인해입니다 tile_static 액세스 합니다.

  • 호출할 수는 tile_barrier::wait 메서드를 모든 지정 된 코드 줄에 하나의 타일에서 스레드를 중지 합니다.스레드는 실행 되는 순서를 보증 하지 않습니다 모든 하나의 타일에서 스레드 호출 중지 됩니다 tile_barrier::wait 는 실행을 계속 하기 전에.

  • 액세스할 수 스레드의 전체를 기준으로 인덱스를 array_view 개체 및 인덱스 타일을 기준으로 합니다.로컬 인덱스를 사용 하 여 사용자 코드를 읽고 디버깅 하기가 쉬워집니다.

행렬 곱셈에서 바둑판식 배열을 이용 하기 위해 알고리즘 해야 행렬 타일로 분할 및 다음 바둑판식 배열 데이터를 복사 tile_static 더 빠른 액세스를 위한 변수입니다.이 예제에서는 행렬에 submatrices 같은 크기의 분할 됩니다.제품의 submatrices를 곱하여 없습니다.두 매트릭스 및 자사 제품의이 예제 다음과 같습니다.

4x4 매트릭스4x4 매트릭스4x4 매트릭스

행렬에는 다음과 같이 정의 된 4 2x2 매트릭스에 분할 됩니다.

2x2 하위 매트릭스로 분할된 4x4 매트릭스2x2 하위 매트릭스로 분할된 4x4 매트릭스

제품의 A 및 B 이제 쓸 고 수는 다음과 같이 계산 합니다.

2x2 하위 매트릭스로 분할된 4x4 매트릭스

때문에 매트릭스 a 통해 h 2x2 매트릭스, 모든 제품 이며 그 중 합도 2x2 매트릭스입니다.또한 A * B 4 x 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 곱의 계산에 대 한 스레드를 중지 합니다.

AMP를 사용한 및 바둑판식 배열 곱할 합니다.

  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 메모리를 두 번.해당 데이터 전송 시간이 걸리지 않습니다.그러나 데이터에 되 면 tile_static 메모리의 데이터 액세스가 훨씬 빠릅니다.제품 계산 같은 반복적인된 액세스는 submatrices의 값을 요구 하기 때문에 전체적인 성능 향상이입니다.각 알고리즘에 대 한 실험은 최적 알고리즘을 찾아서 타일 크기 필요 합니다.

      AMP 비와 비 타일 예제에서 각 요소는 A와 B의 곱을 계산 하는 전역 메모리에서 네 번 액세스 됩니다.타일 예 전역 메모리를 두 번 및 4 번에서 각 요소에 액세스할 수 있는 tile_static 메모리입니다.상당한 성능 향상 아닙니다.그러나 1024 x 1024 A와 B의 경우 행렬 및 타일 크기 16에서 성능이 크게 향상 됩니다.즉, 각 요소에 복사 되는 tile_static 메모리만 16 배 하 고 액세스할 tile_static 메모리 1024 번.

  2. 수정할 기본 메서드를 호출 하는 MultiplyWithTiling 메소드를 사용.

    void main() {
        MultiplyWithOutAMP();
        MultiplyWithAMP();
        MultiplyWithTiling();
        getchar();
    }
    
  3. 디버깅을 시작 하려면 Ctrl + F5 바로 가기 키를 선택 하 고 출력 정확한 지 확인 합니다.

  4. 응용 프로그램을 종료 하려면 스페이스바를 선택 합니다.

참고 항목

작업

연습: C++ AMP 응용 프로그램 디버깅

기타 리소스

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