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

Post on 24-Jan-2015

760 Views

Category:

Documents

6 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Лекция 11:

NVIDIA CUDA

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

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

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

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

http://www.mkurnosov.net

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

Архитектура 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

Архитектура 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

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

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

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

66

NVIDIA Tesla K10 (2 GPU)

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

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

RAM: 8GB GDDR5

PCI Express 3.0

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

77

15 SMX – streaming multiprocessor

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

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

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

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

88

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

99

SMX –streaming

multiprocessor

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

1010

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

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

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

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

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

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

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

1111

Warp scheduler (GK110)

1212

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

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

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

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

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

Warp scheduler (GK110)

1313

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

1414

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

o 48KB shared + 16KB L1 cache

o 16KB shared + 48KB L1 cache

L2 Cache 1536KB –

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

Global Memory 8GB

Архитектура 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

NVIDIA Maxwell (2014)

1616

NVIDIA Maxwell = GPU Cores + ARM Core

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

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

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

(device ←→ host)

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

1717

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

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

и его память

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

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

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

ядра на GPU

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

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

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

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

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

1818

CPU (Host) GPU (Device)

void kernelA(){

/* Code */}

void kernelB(){

/* Code */}

/* Serial code */

/* Serial code */

/* Serial code */

kernelA()

kernelB()

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

1919

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

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

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

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

2020

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

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

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

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

ядра (kernel) в GPU

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

2121

CPU (Host)

CPU

Memory

PCI Express GPU (Device)

Memory

Копирование

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

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

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

ядра (kernel) в GPU

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

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

CUDA HelloWorld!

2222

#include <stdio.h>

__global__ void mykernel() {

/* Parallel GPU code (kernel) */ }

int main() {

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

return 0;}

CUDA HelloWorld!

2323

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

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

-lcudart

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

2424

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

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

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

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

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

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

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

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

на CPU

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

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

Вычислительные потоки (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

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

2626

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

сетку (grid)

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

SMX (в Kepler 15 SMX)

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

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

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

Вычислительные потоки (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

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

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

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

}}

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

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;

}

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

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

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

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…

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

3333

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

int threadsPerBlock = 256; /* Device specific */

int blocksPerGrid = (n + threadsPerBlock - 1) /

threadsPerBlock;

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

devb, devc, n);

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

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

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

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

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

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

3535

dim3 threadsPerBlock(N, N);

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

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

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

3636

cudaSetDevice(0);

cudaDeviceProp deviceProp;

cudaGetDeviceProperties(&deviceProp, 0);

/*

* deviceProp.maxThreadsPerBlock

* deviceProp.warpSize

* devProp.totalGlobalMem

* ...

*/

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

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

38

NVidia GeForce GTS 250

Global memory: 1GB

Shared mem. per block: 16KB

Constant memory: 64KB

Registers per block: 8192

38

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

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;

}

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

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

4242

C = A * B

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

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

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

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

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

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

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

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

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

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

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

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

4646

free(h_A);

free(h_B);

free(h_C);

cudaFree(d_A);

cudaFree(d_B);

cudaFree(d_C);

return 0;

} /* main */

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

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;

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

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

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

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;

}

Reduction

5050

O(log2n)

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

Reduction v1

5151

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

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

Reduction v2

5252

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

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

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

Dynamic parallelism (CUDA 5.0)

5454

__global__ RecursiveKernel(void* data)

{

if (continueRecursion == true)

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

}

Dynamic parallelism (CUDA 5.0)

5555

Литература

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

top related