56
Лекция 11: NVIDIA CUDA Курносов Михаил Георгиевич к.т.н. доцент Кафедры вычислительных систем Сибирский государственный университет телекоммуникаций и информатики http://www.mkurnosov.net

Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Лекция 11:

NVIDIA CUDA

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

к.т.н. доцент Кафедры вычислительных систем

Сибирский государственный университет

телекоммуникаций и информатики

http://www.mkurnosov.net

Page 2: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

NVIDIA CUDA

22

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

2013 – CUDA 5.5 (CUDA 6.0 announced)

2006 – CUDA 1.0

Page 3: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

33

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 4: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

44

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), …

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

o NVIDIA Tesla (2007)

o NVIDIA Fermi (GeForce 400 Series, 2010)

o NVIDIA Kepler (GeForce 600 Series, 2012)

o NVIDIA Maxwell (2014)

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

Page 5: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

55

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 6: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

66

NVIDIA Tesla K10 (2 GPU)

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

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

RAM: 8GB GDDR5

PCI Express 3.0

Page 7: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

77

15 SMX – streaming multiprocessor

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

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

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

Page 8: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

88

Page 9: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

99

SMX –streaming

multiprocessor

Page 10: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1010

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

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

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

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

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

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

Page 11: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1111

Page 12: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Warp scheduler (GK110)

1212

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

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

Каждый такт потоки группы (warp) выполняют две

независимые инструкции (допустимо совмещение

инструкций double и float)

Page 13: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Warp scheduler (GK110)

1313

Page 14: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1414

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

o 48KB shared + 16KB L1 cache

o 16KB shared + 48KB L1 cache

L2 Cache 1536KB –

общий для всех SMX

Global Memory 8GB

Page 15: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1515

FERMI

GF100

FERMI

GF104

KEPLER

GK104

KEPLER

GK110

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 /

Multiprocessor8 8 16 16

Max Threads / Thread Block 1024 1024 1024 1024

32‐bit Registers /

Multiprocessor32768 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 16: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

NVIDIA Maxwell (2014)

1616

NVIDIA Maxwell = GPU Cores + ARM Core

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

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

o поддержка унифицированной виртуальной памяти

(device ←→ host)

Page 17: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1717

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

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

и его память

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

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

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

ядра на GPU

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

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

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

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

Page 18: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1818

CPU (Host) GPU (Device)

void kernelA(){

/* Code */}

void kernelB(){

/* Code */}

/* Serial code */

/* Serial code */

/* Serial code */

kernelA()

kernelB()

Page 19: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

1919

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

данных из памяти

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

Page 20: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2020

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

данных из памяти

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

Загрузка и выполнение

ядра (kernel) в GPU

Page 21: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2121

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

данных из памяти

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

Загрузка и выполнение

ядра (kernel) в GPU

Копирование данных из

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

Page 22: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

CUDA HelloWorld!

2222

#include <stdio.h>

__global__ void mykernel() {

/* Parallel GPU code (kernel) */ }

int main() {

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

return 0;}

Page 23: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

CUDA HelloWorld!

2323

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

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

-lcudart

Page 24: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2424

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

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

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

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

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

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

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

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

на CPU

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

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

Page 25: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2525

Номер потока (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 26: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2626

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

сетку (grid)

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

SMX (в Kepler 15 SMX)

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

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

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

Page 27: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2727

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 28: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

CUDA Program

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

2828

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 29: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

2929

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 30: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

3030

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 31: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

31

// Выделяем память на 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;} 31

Page 32: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

32

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

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

}

32

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

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

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

Thread Block

Thread 0 Thread 1 Thread n - 1…

Page 33: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

3333

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

int threadsPerBlock = 256; /* Device specific */

int blocksPerGrid = (n + threadsPerBlock - 1) /

threadsPerBlock;

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

devb, devc, n);

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

фиксированному количеству потоков

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

Page 34: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

3434

__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 35: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

3535

dim3 threadsPerBlock(N, N);

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

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

Page 36: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

3636

cudaSetDevice(0);

cudaDeviceProp deviceProp;

cudaGetDeviceProperties(&deviceProp, 0);

/*

* deviceProp.maxThreadsPerBlock

* deviceProp.warpSize

* devProp.totalGlobalMem

* ...

*/

Page 37: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

NVIDIA GeForce GTS 250

3737

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 38: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

38

NVidia GeForce GTS 250

Global memory: 1GB

Shared mem. per block: 16KB

Constant memory: 64KB

Registers per block: 8192

38

Page 39: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Data race

3939

__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 40: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

CUDA Atomics

4040

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 41: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

CUDA Atomics

4141

__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 42: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4242

C = A * B

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

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

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

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

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

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

Page 43: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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);

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

4343

Page 44: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4444

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 45: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4545

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 46: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4646

free(h_A);

free(h_B);

free(h_C);

cudaFree(d_A);

cudaFree(d_B);

cudaFree(d_C);

return 0;

} /* main */

Page 47: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4747

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 48: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4848

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 49: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

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

4949

// 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 50: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Reduction

5050

O(log2n)

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

Page 51: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Reduction v1

5151

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

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

Page 52: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Reduction v2

5252

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

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

Page 53: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Dynamic parallelism (CUDA 5.0)

5353

__global__ ChildKernel(void* data)

{

// Operate on data

}

__global__ ParentKernel(void *data)

{

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

}

// In Host Code

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

Page 54: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Dynamic parallelism (CUDA 5.0)

5454

__global__ RecursiveKernel(void* data)

{

if (continueRecursion == true)

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

}

Page 55: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Dynamic parallelism (CUDA 5.0)

5555

Page 56: Лекция 11: Программирование графических процессоров на NVIDIA CUDA

Литература

5656

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