Your SlideShare is downloading. ×
GPGPUFontes:I.      David Kirk/NVIDIA and Wen-mei W. Hwu, ECE 408, University of Illinois,        Urbana-Champaign, 2007-2...
Motivação    • Mercado de entretenimento doméstico cresce      rapidamente. Grande volume de pastilhas específicas      pa...
GPU é um acelerador da CPU        GPU                         CPU                  GPU                   +    CPU e GPU   ...
Como acelerar? Ideia Básica 1    • Como acelerar a execução do laço               DO i = 1, n                 C(i) = A(i) ...
Ideia Básica 2    • Suponha 32 processadores:               P0     P1     P2     P3     P4     P5     .   .   P31    • Mul...
Ideia Básica 3    • Observação: os 32 processadores executam a mesma      instrução a cada instante    • Então, basta uma ...
Ideia Básica 4    • Denomine “Streaming Multiprocessor” esse conjunto       • Nomenclatura NVIDIA    • Replique o conjunto...
Arquitetura da GPU NVIDIA    SM0    SM1    SM31                      DRAM8                               III Semana Invern...
Programação    • Qualquer laço pode ser executado na GPU?       • Não    • As iterações do laço tem que ser independentes!...
Programação     • Solução: quebra o laço em laços com iterações       independentes                 // executa na gpu:    ...
Programação     • Cada iteração do laço é denominada uma thread     • O laço tem que ser “desmontado” em um grupo de      ...
Arquitetura NVIDIA     • A GPU é composta por um conjunto de “Streaming       Multiprocessors” (SM)        – Cada SM possu...
Multithreading SIMD     • A cada ciclo o SM escolhe, no conjunto de warps       prontas para execução, uma warp a executar...
Escalonamento de Blocos de Threads     O número de SMs por GPU varia com o       modelo de GPU           – Alocação dinâmi...
Programação em CUDA:           Visão Geral15                   III Semana Inverno Geofísica
CUDA     • Compute Unified Device Architecture – É um modelo de       programação e uma linguagem para programar CPU +    ...
Fonte e Execução         Código Fonte na CPU            Execução CPU/GPU                 C serial                         ...
Extensões a C/C++ em CUDA - Kernels     • Kernels (funções) que serão executadas na GPU        Identificadas pelo prefixo ...
Exemplo: Disparo de soma de vetores de                    tamanho N     __global__ void VAdd(float* A, float* B, float* C)...
Dados que trafegam entre CPU e GPU     • CPU e GPU atuam em espaços de endereçamento distintos        – A CPU não endereça...
Alocação de memória na GPU     • Suponha as declarações na CPU:        float * A_h; // vetor A residente no host (CPU)    ...
Exemplo: Aloca vetor de tamanho N na GPU     int main()     {       size_t size;       float* A_h, B_h, C_h; // no host (C...
Trafego de dados entre CPU e GPU     • CPU comanda o tráfego entre as memórias da CPU e       da GPU       – Invocando “ru...
Exemplo: Tráfego de dados     int main()     {       size_t size;       float* A_h, B_h, C_h; // no host (CPU)       float...
Sumário     •   kernels executados na GPU identificados por __global__          – Cada kernel é uma thread     •   Endereç...
Programação em CUDA:      Enumerando Threads            Caso 1D26                   III Semana Inverno Geofísica
Múltiplos Blocos de Threads     • VAdd com vetores de tamanho N, particionado em nBlk       blocos de tamanho tamBlk      ...
Quantidade de Threads     • O Kernel tem que conhecer o número de threads por       bloco e o número de blocos utilizado n...
Enumeração das Threads     • Conhecemos a quantidade de threads (gridDim.x * blockDim.x).       Como identificar cada thre...
Enumerando Threads     • Utilidade: encontrar os índices dos vetores         0                                       i    ...
Programação em CUDA:          Sincronismo31                   III Semana Inverno Geofísica
Sincronismo entre Threads de um                   Bloco     • As threads de um mesmo bloco sincronizam invocando a função ...
Execução de funções no device     • Na implementação atual, a execução de       funções no device é serializada     • Ou s...
Overlap de funções do device no host      • As funções cudaMemcpy, cudaMalloc e cudaFree são sincronas         – Só retorn...
Upcoming SlideShare
Loading in...5
×

Introdução ao Processamento Paralelo (4.1)

621

Published on

Minicurso ofericido durante a III Semana de Inverno de Geofisica, IMECC/UNICAMP, 2012, por Jairo Panetta (ITA/ICE).

Published in: Education
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
621
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Transcript of "Introdução ao Processamento Paralelo (4.1)"

  1. 1. GPGPUFontes:I. David Kirk/NVIDIA and Wen-mei W. Hwu, ECE 408, University of Illinois, Urbana-Champaign, 2007-2010;II. David B. Kirk, Wen-Mei W. Hwu, “Programando para Processadores Paralelos”, Elsevier-Campus, 2012III. CUDA Programming Guide V.2.3.1, NVIDIA, 20091 III Semana Inverno Geofísica
  2. 2. Motivação • Mercado de entretenimento doméstico cresce rapidamente. Grande volume de pastilhas específicas para atender o mercado. • Esse mercado demanda alta qualidade de imagem a baixo custo – Requer computação ponto flutuante de alto desempenho • GPUs (Graphics Processing Units) tornam-se 10 vezes mais rápidas (Flop/s) que CPUs nas aplicações gráficas – Talvez possam ser usadas para computação científica • GPGPUs (General Purpose Graphics Processing Units) buscam preencher esse espaço2 III Semana Inverno Geofísica
  3. 3. GPU é um acelerador da CPU GPU CPU GPU + CPU e GPU MEM atuam sobre memórias distintas!3 III Semana Inverno Geofísica
  4. 4. Como acelerar? Ideia Básica 1 • Como acelerar a execução do laço DO i = 1, n C(i) = A(i) + B(i) END DO • Idéia: um processador para cada iteração • Todas as iterações executadas simultaneamente 1 2 3 4 5 6 . . . n • Impossível, pois a quantidade de processadores varia com n • Solução: Divide o laço em blocos de tamanho fixo, mapeados a hardware de tamanho fixo4 III Semana Inverno Geofísica
  5. 5. Ideia Básica 2 • Suponha 32 processadores: P0 P1 P2 P3 P4 P5 . . P31 • Multiplexe os 32 processadores no tempo: P0 P1 P2 P3 P4 P5 . . P31 T=1 i=1 i=2 i=3 i=4 i=5 i=6 . . i=32 T=2 i=33 i=34 i=35 i=36 i=37 i=38 . . i=64 T=3 i=65 i=66 i=67 i=68 i=69 i=70 . . i=96 ... ... i=n5 III Semana Inverno Geofísica
  6. 6. Ideia Básica 3 • Observação: os 32 processadores executam a mesma instrução a cada instante • Então, basta uma unidade de controle e 32 unidades de execução • Substitui P0 P1 P2 P3 P4 P5 . . P31 • Por controle cache unidades de execução6 III Semana Inverno Geofísica
  7. 7. Ideia Básica 4 • Denomine “Streaming Multiprocessor” esse conjunto • Nomenclatura NVIDIA • Replique o conjunto • Para acelerar o laço, use um único SM em instantes sucessivos ou múltiplos SM simultaneamente7 III Semana Inverno Geofísica
  8. 8. Arquitetura da GPU NVIDIA SM0 SM1 SM31 DRAM8 III Semana Inverno Geofísica
  9. 9. Programação • Qualquer laço pode ser executado na GPU? • Não • As iterações do laço tem que ser independentes! • Ex: Laço com iterações dependentes: for (i=1; i<10; i++) { b[i] = func(i); a[i] = b[i-1] + b[i]; }9 III Semana Inverno Geofísica
  10. 10. Programação • Solução: quebra o laço em laços com iterações independentes // executa na gpu: for (i=1; i<10; i++) b[i] = func(i); // espera resultado do anterior e executa na gpu for (i=1; i<10; i++) a[i] = b[i-1] + b[i];10 III Semana Inverno Geofísica
  11. 11. Programação • Cada iteração do laço é denominada uma thread • O laço tem que ser “desmontado” em um grupo de threads: for (i=0; i<n; i++) c[i] = a[i] + b[i] • Na função __global__ void VAdd(float* A, float* B, float* C) { int i = threadIdx.x; c[i] = a[i] + b[i]; } • E na invocação VAdd <<< dimGrid, dimBlk >>> (A_d, B_d, C_d);11 De alguma forma, passa n III Semana Inverno Geofísica
  12. 12. Arquitetura NVIDIA • A GPU é composta por um conjunto de “Streaming Multiprocessors” (SM) – Cada SM possui uma unidade de controle e um programa em execução – O conjunto das SMs na GPU forma uma máquina MIMD de memória central – todas as SM vêem a mesma memória • Cada SM recebe um bloco de até 512 threads para execução – Cada thread possui seu conjunto de registradores • A SM divide o bloco de threads em sub-blocos de 32 threads denominados “warp”12 III Semana Inverno Geofísica
  13. 13. Multithreading SIMD • A cada ciclo o SM escolhe, no conjunto de warps prontas para execução, uma warp a executar – Chavear entre warps a cada ciclo esconde a latência das demais unidades da GPU (ALUs, Memória, etc) – Enquanto uma warp é atendida, outras estão em execução ou prontas para serem escolhidas • A próxima instrução da warp escolhida é executada – Executa simultaneamente apenas nas threads da warp cuja próxima instrução a executar for a instrução enviada para execução13 III Semana Inverno Geofísica
  14. 14. Escalonamento de Blocos de Threads O número de SMs por GPU varia com o modelo de GPU – Alocação dinâmica de blocos de threads a SMs Um modelo Blocos de Threads Outro modelo SM SM Bloco 0 Bloco 1 SM SM SM SM Bloco 2 Bloco 3 Bloco 0 Bloco 1 Bloco 4 Bloco 5 Bloco 0 Bloco 1 Bloco 2 Bloco 3 Bloco 6 Bloco 7 Bloco 2 Bloco 3 time Bloco 4 Bloco 5 Bloco 6 Bloco 7 t Bloco 4 Bloco 5 Cada bloco pode executar em qualquer ordemt Bloco 6 Bloco 7 com relação aos outros blocos14 III Semana Inverno Geofísica
  15. 15. Programação em CUDA: Visão Geral15 III Semana Inverno Geofísica
  16. 16. CUDA • Compute Unified Device Architecture – É um modelo de programação e uma linguagem para programar CPU + aceleradores – CUDA é propriedade da NVIDIA. Perde-se portabilidade – CUDA é uma extensão de C/C++ – Há CUDA Fortran (implementado pelo compilador PGI) • A GPU é um dispositivo escravo da CPU – A CPU envia dados e computação para a GPU e recebe resultados – A CPU comanda a computação • CPU e GPU cooperam na execução da computação – Controlados pela CPU – Códigos a executar na GPU são funções denominadas kernels – CPU envia dados, dispara kernels e recebe resultados – O restante do código é executado na CPU16 III Semana Inverno Geofísica
  17. 17. Fonte e Execução Código Fonte na CPU Execução CPU/GPU C serial CPU KernelA<<< nBlk, nTid >>>(args); ... GPU C serial CPU KernelB<<< nBlk, nTid >>>(args); ... GPU17 III Semana Inverno Geofísica
  18. 18. Extensões a C/C++ em CUDA - Kernels • Kernels (funções) que serão executadas na GPU Identificadas pelo prefixo __global__: __global__ void nome-do-kernel (args) • O código do Kernel representa uma thread • Disparo de Kernels na GPU define o número de threads e blocos nome-do-kernel <<< NBlk, NThread >>> (args); Blocos Threads por bloco independentes de (todas as threads em threads executados um único SM) em qualquer ordem executadas (um bloco por SM) simultaneamente18 III Semana Inverno Geofísica
  19. 19. Exemplo: Disparo de soma de vetores de tamanho N __global__ void VAdd(float* A, float* B, float* C) { int i = threadIdx.x; Threads numeradas C[i] = A[i] + B[i]; } de 0 a N-1 int main() { ..... dim3 dimBlk (N); // N threads por bloco dim3 dimGrid (1); // um unico bloco ... VAdd <<< dimGrid, dimBlk >>> (A_d, B_d, C_d); .... } Um bloco de N threads19 III Semana Inverno Geofísica
  20. 20. Dados que trafegam entre CPU e GPU • CPU e GPU atuam em espaços de endereçamento distintos – A CPU não endereça diretamente a memória da GPU e vice versa – Mas a CPU dispara kernels sobre dados que residem na GPU!! – Valores podem ser copiados da CPU para a GPU, mas não ponteiros! • Dados que trafegam entre a CPU e a GPU devem ser declarados e alocados pela CPU – CPU contém as duas declarações – O endereço do dado na GPU deve ser conhecido pela CPU, para ser usado no “kernel” • Declarações na CPU: float * A_h; // vetor A residente no host (CPU) float * A_d; // vetor A residente no device (GPU)20 III Semana Inverno Geofísica
  21. 21. Alocação de memória na GPU • Suponha as declarações na CPU: float * A_h; // vetor A residente no host (CPU) float * A_d; // vetor A residente no device (GPU) • O vetor A_h no host é alocado e inicializado na forma usual • O vetor A_d é alocado na GPU pela função cudaMalloc, invocada pela CPU – dealocado pela função cudaFree, invocada pela CPU • Alocação e dealocação na GPU por run-time invocado pela CPU: cudaMalloc ((void **) &A_d, size); cudaFree(A_d); Reserva a memória size em bytes e retorna ponteiro21 para A_d na GPU III Semana Inverno Geofísica
  22. 22. Exemplo: Aloca vetor de tamanho N na GPU int main() { size_t size; float* A_h, B_h, C_h; // no host (CPU) float* A_d, B_d, C_d; // no device (GPU) ..... // aloca A, B, C, no device size = N*sizeof(float); cudaMalloc( (void **) &A_d, size); cudaMalloc( (void **) &B_d, size); cudaMalloc( (void **) &C_d, size); ... // invoca kernel, passando A, B, C no device e N VAdd <<< dimGrid, dimBlk >>> (A_d, B_d, C_d, N); .... }22 III Semana Inverno Geofísica
  23. 23. Trafego de dados entre CPU e GPU • CPU comanda o tráfego entre as memórias da CPU e da GPU – Invocando “run-time” • Comunicação síncrona – A invocação na CPU retorna ao término da comunicação – Há versões assíncronas • Envio de dados da CPU para a GPU: – cudaMemcpy (A_d, A_h, size, cudaMemcpyHostToDevice); • Recepção de dados da GPU na CPU: – cudaMemcpy (C_h, C_d, size, cudaMemcpyDeviceToHost); para de tamanho constante simbólica como em uma atribuição (bytes)23 III Semana Inverno Geofísica
  24. 24. Exemplo: Tráfego de dados int main() { size_t size; float* A_h, B_h, C_h; // no host (CPU) float* A_d, B_d, C_d; // no device (GPU) ..... // envia A e B para o device size = N*sizeof(float); cudaMemcpy(A_d, A_h, size, cudaMemcpyHostToDevice); cudaMemcpy(B_d, B_h, size, cudaMemcpyHostToDevice); ... // invoca kernel, passando A, B, C no device e N VAdd <<< dimGrid, dimBlk >>> (A_d, B_d, C_d, N); ... // recebe C do device cudaMemcpy(C_h, C_d, size, cudaMemcpyDeviceToHost); .... }24 III Semana Inverno Geofísica
  25. 25. Sumário • kernels executados na GPU identificados por __global__ – Cada kernel é uma thread • Endereços de ponteiros na CPU e na GPU por variáveis distintas – Declarações distintas • CPU comanda alocação de memória na GPU – Invocando cudaMalloc • CPU copia dados para a memória da GPU – Invocando cudaMemcpy • CPU dispara kernels – Definindo threads por bloco e número de blocos • CPU recebe resultados – Invocando cudaMemcpy25 III Semana Inverno Geofísica
  26. 26. Programação em CUDA: Enumerando Threads Caso 1D26 III Semana Inverno Geofísica
  27. 27. Múltiplos Blocos de Threads • VAdd com vetores de tamanho N, particionado em nBlk blocos de tamanho tamBlk – Por exemplo, quando N > 512 0 i N-1 tamBlk tamBlk tamBlk tamBlk • Disparo do kernel: threads por dim3 dB (tamBlk); bloco dB e dG são variáveis locais dim3 dG (nBlk); à função, invisíveis ao VAdd <<< dG, dB >>> (A_d, B_d, C_d, N); Kernel nBlk blocos27 de threads III Semana Inverno Geofísica
  28. 28. Quantidade de Threads • O Kernel tem que conhecer o número de threads por bloco e o número de blocos utilizado na invocação • Há variáveis “build-in”, acessíveis no Kernel • O número de blocos é gridDim.x – grid de blocos • O número de threads por bloco é blockDim.x – bloco de treads • No exemplo anterior: – gridDim.x = nBlk – blockDim.x = tamBlk28 III Semana Inverno Geofísica
  29. 29. Enumeração das Threads • Conhecemos a quantidade de threads (gridDim.x * blockDim.x). Como identificar cada thread? • Há variáveis “build-in”, acessíveis no Kernel, para o número desta thread no bloco e o número deste bloco • As threads em um bloco são enumeradas de 0 a blockDim.x-1 e identificadas por threadIdx.x • Os blocos de threads são enumerados de 0 a girdDim.x-1 e identificados por blockIdx.x • Logo, o número desta thread (de 0 à N-1) é blockIdx.x * blockDim.x + threadIdx.x threads neste bloco quantos blocos de threads antes deste threads por bloco29 III Semana Inverno Geofísica
  30. 30. Enumerando Threads • Utilidade: encontrar os índices dos vetores 0 i N-1 blockDim.x blockDim.x blockDim.x blockIdx.x = 0 blockIdx.x = 1 blockIdx.x = gridDim.x - 1 • no Kernel: i = blockIdx.x * blockDim.x + threadIdx.x; C[i] = A[i] + B[i];30 III Semana Inverno Geofísica
  31. 31. Programação em CUDA: Sincronismo31 III Semana Inverno Geofísica
  32. 32. Sincronismo entre Threads de um Bloco • As threads de um mesmo bloco sincronizam invocando a função __syncthreads() • A função só pode ser invocada dentro de uma função do device • É responsabilidade do programador evitar condição de corrida entre as threads pelo uso de __syncthreads() • Não há mecanismo de sincronismo explícito entre blocos de threads – Blocos de threads são supostamente independentes32 III Semana Inverno Geofísica
  33. 33. Execução de funções no device • Na implementação atual, a execução de funções no device é serializada • Ou seja, solicitações do host com cudaMalloc, cudaMemcpy e disparo de kernels são executados na sequencia recebida, sem overlap • Logo, invocações consecutivas de um mesmo kernel são serializadas, impondo sincronismo entre os blocos de threads de invocações consecutivas do kernel33 III Semana Inverno Geofísica
  34. 34. Overlap de funções do device no host • As funções cudaMemcpy, cudaMalloc e cudaFree são sincronas – Só retornam controle ao host após terminar no device • Mas invocações de kernel são assincronas – Retornam controle ao host assim que a solicitação de execução for colocada no device • Como a execução no device é sincrona, medidas de tempo no host devem considerar esse fato • A função cudaThreadSynchronize(), invocada no host, aguarda o término de todas as funções em execução no device antes de retornar controle ao host • Função necessária para atribuir corretamente tempos medidos no host a eventos no device (disparo de kernels)34 III Semana Inverno Geofísica

×