aula 14 cuda basics
antes de falarmos de CUDA, vamos falar de GPU de maneira geral primeiro
introdução
começou com o desenvolvimento de APIs que permitissem a programação das placas gráficas em alto nível
a dificuldade era desenvolver os programas em DirectX (já utilizado em GPU) para fazer o processamento de calculos númericos pesados
bibliotecas
desenvolvidas para trabalhar com GPU
- CUDA
- OpenCL
- OpenACC
arquitetura de GPUs
arquitetura massivamente paralela
os núcleos de processamento são direcionados a operações aritméticas
um alto grau de paralelismo aumenta desempenho
Graphical Processing Units
iniciaram a lançar familias de GPUs com enfoque para processamento aritmético SEM saída para tela (processamento gráfico nõa era o principal objetivo)
séries da NVIDIA:
- Tesla (2008)
- Fermi (2011)
- Kepler (2012)
- Ampere (2020)
- Hopper (2022)
programação
as diferenças de arquitetura tornam a programação sensível ao contexto, um programa otimizado para uma GPU pode não funcionar bem em outra
CPU acessa a memória principal, por meio de transferencia para a cache
a GPU também possui cache, mas ela não faz acesso a memória principal, pois o acesso é muito mais lento do que a CPU, então deve-se evitar ao máximo a transferência de dados entre memória principal e GPU. para isso, deve-se transferir o máximo de volume de dados por vez.
CUDA is life
criada para programação em GPU da Nvidia
já é ótimizada para tais placas de vídeo, tanto que um código mediano em CUDA tem desempenho semalhante a um código excelente em OpenCL (estando em uma paca da nvidia)
arquitetura
- expoe paralelismo da GPU para computação de propósito geral
CUDA C/C++
- baseado no padrão C/C++
- pequeno conjunto de extensões para habilitar programação heterogênea (código parcialmente executado na CPU e parcialmente na GPU)
- APIs direcionadas para gerenciar dispositivos, memória
hello world
hello world - versão C (mitinho)
int main (void) {
printf("Hello World!\n");
return 0;
}$ nvcc
hello_world.cu
$ a.out
hello world!hello world - versão CUDA IS LIFE
__global__void mykernel (void) {
}
int main (void) {
mykernel<<<1,1>>>();
printf("hello world!\n");
return 0;
}keywords
__global__: indica uma função que executara na GPUnvcc: separa o src entre host e dispositivo (host é GPU e dispositivo é GPU)mykernel<<<1,1>>>(): não consegui pegar no slide (é o slide da nvidia)
soma de matrizes
cuidados com a memória
ponteiros de dispositivos
- apontas para memória de GPU
- nao podem ser referenciados no código do host
- pode-se passar o conteúdo dele (por meio de chamada de funções)
ponteiros de host
- apontam para memória de CPU
- nao podem ser referenciados no código do device
- pode-se passar o conteúdo dele (por meio de chamada de funções)
// add() roda na GPU, então estes ponteiros precisam ser de la
__global__ void add (int *A, int *B, int *C) {
*C = *A + *B;
}
int main (void) {
int a, b, c;
int *d_a, *d_b, *d_c;
cudaMalloc((void **) &d_a, size);
cudaMalloc((void **) &d_b, size);
cudaMalloc((void **) &d_c, size);
a = 2;
b = 7;
cudaMemcpy(d_a, &a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, &b, size, cudaMemcpyHostToDevice);
add<<<1,1>>>(d_a, d_b, d_c);
cudaMemcpy(&c, d_c, size, cudaMemcpyDeviceToHost);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}mesmo assim, ta uma merda, não ta executando em paralelo
a porra da paralelização surge (e urge)
add<<<1,1>>> -> add<<<N,1>>>
cada chamada de add() é referenciada como um block, um conjunto de blocks é um set, cada chamada pode ser referenciada pelo índice do bloco usando blockIdx.x
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x]
}
int main (void) {
int a, b, c;
int *d_a, *d_b, *d_c;
int size = N * sizeof(int);
cudaMalloc((void **) &d_a, size);
cudaMalloc((void **) &d_b, size);
cudaMalloc((void **) &d_c, size);
a = (int *) malloc(size); random_ints(a, N);
b = (int *) malloc(size); random_ints(b, N);
c = (int *) malloc(size);
cudaMemcpy(d_a, &a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, &b, size, cudaMemcpyHostToDevice);
add<<<N,1>>>(d_a, d_b, d_c);
cudaMemcpy(&c, d_c, size, cudaMemcpyDeviceToHost);
free(a); free(b); free(c);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}