Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах Томский политехнический университет Аксёнов Сергей Владимирович к.т.н., доцент каф.ОСУ ТПУ Преимущества и недостатки SISD архитектуры Преимущества Недостатки Последовательное исполнение инструкций Применение конвейеров Общая память данных Кэширование обращений к памяти Применение отработанных оптимизирующих компиляторов Блочное чтение памяти Программирование е требует особых знаний об архитектуре Невозможность наращивания тактовой частоты современных CPU Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 2 GPU (Graphics Processing Unit) архитектура VS SMP Простые АЛУ, имеющие общую память Кэш и многофункциональные АЛУ Преимущества GPU: Однократная систематическая задержка между потоком исходных данных и результатов. Недостаток GPU: Тщательная разработка алгоритма на управляющих элементах Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 3 GPU: Основные положения Является сопроцессором к CPU Обладает собственной памятью Обладает возможностью параллельного выполнения огромного числа нитей Отличия между нитями на CPU и GPU: 1.Создание, управление и удаление нитей на GPU – низкозатратный процесс. Те же самые операции с нитями на CPU достаточно ресурсоёмки. 2.Число нитей на CPU зависит от числа ядер и крайне мало, количество нитей на GPU достаточно большое (несколько тысяч или десятков тысяч) Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 4 Работа с нитями GPU GPU обладает рядом потоковых мультипроцессоров (Streaming Multiprocessor), каждый из которых способен одновременно выполнять 768 (1024) нитей. Все мультипроцессоры работают независимо друг от друга. Нити разбиваются на группы по 32 нити (warp ). Нити в пределах каждой группы выполняются физически одновременно. Нити в разных группах могут находится на разных стадиях выполнения программ. Управление группой выполняет GPU. Обычно каждой нити соответствует один элемент вычисляемых данных. Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 5 Иерархия нитей в CUDA Ядро – функция, которая выполняется каждой нитью Верхний уровень иерархии – сетка (все нити, выполняющие ядро). Верхний уровень – одномерный или двухмерный массив блоков. Каждый блок – 1D, 2D или 3D массив нитей. Все блоки обладают одинаковой размерностью и размером. Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 6 Встроенные переменные CUDA threadIdx – Идентификатор нити в блоке blockIdx – Идентификатор блока в сетке gridDim – размер сетки (количество блоков в сетке) blockDim – размер блока (количество нитей в блоке) Пример (для одномерного случая) int globalIdx = threadIdx.x+blockIdx.x*blockDim.x; Указанные переменные – трехмерный целочисленный вектор. Доступны только для функций, выполняемых на GPU. Разбиение нитей на группы происходит отдельно для каждого блока. Нити могут взаимодействовать между собой только в пределах блока. Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 7 Пример ядра: сложение 1D массивов __global__ void sumOfVectors(float *a, float *b, float *c) { int index = blockIdx.x*blockDim.x + threadIdx.x; c[index] = a[index] + b[index]; } Каждая нить находит вычисляет только один элемент массива с. Index определяет номер нити и индекс вычисляемого элемента массива. Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 8 Взаимодействие нитей внутри блока Разделяемая память (быстрая память 16Кбайт, которую нити блока могут совместно использовать). Барьерная синхронизация (синхронизация нитей блока). Вызов функции __synchthreads(). Блокировка вызывающих нитей блока до тех пор, пока все нити не войдут в эту функцию. Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 9 Спецификаторы функций Квалификатор Выполняется на Вызывается на __device__ device device __host__ host host __global__ device host __global__ определяет ядро и эту функция возвращает результат void Невозможно взять адрес __device__ функции Функции, выполняемые на устройстве, не допускают рекурсию, объявление статических переменных Пример __global__ void KernelFunction(float a) Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 10 Структура CUDA-программы 1. Выделение памяти на GPU 2. Копирование данных из RAM в выделенную память GPU 3. Запуск ядра 4. Копирование результатов из памяти GPU в RAM 5. Освобождение памяти GPU Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 11 Выделение и освобождение памяти GPU Выделение памяти на GPU cudaMalloc(void ** devPtr, size_t size) devPtr – Указатель на память size – Размер выделяемой памяти в байтах Освобождение памяти GPU cudaFree(void *devPtr) devPtr – Указатель на память для освобождения Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 12 Копирование данных из RAM в память GPU и обратно cudaMemcpy(void *dest, cudaMemcpyKind kind) const void *src, size_t size, enum dest – Адрес памяти приёма данных src – Адрес памяти отправления данных size – Размер кипируемых данных в байтах kind – Направление копирования {cudaMemcpyHostToHost, cudaMemcpyHostToDevice, cudaMemcpyDeviceToHost, cudaMemcpyDeviceToDevice}. Примеры cudaMemcpy(aDev, aRAM, 1024, cudaMemcpyHostToDevice); cudaMemcpy(bRAM, cDev, 2048, cudaMemcpyDeviceToHost); Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 13 Вызов ядра kernelName<<<dim3 Dg, dim3 Db[, size_t Ns, cudaStream_t S]>>>(args) kernelName – Имя __global__ функции. Dg – размерность и размер сетки в блоках Db – размерность и размер блока в нитях Ns – размер дополнительно выделяемой разделяемой памяти (опционально) S- поток СUDA, в котором должен произойти вызов, по умолчанию используется поток 0 (опционально). args – список аргументов функции kernelName Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 14 Пример программы GPU для сложения двух векторов: начало __global__ void sumOfVectors(float *a, float *b, float *c) { int index = blockIdx.x*blockDim.x + threadIdx.x; c[index] = a[index] + b[index]; } void main(float *a, float *b, float *c) { int numBytes = n*sizeof(float); float *aDev, *bDev, *cDev; cudaMalloc((void**)&aDev, numBytes); cudaMalloc((void**)&bDev, numBytes); cudaMalloc((void**)&cDev, numBytes); Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 15 Пример программы GPU для сложения двух векторов: окончание //Определение конфигурации сетки dim3 threads = dim3(512, 1); dim3 blocks = dim3(n/threads.x, 1); cudaMemcpy(aDev, a, numBytes, cudaMemcpyHostToDevice); cudaMemcpy(bDev, b, numBytes, cudaMemcpyHostToDevice); sumOfVectors<<<blocks, threads>>>(aDev, bDev, cDev); cudaMemcpy(c, cDev, numBytes, cudaMemcpyDeviceToHost); cudaFree(aDev); cudaFree(bDev); cudaFree(cDev); } Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 16 Получение информации о GPU #include <stdio.h> int main ( int argc, char * argv [] ) { int deviceCount; cudaDeviceProp devProp; cudaGetDeviceCount ( &deviceCount ); // Определение числа устройств GPU printf ( "Found %d devices\n", deviceCount ); for ( int device = 0; device < deviceCount; device++ ) { cudaGetDeviceProperties ( &devProp, device );// Получение характеристик GPU } } printf ( "Device %d\n", device ); printf ( "Compute capability : %d.%d\n", devProp.major, devProp.minor ); printf ( "Name : %s\n", devProp.name ); printf ( "Total Global Memory : %d\n", devProp.totalGlobalMem ); printf ( "Shared memory per block: %d\n", devProp.sharedMemPerBlock ); printf ( "Registers per block : %d\n", devProp.regsPerBlock ); printf ( "Warp size : %d\n", devProp.warpSize ); printf ( "Max threads per block : %d\n", devProp.maxThreadsPerBlock ); printf ( "Total constant memory : %d\n", devProp.totalConstMem ); return 0; Технологии высокопроизводительных вычислений на GPU и гибридных вычислительных системах - Лекция 1 17