Upload
a15464321646213
View
106
Download
3
Embed Size (px)
DESCRIPTION
Слайды лекции курса "Архитектура и программирование потоковых многоядерных процессоров для научных рассчетов".
Citation preview
Архитектура и программирование Архитектура и программирование потоковых многоядерных процессоров
для научных расчётовдля научных расчётов
Лекция 5. Пример вычислительного ядра – задача умножения матриц Текстуры – задача умножения матриц. Текстуры. Атомарные функции. Библиотека CUTIL
Процедура разработки программыПроцедура разработки программы
Общий подход к програмированию Разбить задачу на элементарные блоки данных, над которыми выполняется стандартный алгоритм обработки (единый для
б )всех блоков)
Разбить за-/вы- гружаемые данные на элементарные непересекающиеся блоки которые каждый из процессов непересекающиеся блоки, которые каждый из процессов прочтёт/запишет
Определить конфигурацию грида/блока позволяющее Определить конфигурацию грида/блока, позволяющее Оптимальное размещение промежуточных данных в регистрах и общей памятиОптимальную вычислительную загрузку потоковых процессоров
Определить график когерентного обращения к памяти процессами при загрузке данных
Вычислительная конфигурация Вычислительная конфигурация GPU
Процессы объединяются в блоки (blocks), внутри которых они имеют общую
Host Device
Grid 1р щу
память (shared memory) и синхронное исполнение
Kernel 1
Block(0, 0)
Block(1, 0)
Block(2, 0)
Block(0 1)
Block(1 1)
Block(2 1)
Блоки объединяются в сетки (grids)
Нет возможности предсказать очерёдность запуска блоков в Kernel
(0, 1) (1, 1) (2, 1)
Grid 2
очерёдность запуска блоков в сеткиМежду блоками нет и не может быть (см. выше) общей памяти
2
Block (1, 1)
Thread(0, 1)
Thread(1, 1)
Thread(2, 1)
Thread(3, 1)
Thread(4, 1)
Thread(0, 0)
Thread(1, 0)
Thread(2, 0)
Thread(3, 0)
Thread(4, 0)
( ) ( ) ( ) ( ) ( )
Thread(0, 2)
Thread(1, 2)
Thread(2, 2)
Thread(3, 2)
Thread(4, 2)
Пример программы для GPUПример программы для GPUумножение матриц -1
bx
tx
0 1 2
Разбить задачу на элементарные блоки данных, над которыми выполняется стандартный алгоритм обработки
N
tx01 bsize-12
WID
TH
стандартный алгоритм обработки (единый для всех блоков)Каждый блок вычисляет некоторую небольшую область результата
BL
OC
K_W
WID
TH H
b
у р уКаждый тред в блоке вычисляет один элемент этой области
M P
BL
OC
K_W
Psub210
0
SIZE
BLOCK WIDTHBLOCK WIDTHBLOCK WIDTH
byty
2
bsize-1
1
BL
OC
K_S Ha
BLOCK_WIDTH
WbWa
BLOCK_WIDTHBLOCK_WIDTH
2
Пример программы для GPUПример программы для GPUумножение матриц -2
bx
tx
0 1 2
Разбить данные на элементарные блоки (непересекающиеся), которые каждый из процессов прочтёт/запишет
N
tx01 bsize-12
WID
TH
процессов прочтёт/запишетНеобходимые области исходных матриц разбиваются на квадратные областиКаждый из тредов читает один элемент из этих
BL
OC
K_W
WID
TH H
b
Каждый из тредов читает один элемент из этих областей в разделяемую память
M P
BL
OC
K_W
Psub210
0
SIZE
BLOCK WIDTHBLOCK WIDTHBLOCK WIDTH
byty
2
bsize-1
1
BL
OC
K_S Ha
BLOCK_WIDTH
WbWa
BLOCK_WIDTHBLOCK_WIDTH
2
Пример программы для GPUПример программы для GPUумножение матриц -3
bx
tx
0 1 2
Определить конфигурацию грида/блока, позволяющее
Оптимальное размещение промежуточных N
tx01 bsize-12
WID
TH
Оптимальное размещение промежуточных данных в регистрах и общей памятиОптимальную вычислительную загрузкупотоковых процессоров
BL
OC
K_W
WID
TH H
b
Блок = 16x16= 256 тредов больше чем 192, меньше чем 512 M P
BL
OC
K_W
Целое количество варпов в блоке
Psub210
0
SIZE
Размеры грида определяются размерами исходных матриц
BLOCK WIDTHBLOCK WIDTHBLOCK WIDTH
byty
2
bsize-1
1
BL
OC
K_S Ha
BLOCK_WIDTH
WbWa
BLOCK_WIDTHBLOCK_WIDTH
2
Пример программы для GPUПример программы для GPUумножение матриц -4
bx
tx
0 1 2
Размер разделяемой памяти = 16KB на мультипроцессор (максимальное
б )N
tx01 bsize-12
WID
TH
количество памяти для блока)В разделяемую память мультипроцессора поместятся данные от 8 блоков
BL
OC
K_W
WID
TH H
b
M P
BL
OC
K_W
Регистровая память расходуется
Psub210
0
SIZE
расходуется незначительно
Для определения
BLOCK WIDTHBLOCK WIDTHBLOCK WIDTH
byty
2
bsize-1
1
BL
OC
K_S Haколичества регистров,
приходящееся на один тред нужно смотреть PTX код
BLOCK_WIDTH
WbWa
BLOCK_WIDTHBLOCK_WIDTH
2
Хост-функция Хост функция умножения матриц
#define BLOCK SIZE 16 #define BLOCK_SIZE 16 __global__ void Muld(float*, float*, int, int, float*); void Mul(const float* A, const float* B, int hA, int wA, int wB, float* C) { int size; // L d A d B t th d i// Load A and B to the devicefloat* Ad; size = hA * wA * sizeof(float); cudaMalloc((void**)&Ad, size); cudaMemcpy(Ad, A, size, cudaMemcpyHostToDevice); float* Bd; size = wA * wB * sizeof(float); cudaMalloc((void**)&Bd, size); cudaMemcpy(Bd, B, size, cudaMemcpyHostToDevice); // Allocate C on the device float* Cd; size = hA * wB * sizeof(float); cudaMalloc((void**)&Cd, size); // Compute the execution configuration assuming the matrix dimensions are multiples of BLOCK_SIZE dim3 dimBlock(BLOCK_SIZE, BLOCK_SIZE); dim3 dimGrid(wB / dimBlock.x, hA / dimBlock.y); // Launch the device computation Muld<<<dimGrid, dimBlock>>>(Ad, Bd, wA, wB, Cd); // Read C from the device cudaMemcpy(C, Cd, size, cudaMemcpyDeviceToHost); // Free device memory cudaFree(Ad); cudaFree(Bd); cudaFree(Cd); }
GPU ядро умножения матрицGPU ядро умножения матриц
global void Muld(float* A float* B int wA int wB float* C) {__global__ void Muld(float* A, float* B, int wA, int wB, float* C) {
int bx = blockIdx.x; // Block indexint by = blockIdx.y; i t t th dId // Th d i d int tx = threadIdx.x; // Thread index int ty = threadIdx.y; int aBegin = wA * BLOCK_SIZE * by; // Index of the first sub-matrix of A processed by the blockint aEnd = aBegin + wA - 1; // Index of the last sub-matrix of A processed by the blockint aStep = BLOCK_SIZE; // Step size used to iterate through the sub-matrices of A int bBegin = BLOCK_SIZE * bx; // Index of the first sub-matrix of B processed by the blockint bStep = BLOCK_SIZE * wB; // Step size used to iterate through the sub-matrices of Bfloat Csub = 0; // The element of the block sub-matrix that is computed by the thread
for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep) { // Shared memory for the sub-matrix of A__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];
// Shared memory for the sub-matrix of B__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];
GPU ядро умножения матрицGPU ядро умножения матриц(продолжение)
As[ty][tx] = A[a + wA * ty + tx]; // Load the matrices from global memory to shared memory;Bs[ty][tx] = B[b + wB * ty + tx]; // each thread loads one element of each matrix __syncthreads(); // Synchronize to make sure the matrices are loadedy () y
// Multiply the two matrices together; // each thread computes one element // of the block sub-matrix//for (int k = 0; k < BLOCK_SIZE; ++k)
Csub += As[ty][k] * Bs[k][tx];
// Synchronize to make sure that the preceding // Synchronize to make sure that the preceding // computation is done before loading two new // sub-matrices of A and B in the next iteration__syncthreads(); } } // Write the block sub-matrix to global memory; // each thread writes one element int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx; C[c + wB * ty + tx] = Csub; C[c + wB * ty + tx] = Csub; }
Уровень производительностиУровень производительности
Для матрицы 1024*1024 Автоматическое распределение : 3.0 сек - 45 GFLOPS1 блок на SM : 3.6 сек. - 35 GFLOPS2 блока на SM : 3.0 сек. - 45 GFLOPSОчевидно ограничивающим фактором является Очевидно, ограничивающим фактором является требование запуска 2 блоков на SM, а не ограничение по разделяемой памяти (8 блоков на SM)
Развёртывание циклов помогает (развёрнуто 16 раз)Развернутая программа (автомат) : 1.6 сек. - 80 GFLOPS1 блок на SM : 2.1 сек. - 61 GFLOPS2 блока на SM : 1.6 сек. - 80 GFLOPS
Уровень производительностиУровень производительности(продолжение)
Возможный альтернативный график вычислений
Loop { Load to Shared MemoryLoop {
Load to Shared Memory
Load to Shared MemorySyncthread()
y
Syncthread()Loop {
C bbl kCompute current subblock
Compute current subblockLoad to Shard Memory
Syncthreads()}
Syncthreads()}} }
Texture Processor Cluster (TPC)Texture Processor Cluster (TPC)
TEX Т й б TPC
TEX – Текстурный блок – логика адресации текстурных массивов в 1D, 2D, 3D
+ L1 Кэш Текстур
Tex T
SM 0
Shared Memory
Instruction Fetch
Instruction L 1 Cache
Instruction Decode
I+ L1 Кэш Текстур
L2 Кэш инструкций и данных для обоих SM
ture
extu
SP 0 R
SP 1 R
SP 2 R
SP 3 R
SP 4R
SP 5R
SP 6R
SP 7R
SFU
SFU
&D
L SM
x2 Потоковых Мультипроцессора (Streaming Multiprocessor)
L1
re
U
SM 1Instruction Fetch
Instruction L 1 Cache
I t ti D d
Constant L 1 Cache 2
Ca (St ea g u t p ocesso )
x8 потоковых процессоров (streaming processors) = 8 MAD/clock cycleРегистры для хранения промежуточных
Cach
nit
SP 0 R
SP 1 R
SP 2 R
SP 4R
SP 5R
SP 6R
Shared Memory
SFU
SFU
Instruction Decode
che
р р р урезультатов у выполняемых тредов => больше тредов лучше скрыты операции чтения, но межет не хватить регистров
e
Load/Store
SP 3 R SP 7R
Constant L 1 Cache
Использование текстурИспользование текстур(обзор)
Текстуры - удобный способ обращения с табличными исходными даннымиК 2 SM б й L1 б й L2Кэшированы – для каждых 2-х SM – общий L1 кэш, общий L2кэш для всего кристалла
Замена разделяемой памяти для RO операндовН ё б й Нет жёстких требований к правильности адресации
Адреса – числа с плавающей точкой (возможно с нормализацией)
Позволяют выделять объекты с адресом, находящимся между табличными значениями
Аппоксимация ближайшимЛ й б й ( й Линейная и билинейная аппроксимация (исходя из значений соседей)
Варианты “tile” и “center”Поведение текстуры при выходе адреса за пределы сеткиПоведение текстуры при выходе адреса за пределы сетки
Использование текстур Использование текстур (детали)
Объявление вне тела функции - texture<Type, Dim, ReadMode> texRef;Type = типDim = 1 | 2Dim 1 | 2ReadMode = cudaReadModeNormalizedFloat | CudaReadModeElementType
Определение – привязывание объявленной ссылки на 1D (линейный массив) или 2D (CudaArray) объекту в глобальной памяти или 2D (CudaArray) объекту в глобальной памяти
cudaBindTexture()cudaBindTextureToArray()cudaUnbindTexture()
Использование – “texture fetch”1D-массивTexture_type tex1Dfetch(texture<uchar4, 1, cudaReadModeNormalizedFloat> texRef,int x);2D-массивTexture_type tex1D(texture<Type, 1, readMode> texRef, float x); Texture_type tex2D(texture<Type, 2, readMode> texRef, float x, float y);
Использование текстур Использование текстур (пример часть 1)
texture<float, 2, cudaReadModeElementType> tex; // declare texture reference for 2D float texture__global__ void transformKernel( float* g_odata, int width, int height, float theta) {
// calculate normalized texture coordinatesunsigned int x = blockIdx.x*blockDim.x + threadIdx.x;unsigned int y = blockIdx.y*blockDim.y + threadIdx.y;
float u = x / (float) width;float v = y / (float) height;
u -= 0.5f; // transform coordinatesu 0.5f; // transform coordinatesv -= 0.5f;float tu = u*cosf(theta) - v*sinf(theta) + 0.5f;float tv = v*cosf(theta) + u*sinf(theta) + 0.5f;
// read from texture and write to global memoryg_odata[y*width + x] = tex2D(tex, tu, tv);
}
Использование текстур Использование текстур (пример часть 2)
// set texture parameterstex.addressMode[0] = cudaAddressModeWrap;tex.addressMode[1] = cudaAddressModeWrap;ptex.filterMode = cudaFilterModeLinear;tex.normalized = true; // access with normalized texture coordinates
// Bind the array to the texture// yCUDA_SAFE_CALL( cudaBindTextureToArray( tex, cu_array, channelDesc));
dim3 dimBlock(8, 8, 1);dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);dim3 dimGrid(width / dimBlock.x, height / dimBlock.y, 1);
// warmuptransformKernel<<< dimGrid, dimBlock, 0 >>>( d_data, width, height, angle);
CUDA_SAFE_CALL( cudaThreadSynchronize() );
// execute the kerneltransformKernel<<< dimGrid dimBlock 0 >>>( d data width height angle);transformKernel<<< dimGrid, dimBlock, 0 >>>( d_data, width, height, angle);
Обращение с памятью из ворпаОбращение с памятью из ворпа
НЕАТОМАРНЫЕ ИНСТРУКЦИИ (G80)ЕСЛИ какая-либо инструкция исполняемая ворпом пишет в одно место в глобальной или общей памяти ТО количество записей и их очерёдность недетерминированыОДНАКО по крайней мере одна запись состоится
АТОМАРНЫЕ ИНСТРУКЦИИ (G92+)ЕСЛИ какая-либо инструкция исполняемая ворпом пишет/читает/модифицирует одно место в глобальной памяти ТО их очерёдность записей недетерминированаОДНАКО все записи состоятся последовательно
Атомарные функцииАтомарные функции
Функции чтения/модификации/записи данных в глобальную память – основа построения алгоритмов стекового декодированияРаботают только с целыми значениями (!)Гарантируют неизменность операнда в процессе операцииоперации
Арифметические функции: atomicAdd,atomicSub, atomicExch, atomicMax, atomicInc, atomicDecatomicMax, atomicInc, atomicDec
int atomicAdd(int* address, int val);
Функция atomicCAS – Compare and store
int atomicCAS(int* address, int compare, int val);
Битовые функции atomicAnd, atomicOr, atomicXor int atomicAnd(int* address, int val);
Библиотека cutilБиблиотека cutil
Набор утилит для различных служебных целей (cut* - функции)Макросы типа CUDA_SAFE_CALL - различные ситуции, с синхронизацией без неё для эмуляциисинхронизацией, без неё, для эмуляцииУтилиты профайлинга
измерение длительности исполнения CUT SAFE CALL( cutCreateTimer( &timer));CUT_SAFE_CALL( cutCreateTimer( &timer));CUT_SAFE_CALL( cutStartTimer( timer)); CUT_SAFE_CALL( cutStopTimer( timer));CUT SAFE CALL( cutDeleteTimer(&timer));CUT_SAFE_CALL( cutDeleteTimer(&timer));Определение конфликтных ситуаций операций чтенияИ т.д. => см cutil.h
Находится в директории common в составе SDKНаходится в директории common в составе SDKВ отличии от других библиотек предоставляется в виде исходных текстовНеобходимо скомпилировать до работы над своим проектомНеобходимо скомпилировать до работы над своим проектом
Итоги лекцииИтоги лекции
В результате лекции студенты должны :Получить практический пример составления вычислительного ядра для типовой задачи.Получить представление о свойствах текстур и возможности их применения в научных вычислениях
Получить представление о свойствах и возможности применения в научных вычислениях атомарных функций
Достаточные знания для начала самостоятельной работы