59
Курносов Михаил Георгиевич E-mail: [email protected] WWW: www.mkurnosov.net Курс “Высокопроизводительные вычислительные системы” Сибирский государственный университет телекоммуникаций и информатики (Новосибирск) Осенний семестр, 2014 Лекция 9 Программирование GPU

Лекция 9. Программирование GPU

Embed Size (px)

DESCRIPTION

Программирование GPU (NVIDIA CUDA)

Citation preview

Page 1: Лекция 9. Программирование GPU

Курносов Михаил Георгиевич

E-mail: [email protected]: www.mkurnosov.net

Курс “Высокопроизводительные вычислительные системы”Сибирский государственный университет телекоммуникаций и информатики (Новосибирск)Осенний семестр, 2014

Лекция 9Программирование GPU

Page 2: Лекция 9. Программирование GPU

Graphics Processing Unit (GPU) – графический процессор, специализированный многопроцессорный ускоритель с общей памятью

Большая часть площади чипа занята элементарными ALU/FPU/Load/Store модулями

Устройство управления (Control unit) относительно простое по сравнению с CPU

NVIDIA GeForce GTX 780 (Kepler, 2304 cores, GDDR5 3 GB)

AMD Radeon HD 8970(2048 cores, GDDR5 3 GB)

GPU – Graphics Processing Unit

Page 3: Лекция 9. Программирование GPU

Гибридные вычислительные узлы

Несколько GPU

CPU управляет GPU через драйвер

Узкое место –передача данных между памятью CPU и GPU

GPU не является bootable-устройством

74 системы из Top500 (Nov. 2014) оснащены ускорителями (NVIDIA, ATI, Intel Xeon Phi, PEZY SC )

CPU1

Core1 Core2 Core3 Core4

GPU1

Cores

GPU Memory

Memory (DDR3) Memory (DDR3)

QPI

I/O Hub

QPI QPI

PCI Express Gen. 2

CPU1

Core1 Core2 Core3 Core4

GPU1

Cores

GPU Memory

Page 4: Лекция 9. Программирование GPU

Кластер Jet

Три узла с GPU (CUDA, MPI + CUDA):

cngpu1 – NVIDIA GeForce GTX 680

cngpu2 – 2 x NVIDIA GeForce 210

cngpu3 – NVIDIA GeForce GT 630

http://cpct.sibsutis.ru/index.php/Main/CUDAGTX680

Page 5: Лекция 9. Программирование GPU

NVIDIA CUDA

55

NVIDIA CUDA – программно-аппаратная

платформа для организации параллельных

вычислений на графических процессорах

(Graphic processing unit – GPU)

http://developer.nvidia.com/cuda

NVIDIA CUDA SDK:

o архитектура виртуальной машины CUDA

o компилятор C/C++

o драйвер GPU

ОС: GNU/Linux, Apple Mac OS X, Microsoft Windows

2014 – CUDA 6.5 (+ 64-bit ARM, Unified memory, Multi-GPU)

2006 – CUDA 1.0

Page 6: Лекция 9. Программирование GPU

Архитектура NVIDIA CUDA

66

CUDA C/C++/Fortran Source

CUDA C/C++/Fortran Compiler(NVIDIA nvcc, PGI pgfortran)

PTX (Parallel Thread Execution)(NVIDA GPU assembly language)

GPU Driver (JIT compiler)

GPU binary code

Host (CPU)

Device (GPU)

CPU Code

Page 7: Лекция 9. Программирование GPU

Архитектура NVIDIA CUDA

77

NVIDIA C/C++ Compiler (nvcc) – компилятор

c расширений языков C/C++ (основан на LLVM), генерирует

код для CPU и GPU

NVIDA PTX Assembler (ptxas)

Набор команд PTX развивается:

PTX ISA 3.2 (2013), PTX ISA 3.1 (2012), PTX ISA 4.1 (2014)

Архитектуры NVIDIA CUDA

o NVIDIA Tesla (2007)

o NVIDIA Fermi (GeForce 400 Series, 2010)

o NVIDIA Kepler (GeForce 600 Series, 2012)

o NVIDIA Maxwell (GeForce 700 Series, 2014)

http://docs.nvidia.com/cuda/parallel-thread-execution/index.html

Page 8: Лекция 9. Программирование GPU

Гетерогенные вычислительные узлы

88

CPU1

Core1 Core2 Core3 Core4

GPU1

Cores

GPU Memory

Memory (DDR3) Memory (DDR3)

QPI

I/O Hub

QPI QPI

PCI Express Gen. 2

CPU1

Core1 Core2 Core3 Core4

GPU1

Cores

GPU Memory

Page 9: Лекция 9. Программирование GPU

Гетерогенные вычислительные узлы

99

NVIDIA Tesla K10 (2 GPU)

Процессорные ядра: 2 GPU NVIDIA GK104 (2 x 1536 ядер)

Архитектура: NVIDIA Kepler

RAM: 8GB GDDR5

PCI Express 3.0

Page 10: Лекция 9. Программирование GPU

Архитектура NVIDIA Kepler (GK110)

1010

15 SMX – streaming multiprocessor

(возможны конфигурации с 13 и 14 SMX)

6 контроллеров памяти (64-бит)

Интерфейс подключения к хосту PCI Express 3.0

Page 11: Лекция 9. Программирование GPU

Архитектура NVIDIA Kepler (GK110)

1111

Page 12: Лекция 9. Программирование GPU

Архитектура NVIDIA Kepler (GK110)

1212

SMX –streaming

multiprocessor

Page 13: Лекция 9. Программирование GPU

Архитектура SMX (GK110)

1313

192 ядра для выполнения операций с одинарной

точностью (single precision float, integer)

64 модуля двойной точности (double precision, FMA)

32 модуля специальных функций (SFU)

32 модуля чтения/записи (LD/ST)

4 планировщика потоков (warp schedulers)

Page 14: Лекция 9. Программирование GPU

Архитектура SMX (GK110)

1414

Page 15: Лекция 9. Программирование GPU

Warp scheduler (GK110)

1515

Планировщик организует потоки в группы по 32 (warps)

Потоки группы выполняются одновременно

Каждый такт потоки группы (warp) выполняют две независимые инструкции (допустимо совмещение инструкций double и float)

Page 16: Лекция 9. Программирование GPU

Warp scheduler (GK110)

1616

Page 17: Лекция 9. Программирование GPU

Организация памяти Kepler (GK110)

1717

Каждый SMX имеет 64KB памяти:

o 48KB shared + 16KB L1 cacheo 16KB shared + 48KB L1 cache

L2 Cache 1536KB –общий для всех SMX

Global Memory 8GB

Page 18: Лекция 9. Программирование GPU

Архитектура NVIDIA Kepler (GK110)

1818

FERMIGF100

FERMIGF104

KEPLERGK104

KEPLERGK110

Compute Capability 2.0 2.1 3.0 3.5

Threads / Warp 32 32 32 32

Max Warps / Multiprocessor 48 48 64 64

Max Thread Blocks / Multiprocessor

8 8 16 16

Max Threads / Thread Block 1024 1024 1024 1024

32‐bit Registers / Multiprocessor

32768 32768 65536 65536

Max Registers / Thread 63 63 63 255

Max X Grid Dimension 2^16‐1 2^16‐1 2^32‐1 2^32‐1

Hyper‐Q No No No Yes

Dynamic Parallelism No No No Yes

Page 19: Лекция 9. Программирование GPU

NVIDIA Maxwell (2014)

1919

NVIDIA Maxwell = GPU Cores + ARM Core

Интегрированное ядро ARM (64 бит, проект Denver)

o возможность загрузки операционной системы на GPU

o поддержка унифицированной виртуальной памяти (device ←→ host)

Page 20: Лекция 9. Программирование GPU

Основные понятия CUDA

2020

Хост (host) – узел с CPU и его память

Устройство (device) – графический процессор

и его память

Ядро (kernel) – это фрагмент программы,

предназначенный для выполнения на GPU

Пользователь самостоятельно запускает с CPU

ядра на GPU

Перед выполнением ядра пользователь копирует данные

из памяти хоста в память GPU

После выполнения ядра пользователь копирует данные

из памяти GPU в память хоста

Page 21: Лекция 9. Программирование GPU

Основные понятия CUDA

2121

CPU (Host) GPU (Device)

void kernelA(){

/* Code */}

void kernelB(){

/* Code */}

/* Serial code */

/* Serial code */

/* Serial code */

kernelA()

kernelB()

Page 22: Лекция 9. Программирование GPU

Выполнение CUDA-программы

2222

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование данных из памяти хоста в память GPU

Page 23: Лекция 9. Программирование GPU

Выполнение CUDA-программы

2323

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование данных из памяти хоста в память GPU

Загрузка и выполнение ядра (kernel) в GPU

Page 24: Лекция 9. Программирование GPU

Выполнение CUDA-программы

2424

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование данных из памяти хоста в память GPU

Загрузка и выполнение ядра (kernel) в GPU

Копирование данных из памяти GPU в память хоста

Page 25: Лекция 9. Программирование GPU

CUDA HelloWorld!

2525

#include <stdio.h>

__global__ void mykernel() {

/* Parallel GPU code (kernel) */ }

int main() {

mykernel<<<1, 1>>>();

return 0;}

Page 26: Лекция 9. Программирование GPU

CUDA HelloWorld!

2626

$ nvcc -c -o prog.o ./prog.cu

$ g++ ./prog.o –o prog -L/opt/cuda-5.5/lib64 \

-lcudart

Page 27: Лекция 9. Программирование GPU

Вычислительные ядра (kernels)

2727

Спецификатор __global__ сообщает компилятору,

что функция предназначена для выполнения на GPU

Компилятор nvcc разделяет исходный код –

ядра компилируются nvcc, а остальной код системным

компилятором (gcc, cl, …)

Тройные угловые скобки “<<< >>>” сообщают о вызове

ядра на GPU и количестве требуемых потоков

Вызов ядра (kernel) не блокирует выполнение потока

на CPU

Функция cudaThreadSynchronize() позволяет реализовать

ожидание завершения ядра

Page 28: Лекция 9. Программирование GPU

Вычислительные потоки (threads)

2828

Номер потока (thread index) – это трехкомпонентный

вектор (координаты потока)

Потоки логически сгруппированы в одномерный,

двухмерный или трёхмерный блок (thread block)

Количество потоков в блоке ограничено (в Kepler 1024)

Блоки распределяются по потоковым

мультипроцессорам SMX

Предопределенные переменные

o threadIdx.{x, y, z} – номер потока

o blockDim.{x, y, z} – размерность блока

Thread Block

Thread Thread Thread

Thread Thread Thread

Page 29: Лекция 9. Программирование GPU

Вычислительные потоки (threads)

2929

Блоки группируются одно- двух- и трехмерную

сетку (grid)

Блоки распределяются по потоковым мультипроцессорам

SMX (в Kepler 15 SMX)

Предопределенные переменные

o blockIdx.{x, y, z} – номер блока потока

o gridDim.{x, y, z} – размерность сетки

Page 30: Лекция 9. Программирование GPU

Вычислительные потоки (threads)

3030

Grid of thread blocks

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

gridDim.y

gridDim.x

blockDim.xblockDim.z

blockDim.y

Page 31: Лекция 9. Программирование GPU

CUDA Program

Выполнение CUDA-программы

3131

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

GPU 1

Block 0 Block 1

Block 2 Block 3

Block 4 Block 5

SMX 1 SMX 2

GPU 2

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

SMX 1 SMX 2 SMX 3

Page 32: Лекция 9. Программирование GPU

Пример: сложение векторов

3232

void vecadd(float *a, float *b, float *c, int n){

int i;

for (i = 0; i < n; i++) {c[i] = a[i] + b[i];

}}

Page 33: Лекция 9. Программирование GPU

Пример: сложение векторов

3333

int main() {

int i, n = 100;float *a, *b, *c;float *deva, *devb, *devc;

a = (float *)malloc(sizeof(float) * n);b = (float *)malloc(sizeof(float) * n);c = (float *)malloc(sizeof(float) * n);for (i = 0; i < n; i++) {

a[i] = 2.0;b[i] = 4.0;

}

Page 34: Лекция 9. Программирование GPU

Пример: сложение векторов

34

// Выделяем память на GPUcudaMalloc((void **)&deva, sizeof(float) * n);cudaMalloc((void **)&devb, sizeof(float) * n);cudaMalloc((void **)&devc, sizeof(float) * n);

// Копируем из памяти узла в память GPUcudaMemcpy(deva, a, sizeof(float) * n,

cudaMemcpyHostToDevice);cudaMemcpy(devb, b, sizeof(float) * n,

cudaMemcpyHostToDevice);

vecadd_gpu<<<1, n>>>(deva, devb, devc);

cudaMemcpy(c, devc, sizeof(float) * n, cudaMemcpyDeviceToHost);

cudaFree(deva); cudaFree(devb); cudaFree(devc);free(a); free(b); free(c);

return 0;} 34

Page 35: Лекция 9. Программирование GPU

Пример: сложение векторов

35

__global__ void vecadd_gpu(float *a, float *b, float *c) {

// Каждый поток обрабатывает один элементint i = threadIdx.x;c[i] = a[i] + b[i];

}

35

Запускается один блок из n потоков (n <= 1024)

vecadd_gpu<<<1, n>>>(deva, devb, devc);

Каждый поток вычисляет один элемент массива c

Thread Block

Thread 0 Thread 1 Thread n - 1…

Page 36: Лекция 9. Программирование GPU

Пример: сложение векторов

3636

Сложение векторов с количеством элементов > 256

int threadsPerBlock = 256; /* Device specific */

int blocksPerGrid = (n + threadsPerBlock - 1) /

threadsPerBlock;

vecadd_gpu<<<blocksPerGrid, threadsPerBlock>>>(deva,

devb, devc, n);

Будет запущена группа блоков, в каждом блоке по фиксированному количеству потоков

Потоков может быть больше чем элементов в массиве

Page 37: Лекция 9. Программирование GPU

Пример: сложение векторов

3737

__global__ void vecadd_gpu(float *a, float *b,

float *c, int n)

{

int i = threadIdx.x + blockIdx.x * blockDim.x;

if (i < n)

c[i] = a[i] + b[i];

}

Thread Block 0

Thread 0 Thread 1 Thread ……

Thread Block 1

Thread 0 Thread 1 Thread ……

Thread Block 2

Thread 0 Thread 1 Thread ……

Thread Block 3

Thread 0 Thread 1 Thread ……

Page 38: Лекция 9. Программирование GPU

Двухмерный блок потоков

3838

dim3 threadsPerBlock(N, N);

matrix<<<1, threadsPerBlock>>>(A, B, C);

Двухмерный блок потоков (threadIdx.x, threadIdx.y)

Page 39: Лекция 9. Программирование GPU

Информация о GPU

3939

cudaSetDevice(0);

cudaDeviceProp deviceProp;

cudaGetDeviceProperties(&deviceProp, 0);

/*

* deviceProp.maxThreadsPerBlock

* deviceProp.warpSize

* devProp.totalGlobalMem

* ...

*/

Page 40: Лекция 9. Программирование GPU

NVIDIA GeForce GTS 250

4040

CUDA Device Query (Runtime API) version (CUDART static linking)

Device 0: "GeForce GTS 250"

CUDA Driver Version: 3.20

CUDA Runtime Version: 3.20

CUDA Capability Major/Minor version number: 1.1

Total amount of global memory: 1073020928 bytes

Multiprocessors x Cores/MP = Cores: 16 (MP) x 8 (Cores/MP) =

128 (Cores)

Total amount of constant memory: 65536 bytes

Total amount of shared memory per block: 16384 bytes

Total number of registers available per block: 8192

Warp size: 32

Maximum number of threads per block: 512

Maximum sizes of each dimension of a block: 512 x 512 x 64

Maximum sizes of each dimension of a grid: 65535 x 65535 x 1

Maximum memory pitch: 2147483647 bytes

Texture alignment: 256 bytes

Clock rate: 1.91 GHz

Concurrent copy and execution: Yes

Run time limit on kernels: Yes

Support host page-locked memory mapping: Yes

Compute mode: Default (multiple host

threads can use this device simultaneously)

Concurrent kernel execution: No

Device has ECC support enabled: No

Page 41: Лекция 9. Программирование GPU

Иерархия памяти

41

NVidia GeForce GTS 250

Global memory: 1GB

Shared mem. per block: 16KB

Constant memory: 64KB

Registers per block: 8192

41

Page 42: Лекция 9. Программирование GPU

Data race

4242

__global__ void race(int* x) {

int i = threadIdx.x + blockDim.x * blockIdx.x; *x = *x + 1; // Data race

}

int main(){

int x;

// ...race<<<1, 128>>>(d_x); // ...

return 0;}

Page 43: Лекция 9. Программирование GPU

CUDA Atomics

4343

CUDA предоставляет API атомарных операций:

atomicAdd, atomicSub, atomicInc, atomicDec

atomicMax, atomicMin

atomicExch

atomicCAS

atomicAnd, atomicOr, atomicXor

atomicOP(a,b){

t1 = *a; // readt2 = t1 OP b; // modify*a = t2; // writereturn t;

}

Page 44: Лекция 9. Программирование GPU

CUDA Atomics

4444

__global__ void race(int* x) {

int i = threadIdx.x + blockDim.x * blockIdx.x; int j = atomicAdd(x, 1); // j = *x; *x = j + i;

}

int main(){

int x;

// ...race<<<1, 128>>>(d_x); // ...

return 0;}

Page 45: Лекция 9. Программирование GPU

Умножение матриц

4545

C = A * B

Результирующая матрица C разбивается на подматрицы

размером 16x16 элементов

Подматрицы параллельно вычисляются блоками потоков

Каждый элемент подматрицы вычисляется отдельным

потоком (в блоке 16x16 = 256 потоков)

Количество потоков = количеству элементов в матрице C

Page 46: Лекция 9. Программирование GPU

int main(){

int block_size = 16;dim3 dimsA(10 * block_size, 10 * block_size, 1);dim3 dimsB(20 * block_size, 10 * block_size, 1);printf("A(%d,%d), B(%d,%d)\n", dimsA.x, dimsA.y,

dimsB.x, dimsB.y);

unsigned int size_A = dimsA.x * dimsA.y;unsigned int mem_size_A = sizeof(float) * size_A;float *h_A = (float *)malloc(mem_size_A);

unsigned int size_B = dimsB.x * dimsB.y;unsigned int mem_size_B = sizeof(float) * size_B;float *h_B = (float *)malloc(mem_size_B);

dim3 dimsC(dimsB.x, dimsA.y, 1);unsigned int mem_size_C = dimsC.x * dimsC.y *

sizeof(float);float *h_C = (float *) malloc(mem_size_C);

Умножение матриц

4646

Page 47: Лекция 9. Программирование GPU

Умножение матриц

4747

const float valB = 0.01f;

constantInit(h_A, size_A, 1.0f);

constantInit(h_B, size_B, 0.01f);

float *d_A, *d_B, *d_C;

cudaMalloc((void **) &d_A, mem_size_A);

cudaMalloc((void **) &d_B, mem_size_B);

cudaMalloc((void **) &d_C, mem_size_C);

cudaMemcpy(d_A, h_A, mem_size_A,

cudaMemcpyHostToDevice);

cudaMemcpy(d_B, h_B, mem_size_B,

cudaMemcpyHostToDevice);

Page 48: Лекция 9. Программирование GPU

Умножение матриц

4848

dim3 threads(block_size, block_size);

dim3 grid(dimsB.x / threads.x,

dimsA.y / threads.y);

matmul_gpu<16><<<grid, threads>>>(d_C, d_A,

d_B, dimsA.x, dimsB.x);

cudaDeviceSynchronize();

cudaMemcpy(h_C, d_C, mem_size_C,

cudaMemcpyDeviceToHost);

Page 49: Лекция 9. Программирование GPU

Умножение матриц

4949

free(h_A);

free(h_B);

free(h_C);

cudaFree(d_A);

cudaFree(d_B);

cudaFree(d_C);

return 0;

} /* main */

Page 50: Лекция 9. Программирование GPU

Умножение матриц

5050

template <int BLOCK_SIZE> __global__ void matmul_gpu(float *C, float *A,

float *B, int wA, int wB){

int bx = blockIdx.x;int by = blockIdx.y;int tx = threadIdx.x;int ty = threadIdx.y;

int aBegin = wA * BLOCK_SIZE * by;int aEnd = aBegin + wA - 1;int aStep = BLOCK_SIZE;int bBegin = BLOCK_SIZE * bx;int bStep = BLOCK_SIZE * wB;float Csub = 0;

Page 51: Лекция 9. Программирование GPU

Умножение матриц

5151

for (int a = aBegin, b = bBegin; a <= aEnd; a += aStep, b += bStep)

{// sub-matrix of A__shared__ float As[BLOCK_SIZE][BLOCK_SIZE];

// sub-matrix of B__shared__ float Bs[BLOCK_SIZE][BLOCK_SIZE];

// Load from device memory to shared memoryAs[ty][tx] = A[a + wA * ty + tx];Bs[ty][tx] = B[b + wB * ty + tx];

// Synchronize (wait for loading matrices)__syncthreads();

Page 52: Лекция 9. Программирование GPU

Умножение матриц

5252

// Multiply the two matrices

#pragma unroll

for (int k = 0; k < BLOCK_SIZE; ++k) {

Csub += As[ty][k] * Bs[k][tx];

}

__syncthreads();

} /* for aBegin ... */

// Write the block sub-matrix to device memory;

int c = wB * BLOCK_SIZE * by + BLOCK_SIZE * bx;

C[c + wB * ty + tx] = Csub;

}

Page 53: Лекция 9. Программирование GPU

Reduction

5353

O(log2n)

Mark Harris. Optimizing Parallel Reduction in CUDA // http://www.cuvilib.com/Reduction.pdf

Page 54: Лекция 9. Программирование GPU

Reduction v1

5454

Условный оператор ifвнутри цикла приводит к сильному ветвлению

Можно перераспределить данные и операции по нитям

Page 55: Лекция 9. Программирование GPU

Reduction v2

5555

Количество ветвлений сокращено

Большое число конфликтов банков при обращении к разделяемой памяти

Page 56: Лекция 9. Программирование GPU

Dynamic parallelism (CUDA 5.0)

5656

__global__ ChildKernel(void* data)

{

// Operate on data

}

__global__ ParentKernel(void *data)

{

ChildKernel<<<16, 1>>>(data);

}

// In Host Code

ParentKernel<<<256, 64>>(data);

Page 57: Лекция 9. Программирование GPU

Dynamic parallelism (CUDA 5.0)

5757

__global__ RecursiveKernel(void* data)

{

if (continueRecursion == true)

RecursiveKernel<<<64, 16>>>(data);

}

Page 58: Лекция 9. Программирование GPU

Dynamic parallelism (CUDA 5.0)

5858

Page 59: Лекция 9. Программирование GPU

Литература

5959

CUDA by Example // http://developer.download.nvidia.com/books/cuda-by-

example/cuda-by-example-sample.pdf

Джейсон Сандерс, Эдвард Кэндрот. Технология CUDA в

примерах. ДМК Пресс, 2011 г.

А. В. Боресков, А. А. Харламов. Основы работы с

технологией CUDA. М.:ДМК, 2010 г.

http://www.nvidia.ru/object/cuda-parallel-computing-books-ru.html