타일 사용
응용 프로그램의 가속을 최대화 하기 위해 타일링 사용할 수 있습니다.사각형 같은 하위 집합으로 구분 됩니다 스레드 바둑판식 배열 또는 타일.적절 한 타일 크기 및 바둑판식된 알고리즘을 사용 하는 경우 더욱 가속 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 변수 및 전역 액세스를 인덱싱 array 및 array_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 패에 정렬 된 데이터를 나타냅니다.
글로벌, 타일, 다음 예제와 매트릭스가 로컬 인덱스를 바둑판식으로 배열 합니다.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.
벡터의 Description 구조에는 8 x 9 복사 array_view 개체입니다.
parallel_for_each 가 tiled_extent 개체로 계산 도메인.tiled_extent 를 호출 하 여 개체가 만들어진는 extent::tile() 메서드는 descriptions 변수.형식 매개 변수는 호출의 extent::tile(), <2,3>, 2 x 3 타일 만들어졌는지 지정 합니다.따라서, 8 x 9 매트릭스 12 타일, 네 개의 행과 세 개의 열으로 배열 됩니다.
parallel_for_each 메서드를 사용 하 여 호출에 tiled_index<2,3> 개체 (t_idx)의 인덱스로.인덱스의 형식 매개 변수 (t_idx)는 compute 도메인의 형식 매개 변수와 일치 해야 (descriptions.extent.tile< 2, 3>()).
각 스레드를 실행 하면 인덱스 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 각 타일의 평균 값을 계산 하는 방법.이 예제를 이해 하려면 키 다음과 같습니다.
Rawdata에는 8 x 8 매트릭스에 저장 됩니다.
타일 크기는 2 x 2입니다.4 X 4 격자가 타일 만들고 사용 하 여 평균 4x4 행렬에 저장 될 수는 array 개체입니다.제한 된 개수의 형식 AMP 제한 함수 참조로 캡처할 수 있습니다.array 클래스를 그 중 하나입니다.
매트릭스 크기와 샘플 크기를 사용 하 여 정의 된 #define 문 때문에 형식 매개 변수를 array, array_view, extent, 및 tiled_index 상수 값 이어야 합니다.또한 사용할 수 있습니다 const int static 선언 합니다.뿐만 아니라, 4 x 4 개 타일의 평균을 계산 하는 샘플 크기를 변경 하는 것이 간단 합니다.
A tile_static 각 바둑판 2x2 float 값의 배열을 선언 합니다.하나의 배열 선언의 모든 스레드에 대 한 코드 경로 이지만 행렬의 각 타일에 만들어집니다.
각 타일에 값을 복사 하는 코드 줄은 tile_static 배열입니다.값을 배열에 복사한 후 각 스레드에 대 한 실행 스레드에서 호출으로 인해 중지 tile_barrier::wait.
모든 스레드는 타일에 장애물에 도달 하면 평균을 계산할 수 있습니다.코드는 모든 스레드에 대해 실행 되므로 if 문만 한 스레드에서 평균을 계산 합니다.평균 평균 변수에 저장 됩니다.사용할 수 있는 것 처럼 장애물 타일의 계산을 제어 하는 구문이 본질적으로 되는 for 루프.
데이터는 averages 변수 이므로 array 개체, 호스트에 다시 복사 해야 합니다.벡터 변환 연산자를 추가 하는 예제입니다.
완전 한 예제 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_barrier::wait 메서드: 만듭니다 담 주위 모두 글로벌 및 tile_static 메모리입니다.
tile_barrier::wait_with_all_memory_fence 메서드: 만듭니다 담 주위 모두 글로벌 및 tile_static 메모리입니다.
tile_barrier::wait_with_global_memory_fence 메서드: 담만 전역 메모리 주위를 만듭니다.
tile_barrier::wait_with_tile_static_memory_fence 메서드:만 주위에 울타리를 만듭니다 tile_static 메모리입니다.
호출 하는 응용 프로그램의 성능을 향상 시킬 수 있습니다 필요한 특정 담 장입니다.장애물 형식 문을 컴파일러와 하드웨어 다시 정렬 하는 방법을 영향을 줍니다.예를 들어, 전역 메모리 울타리를 사용 하 고만 전역 메모리 액세스 적용 및 컴파일러와 하드웨어를 다시 정렬할 수 있습니다 경우 읽기 및 쓰기를 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);
}
});