Программирование для gpu с использованием nvidia cuda

20
Программирование для GPU с использованием NVidia CUDA. Половинкин А.Н.

Upload: theophilia-xylia

Post on 01-Jan-2016

48 views

Category:

Documents


6 download

DESCRIPTION

Программирование для GPU с использованием NVidia CUDA. Половинкин А.Н. Содержание. Постановка задачи Алгоритм умножения матриц на GPU Программная реализация. Постановка задачи. C = A*B A – прямоугольная матрица число строк – hA число столбцов – wA B – прямоугольная матрица - PowerPoint PPT Presentation

TRANSCRIPT

Программирование для GPU с использованием NVidia

CUDA.

Половинкин А.Н.

Постановка задачи Алгоритм умножения матриц на GPU Программная реализация

Содержание

C = A*B A – прямоугольная матрица

◦ число строк – hA◦ число столбцов – wA

B – прямоугольная матрица◦ число строк – hB◦ число столбцов - wB

Постановка задачи

Алгоритм умножения матриц на GPU (1)

Asub

Bsub

Каждый блок потоков занимается вычислением одной подматрицы Csub матрицы С

Каждый поток внутри блока потоков занимается вычислением одного элемента подматрицы Csub

Алгоритм умножения матриц на GPU (2)

Csub = Asub * Bsub

◦ Asub размерности (block_size, wA)◦ Bsub размерности (wA, block_size)◦ Матрицы Asub и Bsub в общем случае могут не

помещаться в общей памяти устройства, что приведёт к потере производительности

◦Решение: разбить Asub и Bsub на блоки Asub,i и Bsub,i размерности (block_size, block_size), вычислять Csub как сумму произведений этих блоков:

Алгоритм умножения матриц на GPU (3)

i

isubisubsub BAC ,,

for i = 1 to wA/block_size◦ загрузить Asub,i и Bsub,i из глобальной (device)

памяти в общую (shared) память блока потоков. Каждый поток загружает «свой» элемент!

◦ каждый поток вычисляет «свой» элемент в произведении Asub,i *Bsub,i и сохраняет аккумулированное значение

end каждый поток загружает «свой»

вычисленный элемент в глобальную (device) память

Алгоритм умножения матриц на GPU (4)

matrixMul.h – содержит определения (через define) размера блока и размеров матриц

matrixMul_gold.cpp◦ computeGold

matrixMul.cu◦ main◦ randomInit◦ printDiff◦ runMultiplication

matrixMul_kernel.cu◦ matrixMul (kernel)

Структура проекта

Используется схема хранения матриц по строкам в виде одномерного массива

__global__ void matrixMul( float* C, float* A, float* B, int wA, int wB)◦ вычисляем координаты текущего блока

потоков и сохраняем их в переменные bx, byint bx = blockIdx.xint by = blockIdx.y

◦ вычисляем координаты текущего потока в блоке потоков и сохраняем их в переменные tx, ty

Реализация функции-ядра (1)

вычисляем индекс элемента в массиве, хранящем исходную матрицу A, который соответствует первому элементу первой обрабатываемой подматрицы Asub,1

int aBegin = wA * BLOCK_SIZE * by; вычисляем шаг для перехода к первому

элементу следующей обрабатываемой подматрицыint aStep = BLOCK_SIZE;

вычисляем индекс (условие останова перебора подматриц Asub,i)int aEnd = aBegin + wA - 1;

Реализация функции-ядра (2)

вычисляем индекс элемента в массиве, хранящем исходную матрицу B, который соответствует первому элементу первой обрабатываемой подматрицы Bsub,1

int bBegin = …

вычисляем шаг для перехода к первому элементу следующей обрабатываемой подматрицыint bStep = …

объявляем переменную, в которой хранится элемент произведения, вычисляемый текущим потокомfloat Csub = 0.f;

Реализация функции-ядра (3)

В цикле по всем подматрицам Asub,i, Bsub,i выполняем следующие действияfor (int a = aBegin, b = bBegin; a <= aEnd;

a += aStep, b += bStep) { … }

◦ объявляем в общей (shared) памяти подматрицы Asub,i и Bsub,i

__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];

__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];

◦ копируем из глобальной памяти в общую элементы соответствующих подматриц (каждый поток копирует «свой» элемент)

◦ синхронизируем все потоки в блоке потоков__syncthreads();

Реализация функции-ядра (4)

◦ каждый поток вычисляет свой элемент Csub в произведении подматриц Asub,i*Bsub,i

◦ синхронизируем потоки в блоке потоков конец цикла загрузить вычисленный элемент Csub в

соответствующий элемент матрицы С (в глобальную память)int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;C[c + wB * ty + tx] = Csub;

Реализация функции-ядра (5)

void runMultiplication(int argc, char** argv)

инициализируем устройство (device)CUT_DEVICE_INIT(argc, argv);

выделяем память на хосте для хранения матриц A и Bunsigned int size_A = WA * HA;unsigned int mem_size_A = sizeof(float) * size_A;float* h_A = (float*)malloc(mem_size_A);unsigned int size_B = WB * HB;unsigned int mem_size_B = sizeof(float) * size_B;float* h_B = (float*)malloc(mem_size_B);

Реализация функции runMultiplication (1)

инициализируем матрицы A и B случайными значениямиrandomInit(h_A, size_A);

randomInit(h_B, size_B);

выделяем память под матрицы A и B на устройстве, копируем данные с хоста на устройствоfloat* d_A;CUDA_SAFE_CALL(cudaMalloc((void**)&d_A, mem_size_A));float* d_B;CUDA_SAFE_CALL(cudaMalloc((void**)&d_B, mem_size_B));CUDA_SAFE_CALL(cudaMemcpy(d_A, h_A, mem_size_A,cudaMemcpyHostToDevice) );CUDA_SAFE_CALL(cudaMemcpy(d_B, h_B, mem_size_B,cudaMemcpyHostToDevice) );

выделяем память под матрицу C на хосте и на устройстве (имена соответствующих переменных h_C, d_C, size_C, mem_size_C)

Реализация функции runMultiplication (2)

создаем и инициализируем таймерunsigned int timer = 0;CUT_SAFE_CALL(cutCreateTimer(&timer));CUT_SAFE_CALL(cutStartTimer(timer));

определяем конфигурацию выполнения ядра (размер решетки блоков потоков и блока потоков)dim3 threads(BLOCK_SIZE, BLOCK_SIZE);

dim3 grid(WC / threads.x, HC / threads.y);

запускаем ядро копируем вычисленную матрицу С с

устройства на хост

Реализация функции runMultiplication (3)

останавливаем таймер, выводим время вычислений, освобождаем ресурсы таймераCUT_SAFE_CALL(cutStopTimer(timer));printf("Processing time: %f (ms) \n", cutGetTimerValue(timer));

CUT_SAFE_CALL(cutDeleteTimer(timer));

вычисляем то же самое произведение на CPUfloat* reference = (float*) malloc(mem_size_C);

computeGold(reference, h_A, h_B, HA, WA, WB);

Реализация функции runMultiplication (4)

сравниваем результат, полученный на GPU, с результатом, полученным на CPU (по евклидовой норме)CUTBoolean res = cutCompareL2fe(reference, h_C, size_C, 1e-6f);printf("Test %s \n", (1 == res) ? "PASSED" : "FAILED");if (res!=1) printDiff(reference, h_C, WC, HC);

освобождаем память

Реализация функции runMultiplication (5)

Nvidia CUDA Programming Guide Многочисленные курсы по CUDA:

◦ http://courses.ece.uiuc.edu/ece498/al1/Syllabus.html

◦ http://www.nvidia.ru/object/cuda_state_university_courses_ru.html (на русском языке)

Литература

?

Вопросы