다음을 통해 공유


타일 사용

응용 프로그램의 가속을 최대화 하기 위해 타일링 사용할 수 있습니다.사각형 같은 하위 집합으로 구분 됩니다 스레드 바둑판식 배열 또는 타일.적절 한 타일 크기 및 바둑판식된 알고리즘을 사용 하는 경우 더욱 가속 AMP C++ 코드의 얻을 수 있습니다.바둑판식 배열의 기본 구성 요소는 다음과 같습니다.

  • tile_static변수입니다.타일링 가장 큰 장점은 성능 향상 된 tile_static 액세스 합니다.데이터에 액세스할 수 tile_static 메모리 액세스 전역 공간에서 데이터를 보다 훨씬 빠를 수 있습니다 (array 또는 array_view 개체).인스턴스는 tile_static 만들어진 각 타일에 대 한 변수 및 변수 타일의 모든 스레드가 액세스할 수 있습니다.데이터가 복사 되 일반적인 바둑판식된 알고리즘에서 tile_static 메모리 전역 메모리에서 한 번 하 고 다음 여러 번 액세스할는 tile_static 메모리.

  • tile_barrier::wait 메서드.호출을 tile_barrier::wait 의 모든 스레드가 동일한 타일에서 호출에 도달할 때까지 현재 스레드 실행 일시 tile_barrier::wait.스레드를 실행 하는, 해당 호출을 지나서 타일에서 스레드가 실행 됩니다 순서를 보장할 수 없습니다 tile_barrier::wait 의 모든 스레드에서 호출이 도달할 때까지.사용 하 여 즉는 tile_barrier::wait 메서드를 스레드-의해-스레드가 아닌 바둑판식 배열에서 타일 별로 작업을 수행할 수 있습니다.초기화 하는 코드는 일반적인 바둑판식 배열 알고리즘이는 tile_static 메모리 전체 타일을 뒤에 호출 하 여 tile_barrer::wait.뒤에 오는 코드 tile_barrier::wait 액세스 해야 하는 모든 계산에 포함의 tile_static 값입니다.

  • 로컬 및 전역 인덱싱입니다.액세스할 수 스레드의 전체를 기준으로 인덱스를 array_view 또는 array 개체 및 인덱스 타일을 기준으로 합니다.로컬 인덱스를 사용 하 여 코드를 읽고 디버깅 하기가 쉬워집니다.일반적으로 로컬 액세스를 인덱싱 사용 tile_static 변수 및 전역 액세스를 인덱싱 arrayarray_view 변수입니다.

  • tiled_extent 클래스tiled_index 클래스사용 하는 tiled_extent 대신 개체는 extent 개체에 parallel_for_each 호출.사용 하는 tiled_index 대신 개체는 index 개체에 parallel_for_each 호출.

타일링을 이용 하기 위해 알고리즘 해야 compute 도메인 타일로 분할 및 다음 바둑판식 배열 데이터 복사 tile_static 빠른 액세스를 위한 변수입니다.

예를 들어 전역, 바둑판 및 로컬 인덱스

다음 다이어그램은 8 x 9 매트릭스를 2 x 3 패에 정렬 된 데이터를 나타냅니다.

2x3 바둑판식 배열로 나뉜 8x9 매트릭스

글로벌, 타일, 다음 예제와 매트릭스가 로컬 인덱스를 바둑판식으로 배열 합니다.array_view 형식의 요소를 사용 하 여 개체가 만들어진 Description.Description 의 글로벌 보유 타일과 행렬에서 요소의 로컬 인덱스.코드를 호출 하 여 parallel_for_each 전역 변수의 값, 타일, 각 요소의 로컬 인덱스를 설정 합니다.출력 값의 표시를 Description 구조.

#include <iostream>
#include <iomanip>
#include <Windows.h>
#include <amp.h>
using namespace concurrency;

const int ROWS = 8;
cons tint COLS = 9;

// tileRow and tileColumn specify the tile that each thread is in.
// globalRow and globalColumn specify the location of the thread in the array_view.
// localRow and localColumn specify the location of the thread relative to the tile.
struct Description {
    int value;
    int tileRow;
    int tileColumn;
    int globalRow;
    int globalColumn;
    int localRow;
    int localColumn;
};

// A helper function for formatting the output.
void SetConsoleColor(int color) {
    int colorValue = (color == 0) ? 4 : 2;
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), colorValue);
}

// A helper function for formatting the output.
void SetConsoleSize(int height, int width) {
    COORD coord; coord.X = width; coord.Y = height;
    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coord);
    SMALL_RECT* rect = new SMALL_RECT();
    rect->Left = 0; rect->Top = 0; rect->Right = width; rect->Bottom = height;
    SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), true, rect);
}

// This method creates a 4x4 matrix of Description structures. In the call to parallel_for_each, the structure is updated 
// with tile, global, and local indices.
void TilingDescription() {
    // Create 16 (4x4) Description structures.
    std::vector<Description> descs;
    for (int i = 0; i < ROWS * COLS; i++) {
        Description d = {i, 0, 0, 0, 0, 0, 0};
        descs.push_back(d);
    }

    // Create an array_view from the Description structures.
    extent<2> matrix(ROWS, COLS);
    array_view<Description, 2> descriptions(matrix, descs);

    // Update each Description with the tile, global, and local indices.
    parallel_for_each(descriptions.extent.tile< 2, 3>(),
         [= ] (tiled_index< 2, 3> t_idx) restrict(amp) 
    {
        descriptions[t_idx].globalRow = t_idx.global[0];
        descriptions[t_idx].globalColumn = t_idx.global[1];
        descriptions[t_idx].tileRow = t_idx.tile[0];
        descriptions[t_idx].tileColumn = t_idx.tile[1];
        descriptions[t_idx].localRow = t_idx.local[0];
        descriptions[t_idx].localColumn= t_idx.local[1];
    });

    // Print out the Description structure for each element in the matrix.
    // Tiles are displayed in red and green to distinguish them from each other.
    SetConsoleSize(100, 150);
    for (int row = 0; row < ROWS; row++) {
        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Value: " << std::setw(2) << descriptions(row, column).value << "      ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Tile:   " << "(" << descriptions(row, column).tileRow << "," << descriptions(row, column).tileColumn << ")  ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Global: " << "(" << descriptions(row, column).globalRow << "," << descriptions(row, column).globalColumn << ")  ";
        }
        std::cout << "\n";

        for (int column = 0; column < COLS; column++) {
            SetConsoleColor((descriptions(row, column).tileRow + descriptions(row, column).tileColumn) % 2);
            std::cout << "Local:  " << "(" << descriptions(row, column).localRow << "," << descriptions(row, column).localColumn << ")  ";
        }
        std::cout << "\n";
        std::cout << "\n";
    }
}

void main() {
    TilingDescription();
    char wait;
    std::cin >> wait;
}

예제의 주 작업 정의에 해당 array_view 개체를 호출 하 고 parallel_for_each.

  1. 벡터의 Description 구조에는 8 x 9 복사 array_view 개체입니다.

  2. parallel_for_eachtiled_extent 개체로 계산 도메인.tiled_extent 를 호출 하 여 개체가 만들어진는 extent::tile() 메서드는 descriptions 변수.형식 매개 변수는 호출의 extent::tile(), <2,3>, 2 x 3 타일 만들어졌는지 지정 합니다.따라서, 8 x 9 매트릭스 12 타일, 네 개의 행과 세 개의 열으로 배열 됩니다.

  3. parallel_for_each 메서드를 사용 하 여 호출에 tiled_index<2,3> 개체 (t_idx)의 인덱스로.인덱스의 형식 매개 변수 (t_idx)는 compute 도메인의 형식 매개 변수와 일치 해야 (descriptions.extent.tile< 2, 3>()).

  4. 각 스레드를 실행 하면 인덱스 t_idx 는 스레드가 어떤 타일에 대 한 정보를 반환 (tiled_index::tile 속성) 및 스레드의 타일 내의 위치 (tiled_index::local 속성).

타일 동기화-tile_static 및 tile_barrier::wait

이전 예제에서는 바둑판식 배열 레이아웃 및 인덱스를 설명 되어 있지만 그 자체로는 그다지 유용 하지 않습니다.타일 알고리즘 및 악용에 필수적인 경우 바둑판식 배열 도움이 됩니다 tile_static 변수입니다.타일의 모든 스레드가 액세스할 수 있으므로 tile_static 변수 호출을 tile_barrier::wait 액세스를 동기화 하는 데 사용 되는 tile_static 변수입니다.모든 타일의 스레드가 액세스할 수 있지만 tile_static 변수 순서는 없습니다 보장 된 바둑판의 스레드를 실행 합니다.다음 예제에서는 사용 하는 방법을 보여 줍니다. tile_static 변수 및 tile_barrier::wait 각 타일의 평균 값을 계산 하는 방법.이 예제를 이해 하려면 키 다음과 같습니다.

  1. Rawdata에는 8 x 8 매트릭스에 저장 됩니다.

  2. 타일 크기는 2 x 2입니다.4 X 4 격자가 타일 만들고 사용 하 여 평균 4x4 행렬에 저장 될 수는 array 개체입니다.제한 된 개수의 형식 AMP 제한 함수 참조로 캡처할 수 있습니다.array 클래스를 그 중 하나입니다.

  3. 매트릭스 크기와 샘플 크기를 사용 하 여 정의 된 #define 문 때문에 형식 매개 변수를 array, array_view, extent, 및 tiled_index 상수 값 이어야 합니다.또한 사용할 수 있습니다 const int static 선언 합니다.뿐만 아니라, 4 x 4 개 타일의 평균을 계산 하는 샘플 크기를 변경 하는 것이 간단 합니다.

  4. A tile_static 각 바둑판 2x2 float 값의 배열을 선언 합니다.하나의 배열 선언의 모든 스레드에 대 한 코드 경로 이지만 행렬의 각 타일에 만들어집니다.

  5. 각 타일에 값을 복사 하는 코드 줄은 tile_static 배열입니다.값을 배열에 복사한 후 각 스레드에 대 한 실행 스레드에서 호출으로 인해 중지 tile_barrier::wait.

  6. 모든 스레드는 타일에 장애물에 도달 하면 평균을 계산할 수 있습니다.코드는 모든 스레드에 대해 실행 되므로 if 문만 한 스레드에서 평균을 계산 합니다.평균 평균 변수에 저장 됩니다.사용할 수 있는 것 처럼 장애물 타일의 계산을 제어 하는 구문이 본질적으로 되는 for 루프.

  7. 데이터는 averages 변수 이므로 array 개체, 호스트에 다시 복사 해야 합니다.벡터 변환 연산자를 추가 하는 예제입니다.

  8. 완전 한 예제 SAMPLESIZE를 4로 변경할 수 있습니다 및 기타 변경 하지 않고 코드를 제대로 실행 합니다.

#include <iostream>
#include <amp.h>
using namespace concurrency;

#define SAMPLESIZE 2
#define MATRIXSIZE 8
void SamplingExample() {

    // Create data and array_view for the matrix.
    std::vector<float> rawData;
    for (int i = 0; i < MATRIXSIZE * MATRIXSIZE; i++) {
        rawData.push_back((float)i);
    }
    extent<2> dataExtent(MATRIXSIZE, MATRIXSIZE);
    array_view<float, 2> matrix(dataExtent, rawData);

    // Create the array for the averages.
    // There is one element in the output for each tile in the data.
    std::vector<float> outputData;
    int outputSize = MATRIXSIZE / SAMPLESIZE;
    for (int j = 0; j < outputSize * outputSize; j++) {
        outputData.push_back((float)0);
    }
    extent<2> outputExtent(MATRIXSIZE / SAMPLESIZE, MATRIXSIZE / SAMPLESIZE);
    array<float, 2> averages(outputExtent, outputData.begin(), outputData.end());

    // Use tiles that are SAMPLESIZE x SAMPLESIZE.
    // Find the average of the values in each tile.
    // The only reference-type variable you can pass into the parallel_for_each call
    // is a concurrency::array.
    parallel_for_each(matrix.extent.tile<SAMPLESIZE, SAMPLESIZE>(),
         [=, &averages] (tiled_index<SAMPLESIZE, SAMPLESIZE> t_idx) restrict(amp) 
    {
        // Copy the values of the tile into a tile-sized array.
        tile_static float tileValues[SAMPLESIZE][SAMPLESIZE];
        tileValues[t_idx.local[0]][t_idx.local[1]] = matrix[t_idx];

        // Wait for the tile-sized array to load before you calculate the average.
        t_idx.barrier.wait();

        // If you remove the if statement, then the calculation executes for every
        // thread in the tile, and makes the same assignment to averages each time.
        if (t_idx.local[0] == 0 && t_idx.local[1] == 0) {
            for (int trow = 0; trow < SAMPLESIZE; trow++) {
                for (int tcol = 0; tcol < SAMPLESIZE; tcol++) {
                    averages(t_idx.tile[0],t_idx.tile[1]) += tileValues[trow][tcol];
                }
            }
            averages(t_idx.tile[0],t_idx.tile[1]) /= (float) (SAMPLESIZE * SAMPLESIZE);
        }
    });

    // Print out the results.
    // You cannot access the values in averages directly. You must copy them
    // back to a CPU variable.
    outputData = averages;
    for (int row = 0; row < outputSize; row++) {
        for (int col = 0; col < outputSize; col++) {
            std::cout << outputData[row*outputSize + col] << " ";
        }
        std::cout << "\n";
    }
    // Output for SAMPLESSIZE = 2 is:
    //  4.5  6.5  8.5 10.5
    // 20.5 22.5 24.5 26.5
    // 36.5 38.5 40.5 42.5
    // 52.5 54.5 56.5 58.5

    // Output for SAMPLESIZE = 4 is:
    // 13.5 17.5
    // 45.5 49.5
}

int main() {
    SamplingExample();
}

경쟁 상태

만들 수 있다고 생각할 수는 tile_static 라는 변수 total 및 해당 변수를 다음과 같이 각 스레드에 대 한 증분:

// Do not do this.
tile_static float total;
total += matrix[t_idx];
t_idx.barrier.wait();
averages(t_idx.tile[0],t_idx.tile[1]) /= (float) (SAMPLESIZE * SAMPLESIZE);

이 방법은 첫 번째 문제는입니다 tile_static 변수 이니셜라이저를 가질 수 없습니다.두 번째 문제는 경합 할당에 있습니다 total, 모든 스레드 타일에서 변수는 특정 한 순서 없이 액세스할 수 있기 때문에.만 다음과 같이 총 각 장애물에 액세스 하기 위해 하나씩 스레드를 허용 하는 알고리즘을 프로그래밍할 수 있습니다.그러나이 솔루션을 확장할 수 없습니다.

// Do not do this.
tile_static float total;
if (t_idx.local[0] == 0 && t_idx.local[1] == 0) {
    total = matrix[t_idx];
}
t_idx.barrier.wait();

if (t_idx.local[0] == 0 && t_idx.local[1] == 1) {
    total += matrix[t_idx];
}
t_idx.barrier.wait();

// etc.

메모리 울타리

두 가지 종류의 동기화 해야 하는 메모리 액세스-전역 메모리 액세스 및 tile_static 메모리 액세스 합니다.A concurrency::array 만 전역 메모리 개체를 할당 합니다.A concurrency::array_view 전역 메모리를 참조할 수 있습니다 tile_static 메모리 또는 둘 다 생성 된 방법에 따라 합니다.동기화 해야 하는 메모리의 두 종류가 있습니다.

  • 전역 메모리

  • tile_static

A 메모리 울타리 액세스를 다른 스레드에 스레드 타일에서 사용할 수 있는 메모리와 메모리 액세스 프로그램 순서에 따라 실행 됩니다.이 위해 컴파일러와 프로세서가 읽기 및 쓰기 울타리에서 재정렬 하지 않습니다.C + + AMP에서 메모리 담 이러한 방법 중 하나를 호출 하 여 만들어집니다.

호출 하는 응용 프로그램의 성능을 향상 시킬 수 있습니다 필요한 특정 담 장입니다.장애물 형식 문을 컴파일러와 하드웨어 다시 정렬 하는 방법을 영향을 줍니다.예를 들어, 전역 메모리 울타리를 사용 하 고만 전역 메모리 액세스 적용 및 컴파일러와 하드웨어를 다시 정렬할 수 있습니다 경우 읽기 및 쓰기를 tile_static 변수는 울타리의 양쪽.

다음 예제에서는 쓰기에 장애물을 동기화 tileValuesa tile_static 변수입니다.이 예제에서 tile_barrier::wait_with_tile_static_memory_fence 대신 호출 tile_barrier::wait.

// Using a tile_static memory fence.
parallel_for_each(matrix.extent.tile<SAMPLESIZE, SAMPLESIZE>(),
     [=, &averages] (tiled_index<SAMPLESIZE, SAMPLESIZE> t_idx) restrict(amp) 
{
    // Copy the values of the tile into a tile-sized array.
    tile_static float tileValues[SAMPLESIZE][SAMPLESIZE];
    tileValues[t_idx.local[0]][t_idx.local[1]] = matrix[t_idx];

    // Wait for the tile-sized array to load before calculating the average.
    t_idx.barrier.wait_with_tile_static_memory_fence();

    // If you remove the if statement, then the calculation executes for every
    // thread in the tile, and makes the same assignment to averages each time.
    if (t_idx.local[0] == 0 && t_idx.local[1] == 0) {
        for (int trow = 0; trow < SAMPLESIZE; trow++) {
            for (int tcol = 0; tcol < SAMPLESIZE; tcol++) {
                averages(t_idx.tile[0],t_idx.tile[1]) += tileValues[trow][tcol];
            }
        }
        averages(t_idx.tile[0],t_idx.tile[1]) /= (float) (SAMPLESIZE * SAMPLESIZE);
    }
});

참고 항목

참조

tile_static 키워드

기타 리소스

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