Você está na página 1de 51

Introduo a CUDA

Esteban Walter Gonzalez Clua


Medialab - Instituto de Computao Universidade Federal Fluminense NVIDIA CUDA Research Center

START

1536 cores Dynamic Parallelism Hyper - Q

Pipeline Grfico
- Pipeline / Estgios - Gargalo - Otimizao - Tipos de Processamento Paralelo
referncia

Real Time Rendering Second Edition Akenine-Mller, Haines

GPGPU a evoluo ...


H 4 fases histricas das GPUs: - Dispositivos de Rasterizao - Fixed Function GPU - Programmable GPU - Arquitetura Unificada

Dispositivos de Rasterizao

-Brinquedo de U$ 175.000,00

Fixed Function GPUs


-Arquitetura incapaz de aceitar programao -Impossvel realizar clculos sem ser de computao grfica -Incapacidade de acesso ao processador - Conceito de APIs

Programmable GPU
- Vertex and Pixel Shaders - Arquitetura orientada a estrutura de dados de computao grfica

Programmable GPU

Programmable GPU

GPGPU
- Problema de ter que mapear tudo para APIs - Usar funes OpenGL ou DirectX para efetuar todo tipo de operaes -LINGUAGEM BROOKS (Ian Buck, 2003)

Arquiteturas Unificadas

GPU

Operaes aritimticas ++ Operaes de memria --

CUDA

16KB / 48KB

768KB

- Maior proximidade com C++ -Acesso a um thread especifico dentro de um cluster de GPUs - Facilidade em implementar algoritmos paralelos que requerem threads concorrentes (entre GPUs) -Host diferentes podem gerenciar threads diferentes -Gerenciamento de memria dinmica -Mtodos virtuais de C++ -Proximidade com STL do C++

- Transferencia direta entre memrias globais

- UVA: Unified Virtual Adressing

- NVIDIA Performance Primitives Libraries (NPP), para processamento de imagens - GPUDirect 2.0

Afinal, de onde vem os poderes???

GPU x CPU

Operaes aritimticas ++ Operaes de memria --

Paralelismo na CPU

Paralelismo na GPU

Threads Custo de gerenciamento


Em CPU, como fazemos pouca troca de threads, podemos achar natural gastar 1000 instrues para fazer a troca de uma thread para outra. Em CUDA h outro paradigma.... No necessrio gerenciar as threads, a priori.

Threads, Blocos e Grids


Um kernel ser executado num grid de Blocos Cada Bloco composto por threads Todas as threads de um bloco podem usar a mesma memria compartilhada Threads de blocos diferentes no podem compartilhar memria entre si, seno a memria global

Funes em CUDA
__ global__ void KernelFunction (...) dim3 DimGrid (100, 10); // Grid com 1000 blocos dim3 DimBlock (4, 8, 8); // Cada bloco tem 256 threads

KernelFun << DimGrid, DimBlock, SharedMemBytes>> (...);

Threads, Blocos e Grids memrias

Funes em CUDA

-No pode haver recurso no __device__ -Sem variveis estticas

kernel
__global__ void vecAdd(float* A, float* B, float* C) { int i = threadIdx.x; C[i] = A[i] + B[i]; } int main() { ... vecAdd<<<1, N>>>(A, B, C); }

threadIdx << n, m>>

define um ID de um dos threads numero de blocos (n) e threads (m) solicitados para o kernel

kernel
__global__ void vecAdd(float* A, float* B, float* C) { int i = threadIdx.x; C[i] = A[i] + B[i]; } int main() { vecAdd<<<1, N>>>(A, B, C); }

threadIdx << n, m>>

define um ID de um dos threads numero de blocos (n) e threads (m) solicitados para o kernel PROBLEMA - Nmero mximo de threads por bloco: 1024 - Nmero mximo de Blocos: 65.535

Kernel: Blocos ou Threads, eis a questo...


__global__ void vecAdd(float* A, float* B, float* C) { int id = threadIdx.x + blockIdx.x * blockDim.x; C[id] = A[id] + B[id]; } int main() { vecAdd<<<M, N>>>(A, B, C); }

WARPS

Invocando o kernel
Int main (void) { int A[N], B[N], C[N]; int *dev_a, *dev_b, *dev_c; cudaMalloc ( (void **) &dev_a, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_b, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_c, N * sizeof (int) ) ); // Supondo que a e b sejam preenchidos aqui... cudaMemcpy (dev_a, A, N * sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy (dev_b, B, N * sizeof(int), cudaMemcpyHostToDevice); VecAdd <<dimGrid, dimBlocos>> (dev_a, dev_b, dev_c); cudaMemcpy (C, dev_c, N * sizeof(int), cudaMemcpyHostToDevice); // Use o resultado da soma no vetor C.... CudaFree (dev_a); CudaFree (dev_b); CudaFree (dev_c); }

E se meu vetor for maior que o total de blocos e threads???


// Vetor com N dados, onde N maior que o total de threads*blocos possvel __global__ void vecAdd(float* A, float* B, float* C) { int id = threadIdx.x + blockIdx.x * blockDim.x; While (id < N) { C[id] = A[id] + B[id]; id += blockDim.x * gridDim.x; } }

Um pouco sobre memria(s)...


Int main (void) { int A[N], B[N], C[N]; int *dev_a, *dev_b, *dev_c; cudaMalloc ( (void **) &dev_a, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_b, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_c, N * sizeof (int) ) ); // Supondo que a e b sejam preenchidos aqui... cudaMemcpy (dev_a, A, N * sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy (dev_b, B, N * sizeof(int), cudaMemcpyHostToDevice); VecAdd <<2500, 128>> (dev_a, dev_b, dev_c); cudaMemcpy (C, dev_c, N * sizeof(int), cudaMemcpyHostToDevice); // Use o resultado da soma no vetor C.... CudaFree (dev_a); CudaFree (dev_b); CudaFree (dev_c); }

Que memria esta???


Int main (void) { int A[N], B[N], C[N]; int *dev_a, *dev_b, *dev_c; cudaMalloc ( (void **) &dev_a, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_b, N * sizeof (int) ) ); cudaMalloc ( (void **) &dev_c, N * sizeof (int) ) ); // Supondo que a e b sejam preenchidos aqui... cudaMemcpy (dev_a, A, N * sizeof(int), cudaMemcpyHostToDevice); cudaMemcpy (dev_b, B, N * sizeof(int), cudaMemcpyHostToDevice); VecAdd <<2500, 128>> (dev_a, dev_b, dev_c); cudaMemcpy (C, dev_c, N * sizeof(int), cudaMemcpyHostToDevice); // Use o resultado da soma no vetor C.... CudaFree (dev_a); CudaFree (dev_b); CudaFree (dev_c); }

A memria compartilhada
Como os threads podem interagir e intercambiar dados entre si? Nada vem de graa: problema de concorrncia de dados

Exemplo de memria compartilhada


(x1, x2, x3, x4) . (y1, y2, y3, y4) = x1.y1 + x2.y2 + x3.y3 + x4.y4

const int numThreadsPorBloco = 256; __global__ void dot (float *x, float *y, float *z) { __shared__ float cache [numThreadsPorBloco]; Int id = threadIdx.x + blockIdx.x * blockDim.x; Int cacheIndex = threadIdx.x; Float temp = 0; While (id < N) { Temp += x[id] * y[id]; Id += blockDim.x * gridDim.x; } cache[cacheIndex] = temp; }

Problema da Sincronizao...
(x1, x2, x3, x4) . (y1, y2, y3, y4) = x1.y1 + x2.y2 + x3.y3 + x4.y4

const int numThreadsPorBloco = 256; __global__ void dot (float *x, float *y, float *z) { __shared__ float cache [numThreadsPorBloco]; Int id = threadIdx.x + blockIdx.x * blockDim.x; Int cacheIndex = threadIdx.x; Float temp = 0; While (id < N) { Temp += x[id] * y[id]; Id += blockDim.x * gridDim.x; } cache[cacheIndex] = temp; }

Problema da Sincronizao...

// Espere todos os threads terminarem de calcular a soma __syncthreads (); int i = blockDim.x/2; While (i != 0) { If (cacheIndex < i) cache[cacheIndex] += cache[cacheIndex + i]; __syncthreads(); } If (cacheIndex == 0) c[blockIdx.x] = cache[0]; }

Memria Constante e Memria de Textura

CUDA Research Center & Teaching Center

Você também pode gostar